Creating Custom Button Behaviour. Is This Wrong?

i think there are better ways to do this. in C++ Programming Style cargill show examples of poorly designed classes and how to do them better.

i think your use of a class to exercise function pointers is unnecessary

consider how a single function could simply be passed a button id argument and how unique function can be invoked thru an array of function pointers.

byte pinButs [] = { A1, A2, A3 };
#define N_PINS   3

byte butState [N_PINS];

// -----------------------------------------------------------------------------
void butFunc1 (void)  { Serial.println (__func__); }
void butFunc2 (void)  { Serial.println (__func__); }
void butFunc3 (void)  { Serial.println (__func__); }

void (*butFunc [])(void) = {
    butFunc1,
    butFunc2,
    butFunc3,
};

// -----------------------------------------------------------------------------
void
genericButFunc (
    int  n )
{
    Serial.print   ("genericButFunc: ");
    Serial.println (n);
}

// -----------------------------------------------------------------------------
void loop ()
{
    for (unsigned n = 0; n < N_PINS; n++)  {
        byte but =  digitalRead (pinButs [n]);

        if (butState [n] != but)  {
            butState [n] = but;
            delay (10);
            if (LOW == but)  {
                genericButFunc (n);
                (*butFunc[n])();
            }
        }
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < N_PINS; n++)  {
        pinMode (pinButs [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinButs [n]);
    }
}

here's example code for a state machine that uses a table of function pointers

//
// example state machine
//

#include <stdio.h>

#include "stateMach.h"

// ------------------------------------------------
Status
a (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// ------------------------------------------------
Status
b (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// ------------------------------------------------
Status
c (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// ------------------------------------------------
Status
__ (void* a)
{
    printf ("%s:\n", __func__);
    return OK;
}

// --------------------------------------------------------------------

#define N_STATE     3
#define N_STIM      2

typedef enum { S0, S1, S2 } State_t;

typedef Status(*Action_t)(void*) ;

State_t smTransitionTbl [N_STATE] [N_STIM] = {
    {   S1, S2, },
    {   S0, S2, },
    {   S2, S1, },
};


Action_t smActionTbl [N_STATE] [N_STIM] = {
     {  a,  b },
     {  c, __ },
     { __,  a },
};

// ------------------------------------------------
char *msg1 [] = {
    "State machine has 3 states and 2 stimuli.",
    "It has the following state transistion and action routine tables.",
    "A __() represents do nothing and is easily identified in table.",
    "Each row is indexed by a state and each column a stimulus.",
    "Of course, meaningful action routine names are helpful.",
};

char *msg2 [] = {
    "Enter valid stimuli [0-1]:"
};

void
smHelp (void)
{
    for (int i = 0; i < sizeof(msg1)/sizeof(char*); i++)
        printf ("  %s\n", msg1 [i]);

    for (int state = 0; state < N_STATE; state++)  {
        printf ("%8s", "");
        for (int stim = 0; stim < N_STIM; stim++)
            printf (" %d", smTransitionTbl [state][stim]);

        printf ("%8s", "");
        for (int stim = 0; stim < N_STIM; stim++)  {
            if (a == smActionTbl [state][stim])
                printf ("  a()");
            else if (b == smActionTbl [state][stim])
                printf ("  b()");
            else if (c == smActionTbl [state][stim])
                printf ("  c()");
            else
                printf (" __()");
        }

        printf ("\n");
    }

    for (int i = 0; i < sizeof(msg2)/sizeof(char*); i++)
        printf ("  %s\n", msg2 [i]);

}

// ------------------------------------------------
static State_t _smState  = S0;

Status
smEngine (Stim_t stim)
{
    Action_t   func;
    State_t    last = _smState;

    if (N_STIM <= stim)
        return ERROR;

    func        = smActionTbl [_smState] [stim];
    _smState    = smTransitionTbl [_smState] [stim];

    printf ("    stim %d, transition from state %d to %d, exec ",
        stim, last, _smState);

    return (*func) (NULL);
}