2009-06-09 6 views
5

Ich lese irgendwann zurück (wahrscheinlich auf c.l.C++. Moderiert), dass virtuelle Funktionsaufrufe templatetisiert werden können. Ich habe etwas in den folgenden Zeilen versucht.Virtuelle Aufrufe unter Verwendung der Adresse des reinen virtuellen Mitglieds. Ist es legal?

Die templated-Methode, obwohl die Adresse der reinen virtuellen Basiselementmethode zur Kompilierzeit angegeben wird, ruft die richtige Methode zur Laufzeit auf. Ist es in C++ Standard zulässig, die Adresse eines reinen virtuellen Members zu übernehmen?

Vielen Dank im Voraus

EDIT-1: entfernte ich den zweiten Teil der Frage: 'Wie funktioniert das?'. Sieht so aus, als ob das Aufmerksamkeit erregt.

EDIT-2: Ich suchte c.l.C++ moderiert und kam in dieser link (http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/5ddde8cf1ae59a0d).. Der Konsens scheint so zu sein, da der Standard ihn nicht einschränkt, er ist gültig.

EDIT-3: Nach dem Lesen des Codeprojektartikels (danke an ovanes), denke ich, dass die Compiler einige Magie tun. Da virtuelle Funktionen über vtable implementiert werden (was compilerspezifisch ist), ergibt die Adresse einer virtuellen Funktion immer den Offset in der vtable. Abhängig vom verwendeten 'this' -Zeiger wird die entsprechende Funktion (deren Adresse auf dem Offset steht) aufgerufen. Ich bin mir nicht sicher, wie ich das rechtfertigen soll, denn der Standard sagt nichts darüber aus!

Antwort

0

Ich würde vermuten, dass es nicht definiert ist. Ich konnte nichts in der Spezifikation finden.

virtuelle Methoden werden mithilfe des Konzepts vtable implementiert.

Ich würde sagen, dass es Compiler-Implementierung ist. Ich glaube nicht, dass es wichtig ist, dass es ein rein virtuelles ist, das gleiche würde passieren, wenn es auch nur virtuell wäre.

Ich habe gerade Ihren Code mit Visual Studio 2008 kompiliert und die Exe demontiert. Was VS2008 tut, ist eine Thunk-Funktion zu erstellen, um mit dem übergebenen "this" -Zeiger zum vtable-Eintrag zu springen.

Dies ist das Setup und Aufruf der CallVirtual-Template-Funktion.

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)) 

So übergeben Sie die Funktionszeiger auf die Thunk-Funktion: [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 

die All Thunk-Funktion macht, ist mit der V-Tabelle in der realen Klassenmethode zu springen.

+0

Dank. Kennen Sie einen Abschnitt des C++ - Standards, der Member-Function-Zeiger auf reine virtuelle Elemente erwähnt? Ich habe versucht, sie zu finden, konnte aber keine spezifischen Details finden. Da Member-Pointer auf einen unvollständigen Typ erlaubt sind, sind Member-Pointer auf pure-virtuals ebenfalls erlaubt. – Abhay

+0

Ich habe die Frage falsch gelesen. Ich werde meine Antwort aktualisieren. –

+0

+1 Ihre bearbeitete Antwort kommt einer Antwort auf diese Frage nahe :-). Ich beginne zu denken, dass die Adressierung virtueller Funktionen (rein oder nicht) und deren Aufruf eher auf einen implementierungsdefinierten Aspekt der Sprache gerichtet ist. – Abhay

1

Sicher ist es. Der Code ist nicht anders als nur die rein virtuelle Methode wie folgt aufrufen:

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

Der einzige Unterschied ist, dass Sie einen anderen Mechanismus für das Erledigen der Ruf haben, und in diesem Fall durch callVirtual().

+0

Ahh, und dieser andere Mechanismus beinhaltet die Adresse einer reinen virtuellen Elementfunktion. Darum ging es mir hauptsächlich. – Abhay

1

Wie Magnus Skog sagte, ist der Template-Teil nicht wirklich relevant.Was es läuft darauf hinaus, dass:

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

scheint zu funktionieren, aber

ptr->Base::sayHi() 

offensichtlich nicht, weil sayHi virtuelle rein ist.

Ich konnte nichts im Standard darüber finden, was passiert, wenn Sie die Adresse einer virtuellen oder rein virtuellen Funktion nehmen. Ich bin mir nicht sicher, dass es legal ist. Es funktioniert jedoch in GCC und MSVC, und Comeaus Online-Compiler beklagt sich auch nicht.

bearbeiten

Auch wenn es gültig ist, wie Ihre Änderungen sagen, ich bin immer noch fragen, was es bedeutet.

Wenn wir der Einfachheit halber annehmen, dass sayHi ist nicht rein (so eine Definition von Base::sayHi existiert), was passiert dann, wenn ich die Adresse davon nehme? Bekomme ich die Adresse von Base :: sayHi oder die Adresse der Funktion, auf die der vtable zeigt (in diesem Fall abgeleitet :: sayHi)?

Offenbar übernehmen Compiler die letzteren, aber warum? ptr->Base::sayHi() Aufruf ruft sayHi in der Basisklasse, aber die Adresse Base::sayHi Einnahme gibt mir die Adresse von Derived::sayHi

Es scheint mir inkonsistent. Gibt es Gründe dafür, dass ich vermisse?

+0

Ich bin auch verwirrt. C++ verwirrt mich weiter :-( – Abhay

+0

"_ was dann passiert, wenn ich die Adresse davon nehme? _" Sie nehmen seinen ** Namen **. Zeiger auf Mitglieder sind alles über Namen. – curiousguy

1

In diesem Artikel werden Memberfunktionszeiger in C++ ausführlich erläutert, wie sie implementiert werden und wo die Fehler liegen. Es behandelt auch virtuelle Mitgliedsfunktionszeiger und vieles mehr. Ich denke, es wird alle Ihre Fragen beantworten.

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

Es zeigt auch, wie die Delegierten in C++ implementieren und welche Gefahren könnten Sie Falle.

Mit freundlichen Grüßen

Ovanes

+0

Eine ausgezeichnete Lektüre, Danke. Sieht so aus, als hätte ich meine Antwort erhalten (Siehe Edit-3). – Abhay

Verwandte Themen