2009-07-30 4 views
19

Voglio chiamare l'implementazione della classe base di una funzione virtuale utilizzando un puntatore funzione membro.Chiamare la definizione della classe base della funzione membro virtuale con il puntatore di funzione

class Base { 
public: 
    virtual void func() { cout << "base" << endl; } 
}; 

class Derived: public Base { 
public: 
    void func() { cout << "derived" << endl; } 

    void callFunc() 
    { 
     void (Base::*fp)() = &Base::func; 
     (this->*fp)(); // Derived::func will be called. 
         // In my application I store the pointer for later use, 
         // so I can't simply do Base::func(). 
    } 
}; 

Nel codice sopra l'implementazione della classe derivata di func sarà chiamata da callFunc. C'è un modo per salvare un puntatore a funzione membro che punta a Base :: func, o dovrò usare using in qualche modo?

Nella mia applicazione effettiva uso boost :: bind per creare un oggetto boost :: function in callFunc che uso in seguito per chiamare func da un'altra parte del mio programma. Quindi se boost :: bind o boost :: function ha un modo per aggirare questo problema che potrebbe anche aiutare.

+0

Possibile duplicato di [C++: puntatore a versione monomorfa della funzione membro virtuale?] (Https://stackoverflow.com/questions/5064614/c-pointer-to-monomorphic-version-of-virtual-member-function) –

risposta

11

Quando si chiama un metodo virtuale tramite un riferimento o un puntatore, si attiverà sempre il meccanismo di chiamata virtuale che trova il tipo più derivato.

La soluzione migliore è aggiungere una funzione alternativa che non sia virtuale.

0

C'è qualche motivo specifico per farlo tramite un puntatore di funzione?

Si dovrebbe essere in grado di scrivere solo:

Base::func(); 

per chiamare l'implementazione della classe base.

+1

Come ho scritto nella mia domanda, salvo il puntatore in callFunc ma lo uso per chiamare effettivamente func da un'altra posizione nel mio programma. –

+0

Per una funzione virtuale questo non funzionerà. Farà sempre una ricerca vtable e chiamerà il metodo sulla classe derivata. – Joel

1

Il tuo problema è che un puntatore a funzione membro non è esattamente come un puntatore a funzione nuda. In realtà non è solo un puntatore, ma uno considerably more complex structure, che varia nei suoi dettagli a livello dell'implementazione del compilatore. Quando lo invochi tramite la sintassi (this->*fp)(), lo stai effettivamente chiamando sull'oggetto originale, che causa l'invio della funzione virtuale.

Una cosa che potrebbe funzionare è lanciarlo su un tipo di puntatore non-metodo. Questo è un po 'scricchiolante ma I penso che dovrebbe funzionare. Hai ancora bisogno di passare un Base * ma lo si fa in modo esplicito e la funzione di invio virtuale è by-passato:

typedef void BasePointer(Base*); 

void callFunc() 
{ 
    BasePointer fp = (BasePointer *)&Base::func; 
    fp(this); 
} 

Aggiornamento: Ok, no, non si può fare in questo modo. È illegale e non sarebbe sicuro se fosse legale. Lo C++ FAQ ha more on this. Ma sapendo che non risolve il tuo problema. Il problema è che, da puntatore a oggetto o da puntatore a membro se si desidera chiamare Base::func tramite un puntatore Base, l'oggetto che sta indicando deve anche essere un Base. Se è possibile organizzarlo, è possibile utilizzare un puntatore a funzione membro.

Ecco un altro pensiero, non bello, ma almeno fattibile. Fornire una funzione in Derived, non virtuale, che chiama esplicitamente Base::func. Indicalo invece. Non verrà ridimensionato se è necessario farlo nel caso generale di molte varianti diverse di func e callFunc ma funzionerà correttamente per un metodo.

+1

Questo sicuramente non funzionerà! Confronta 'sizeof (& Base :: func)' con 'sizeof (BasePointer)': o –

+0

mmutz: Yup. Cerco di testare sempre prima di pubblicare, e oggi non mi sono lasciato il tempo per farlo. Era * possibile * un 'static_cast' potrebbe essere abbastanza intelligente da far uscire la parte del puntatore di funzione dal puntatore della funzione membro, ma temo che la mia nozione Python di una funzione stia sovrascrivendo il mio C++ :). – quark

+0

Questo è falso: "porta con sé non solo l'indirizzo della funzione da chiamare, ma anche l'oggetto per chiamarlo su" –

0

Oltre a ciò che dice quark, un'osservazione più generale è che si dovrebbe usare un'implementazione signal/slot piuttosto che un puntatore a funzione nuda. Boost ne ha uno, c'è libsigc e un mucchio di altri.

2

Purtroppo quello che stai cercando di fare non è possibile. Le funzioni da puntatore a membro sono progettate per per mantenere la virtualità della funzione indicata.

0

Cosa c'è di sbagliato in questo?

(Base(*this).*fp)(); 

Ora, se siete soddisfatti con quello, esso solleva la questione del perché si sta anche utilizzando un puntatore a funzione, in primo luogo. Penso che un altro contesto potrebbe aiutare.

+0

Questo è stato già suggerito dal quark ("... se vuoi chiamare Base :: func attraverso un puntatore Base, l'oggetto che sta puntando deve essere anche una Base."). Suppongo che Eddie non possa garantire di conoscere il tipo a cui tagliare quando chiama la funzione. – Troubadour