Yet Another PushButton Class

A few days ago, someone started a new topic thread on debouncing several push-buttons. I was very impressed with PieterP's response and class handling of the push-buttons. I deal with a lot of push-button interfaces and have been wanting to do something like this for myself for a long time to reduce overall coding. I am not as fluent in C++ but wish to adopt the concepts presented in the code presented. To this end, I've copied the code, modified the code slightly and present it here. My actual question(s) are presented as comments in the code.

class PushButton {
  public:
    PushButton(byte pin) : pin(pin) {}// Can someone explain what the ":" here means? what is pin(pin)? How does it work with the array buttons[]?
    void begin() const {
      // for (auto &button : buttons) I would like to move this bit of code from the setup loop to here, if possible.
      //    What does auto mean?
      //    What is the syntax &button : buttons?
      //    I find it difficult to Google for help with this. Most guides I've found on classes do not cover these advanced topics.
      pinMode(pin, INPUT_PULLUP);
    }
    /*I have modified the actual debounce code. this is yet untested, but the goal is to return true on the initial HIGH to LOW transition but debounce
    both transitions so if the button is held down for longer than the debounce interval and released, it doesn't trigger a second press from the noise...
    I have also structured it with the flag to avoid constantly calling millis() as that stops interrupts each time it's called...*/
    bool isPressed() {
      bool b = false;
      if (debounceFlag == 1) {
        if (millis() >= debounceStopTime) debounceFlag = 0;
      }
      else if (debounceFlag == 0) {
        int currentState = digitalRead(pin);
        int stateChange = currentState - previousState;
        if (stateChange == fallingEdge) {
          debounceStopTime = millis() + DebounceInterval;
          b = true;
          debounceFlag = 1;
        }
        else if (stateChange == risingEdge) {
          debounceStopTime = millis() + DebounceInterval;
          b = false;
          debounceFlag = 1;
        }
        else b = false;
        previousState = currentState;
      }
      return b;
    }

    /* byte whichButton() I would like to create a member function that would check up to 8 buttons for isPressed() and map the returns to bits in a byte
    and return the value, so code later could take action based on a combination of buttons pressed. I think if I had a better understanding of some of the
    advanced concepts introduced here, I could figure out how to code this.
      Do I refer to them as pin? Do I refer to them as buttons[i]?
      I vaguely understand buttons is an external name and maybe pin is an internal name, but I am looking for a better explanation on the names presented.*/

    const byte pin;// which pin is which? There is this pin, and there is a pass by value pin in the constructor. What does this pin do? If I change the name of this, where also must the name change?

  private:
    int previousState = HIGH;
    byte debounceFlag = 0;
    unsigned long debounceStopTime;
    const static unsigned long DebounceInterval = 20UL;
    const static int risingEdge = HIGH - LOW;
    const static int fallingEdge = LOW - HIGH;
};

PushButton buttons[] = { 2, 3, 4 };        // Create PushButton objects on pins 2, 3 and 4

void setup() {
  Serial.begin(9600);
  for (auto &button : buttons) button.begin();
}

void loop() {
  for (auto &button : buttons)
    if (button.isPressed()) {// since the only reference to a button in the array is all buttons in the array, what would a call for isPressed() on a single button look like?
      Serial.print("Button on pin #");
      Serial.print(button.pin);
      Serial.println(" pressed!");
    }
}

PushButton(byte pin) : pin(pin) {}// Can someone explain what the ":" here means? what is pin(pin)? How does it work with the array buttons[]?

It's an Initializer List

// for (auto &button : buttons) I would like to move this bit of code from the setup loop to here, if possible.

The original code used this loop to call the pinMode() function for all of the button objects in the array. It did this in setup() because you don't want to call pinMode() from the constructor as the hardware may not yet be set up to handle that function.

gfvalvo:
It's an Initializer List
The original code used this loop to call the pinMode() function for all of the button objects in the array. It did this in setup() because you don't want to call pinMode() from the constructor as the hardware may not yet be set up to handle that function.

Thank you for the help with the initializer list.
I am not calling pinMode() from the constructor, but from begin(), or wish to create a member function that can be called from setup to perform pinMode() (or some other function) on all members of the class.

for (auto &button : buttons)
if (button.isPressed()) {// since the only reference to a button in the array is all buttons in the array, what would a call for isPressed() on a single button look like?
Serial.print("Button on pin #");
Serial.print(button.pin);
Serial.println(" pressed!");
}

That loops through and calls isPressed() on the individual buttons (one at a time). In the loop 'button' is a reference to each array element -- one at time.

When not in that loop, just call:

buttons[i].isPressed

where i is the array element you want.

Perehama:
I am not calling pinMode() from the constructor, but from begin()

The original code does that with a loop in setup(). What's wrong with that?

, or wish to create a member function that can be called from setup to perform pinMode() (or some other function) on all members of the class.

You could do it with a static member function, but you'd have to keep track of all the instances in order to do so. A linked list would work as discussed here: methods for an array of a class - Programming Questions - Arduino Forum
Might be more trouble than it's worth.

gfvalvo:
The original code does that with a loop in setup(). What's wrong with that?

There is absolutely nothing wrong with it, It was an exercise in refactoring to better understand the entire concept. And, although not necessary for begin(), it would be necessary for the function to return a number based on the states of 8 push-buttons.

gfvalvo:
Might be more trouble than it's worth.

I think you are right. I think the better way to do the 8 push-buttons is to have a function external to the class iterate over isPressed() for the number of buttons that would be known to that function.

gfvalvo:
When not in that loop, just call:

buttons[i].isPressed

where i is the array element you want.

Thank you, again. My plan is to enumerate the index so the buttons can have names such as:

if (buttons[volumeUp].isPressed) volumeUp();