Come chiamare una funzione membro sconosciuta attraverso un'istanza di una classe correlata?

0

Ho due classi. Il primo, chiamato Game_Events , controlla gli oggetti e gestisce le attività generali. Il secondo, chiamato Button , è per una di queste istanze di oggetto.

L'esempio sotto è sbagliato ma mostra come voglio farlo:

class Button
{
   public:
       Button(void (*f)()) : f_m(f)
       ~Button(){}

   private:
       void (*f_m);
};

class Game_Events()
{
    public:
        Game_Events()
        {
            button = new Button(&Close);
        }
        ~Game_Events(){}
        Close(){} //I need to pass this function for call it on the other side
}

Quindi voglio creare un'istanza Button all'interno della classe Game_Events e dargli l'autorizzazione a chiamare la funzione Close() della stessa classe dell'istanza proprietaria del pulsante. Nella tessera dico "funzione sconosciuta", perché la funzione da chiamare deve essere determinata quando viene creato il pulsante.

È possibile?

    
posta Andre Marques 09.05.2016 - 00:29
fonte

1 risposta

6

Sì, questo è definitivamente possibile.

Ma ci sono due sfide da affrontare nel tuo progetto:

  • Close() è una funzione membro. Quindi se il pulsante deve chiamare questa funzione membro, deve sapere su quale oggetto chiamarlo.

  • Se il pulsante successivo deve intervenire sulla chiamata, devi renderlo visibile in qualche modo nella tua architettura (o è un elemento della GUI che esegue l'azione quando è troncato, o dovrebbe essere accessibile pragrammaticamente all'interno del tuo codice.

Risponderò con C ++. Per il secondo argomento, nell'interesse della demo, presumo che il pulsante sia un membro pubblico di Game_Events.

Alternativa 1: approccio di base che elabora il puntatore della funzione

Se non hai bisogno di chiamare una funzione membro, cioè se Close() potrebbe essere static , il tuo codice potrebbe funzionare quasi come è, con alcune correzioni sintattiche minori:

class Button
{
   private:
       void (*f_m)();
   public:
       Button(void (*f)()) : f_m(f) {}
       ~Button(){}
       void operator() () { f_m(); }   // make button a callable (i.e. button() ) 
};

Demo online

Tuttavia, l'uso è limitato, perché CLose dovrebbe essere statico, quindi non potrebbe utilizzare alcun valore di vval dell'oggetto dipendente dall'evento. Che pessima

Alternativa 2: avvicinare l'astrazione della funzione da chiamare

Quindi rendiamolo un po 'più generale. Invece di usare un puntatore a funzione o un puntatore a funzione membro (che ci obbligherebbe a determinare la classe della funzione), potremmo usare la libreria funzionale di C ++, che permette di definire una funzione astratta ab:

#include <functional>
...
class Button
{
   private:
       function<void()> f_m;
   public:
       Button(function<void()> f) : f_m(f) {}
       ~Button(){}
       void operator() () { f_m(); }
};

Quindi potresti creare un pulsante, con una flessibilità totale per la funzione che fornirai. Qui userò una funzione lambda:

class Game_Events
{
    public:
        Button* button; 
        Game_Events()
        {
            button = new Button([this]()->void{this->Close(); });
        }
        ~Game_Events(){}
        void Close(){ cout<<"Boum"<<endl;} //I need to pass this function for call it on the other side
};

Il requisito è che l'oggetto esista ancora quando il pulsante viene attivato.

demo online

Alternativa 3: schema di progettazione del comando

Questo è il mio approccio preferito: implementa il schema di comando .

Il principio generale è leggermente cambiato rispetto al tuo approccio originale, ma è più facile da gestire e molto potente:

  • Avresti una classe Command astratta con una sola funzione membro virtuale: Execute() .
  • Il costruttore di pulsanti verrà creato con un puntatore a un oggetto Command astratto (anziché un puntatore a funzione).
  • Quando il tuo pulsante è attivo, chiama solo Execute() per l'oggetto Command .
  • Avresti una classe CloseGameEvent che eredita da Command . Il suo costruttore tiene traccia del Game_Event e il suo overriden Execute() chiamerebbe la funzione Close() per esso.
  • Quando costruisci il tuo oggetto pulsante, devi quindi passarlo al CloseGameEvent pertinente.
risposta data 09.05.2016 - 01:51
fonte

Leggi altre domande sui tag