2009-06-09 18 views
5

Ho letto qualche volta (probabilmente su c.l.C++. Moderato) che le chiamate alle funzioni virtuali possono essere template. Ho provato qualcosa nelle seguenti righe.Chiamate virtuali utilizzando l'indirizzo di un membro virtuale puro. È legale?

#include <iostream> 

template<class T, class FUN> 
void callVirtual(T& t, FUN f){ 
    (*t.*f)(); 
} 


struct Base{ 
    virtual ~Base(){} 
    virtual void sayHi()=0; 
}; 


struct Derived : public Base{ 
    void sayHi(){ 
     std::cout << "Hi!" << std::endl; 
    } 
}; 


void Test(){ 
    Base* ptr = new Derived; 
    callVirtual(ptr,&Base::sayHi); 
} 

int main() 
{ 
    Test(); 
    return 0; 
} 

Output: 
Hi! 

Procedimento templatized però dare l'indirizzo del metodo di base virtuale pura al momento della compilazione chiama il metodo corretto in fase di esecuzione. È legale in C++ standard prendere l'indirizzo di un membro virtuale puro?

Grazie in anticipo

EDIT-1: ho rimosso la seconda parte della domanda 'come funziona?'. Sembra che sia ciò che cattura l'attenzione.

EDIT-2: Ho cercato c.l.C++ moderati e sono imbattuto in questo link (http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/5ddde8cf1ae59a0d).. Il consenso sembra che dal momento che lo standard non lo limita, è vaild.

EDIT-3: Dopo aver letto l'articolo codeproject (grazie agli ovanes), sto pensando che i compilatori fanno un po 'di magia. Poiché le funzioni virtuali sono implementate tramite vtable (che è specifico del compilatore), l'assunzione dell'indirizzo di una funzione virtuale fornisce sempre l'offset nel vtable. A seconda del puntatore "this" utilizzato, viene richiamata la funzione appropriata (il cui indirizzo è all'offset). Non sono sicuro di come giustificarlo, poiché lo standard non dice nulla di simile!

risposta

0

Direi che non è definito. Non ho trovato nulla nelle specifiche.

i metodi virtuali vengono implementati utilizzando il concetto denominato vtable.

Direi che è specifica dell'implementazione del compilatore. Non penso davvero che sia importante che sia un puro virtuale, lo stesso accadrebbe se fosse anche solo virtuale.

Ho appena compilato il codice con Visual Studio 2008 e ho simulato l'exe. Quello che VS2008 fa è creare una funzione thunk per passare alla voce vtable usando il puntatore passato "this".

Questo è il setup e chiama la funzione callVirtual template.

push offset [email protected]@$B3AE ; void (__thiscall *)(Base *) 
lea  eax, [ebp+ptr] 
push eax    ; Base ** 
call [email protected]@@[email protected]@@[email protected]@[email protected]@Z ; callVirtual<Base *,void (Base::*)(void)>(Base * &,void (Base::*)(void)) 

Così passando il puntatore a funzione alla funzione thunk: [email protected]@$B3AE

; void __thiscall Base___vcall_(Base *) 
[email protected]@$B3AE proc near 
jmp  [email protected]@$B3AE ; [thunk]: Base::`vcall'{4,{flat}} 
[email protected]@$B3AE endp 

Tutte le funzioni thunk sta facendo sta usando il vtable per passare al metodo di vera classe.

+0

Grazie. Conoscete una sezione dello standard C++ che menziona i puntatori a funzioni dei membri ai puri virtual. Ho provato a trovare, ma non sono riuscito a carpire dettagli specifici. Mi sembra che dal momento che sono consentiti i puntatori dei membri a un tipo incompleto, sono consentiti anche i puntatori dei membri ai puri-virtuali. – Abhay

+0

Ho letto male la domanda. Aggiornerò la mia risposta –

+0

+1 La tua risposta modificata si avvicina a una "risposta" a questa domanda :-). Comincio a pensare che prendere in considerazione le funzioni virtuali (pure o no) e invocarlo è più verso un aspetto definito dalla lingua. – Abhay

1

Sicuro che lo sia. Il tuo codice non è diverso che semplicemente chiamando il metodo virtuale pura come questo:

void Test() 
{ 
    Base* ptr = new Derived; 
    ptr->sayHi(); 
    delete ptr; 
} 

L'unica differenza è che avete un altro meccanismo per fare la chiamata, e in questo caso attraverso callVirtual().

+0

Ah, e questo altro meccanismo comporta l'assunzione dell'indirizzo di una funzione membro virtuale pura. Questo è quello che mi interessava principalmente. – Abhay

1

Come detto da Magnus Skog, la parte modello non è veramente rilevante.Che cosa si riduce a è che:

(ptr->* &Base::sayHi)() 

sembra funzionare, ma

ptr->Base::sayHi() 

non lo fa, ovviamente, perché è puro sayHi virtuale.

Non sono stato in grado di trovare nello standard nello standard su ciò che accade quando si prende l'indirizzo di una funzione virtuale, o pure virtuale, sebbene. Non sono sicuro che sia legale. Funziona comunque in GCC e MSVC, e il compilatore online di Comeau non si lamenta neanche.

Modifica

Anche se è valido, come dicono le modifiche, sto ancora chiedendo che cosa significa.

Se supponiamo per semplicità che sayHi non sia puro (quindi esiste una definizione di Base::sayHi), cosa succede se prendo l'indirizzo di esso? Ricevo l'indirizzo di Base :: sayHi o l'indirizzo della funzione a cui punta il vtable (Derived :: sayHi in questo caso)?

A quanto pare, i compilatori assumono quest'ultimo, ma perché? Calling ptr->Base::sayHi() chiama sayHi nella classe base, ma prendendo l'indirizzo del Base::sayHi mi dà l'indirizzo della Derived::sayHi

Sembra incoerente per me. C'è qualche logica dietro a questo che mi manca?

+0

Anch'io sono confuso. C++ continua a sconcertarmi :-( – Abhay

+0

"_questo cosa succede se prendo l'indirizzo di esso? _" Prendi il suo ** nome **. Puntatore ai membri sono tutti sui nomi. – curiousguy

1

Questo articolo che segue discute ampiamente i puntatori di funzioni dei membri in C++, come sono implementati e dove sono i difetti. Gestisce anche i puntatori di funzioni dei membri virtuali e molto altro. Penso che risponda a tutte le tue domande.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

Viene inoltre illustrato come implementare i delegati in C++ e che cosa insidie ​​si potrebbe trappola.

Cordiali saluti,

Ovanes

+0

Una lettura eccellente, grazie. Sembra che abbia ottenuto la mia risposta (vedi modifica 3). – Abhay

Problemi correlati