2016-03-22 16 views
0

ich weiß, sind hier über vtables viele Fragen gestellt, aber ich bin immer noch ein wenig verwirrt.Sind vtables nur mit einem Zeiger auf Basisklasse verwendet

Verwendet werden vtables nur dann, wenn wir einen Zeiger auf eine Basisklasse aufzurufen, die virtuelle Funktion von abgeleiteten Klassen zu lösen?

In meinem Beispiel unten, in Fall 1, werden zur Laufzeit hier Vtables verwendet, obwohl das Tiger-Objekt nicht dynamisch auf dem Heap/Free Store erstellt wurde?

In Fall 2 werden Vtables verwendet, obwohl der Compiler zur Kompilierzeit weiß, dass wir auf ein Tiger-Objekt zeigen.

Was ist mit Fall 3?

Vielen Dank im Voraus.

#include <iostream> 

using namespace std; 

class Animal // base class 
{ 
    public: 
     virtual void makeNoise() {cout<<" "<<endl;} 
}; 

class Tiger: public Animal 
{ 
    public: 
     void makeNoise() {cout<<"Tiger Noise"<<endl;} 
}; 

class Elephant: public Animal 
{ 
    public: 
     void makeNoise() {cout<<"Elephant Noise"<<endl;} 
}; 

int main() 
{ 
    //case 1 
    Tiger t1; 
    Animal* aptr = &t1; 
    aptr->makeNoise(); // vtables used? 

    //case 2 
    Tiger t2; 
    Tiger* tptr = &t2; //vtables used ? 
    tptr->makeNoise(); 

    //case 3 
    Elephant e1;  //vtables used ? 
    e1.makeNoise(); 

} 
+0

Eine Vtable wird verwendet, wenn Sie eine virtuelle Methode über einen übergeordneten Klassenzeiger wie in Fall 1 aufrufen. – user2061057

+0

@ user2061057 ... oder übergeordnete Klassenreferenz ... –

+1

Es ist Aufgabe des Compilers, eine vtable zu verwenden oder nicht in jedem einzelnen Fall. Wenn der Compiler beweisen kann, was der abgeleitete Typ des Zeigeobjekts ist, ist es völlig in Ordnung, für diesen Aufruf keine Vtable zu verwenden. –

Antwort

4

Ob eine bestimmte Compiler eine virtuelle Funktionstabelle oder einen ganz anderen Mechanismus verwendet dynamische virtuelle Funktion Dispatch zu implementieren, ist bis zu diesem internen Implementierung des Compilers. Wenn Sie eine Antwort auf das Verhalten eines bestimmten Compilers wünschen, konsultieren Sie die Dokumentation und/oder den Quellcode dieses Compilers. so

Die Sprache C++ selbst definiert, wie virtueller Funktionsaufruf arbeiten muss, und überlässt es den Compiler, um es zu machen.

Was der Standard erfordert, ist der Aufruf einer virtuellen Funktion an den endgültigen Overrider, basierend auf dem dynamischen Typ des Objekts, auf dem die Funktion aufgerufen wird. In Ihrem Code ist der dynamische Typ t1 und t2Tiger und der dynamische Typ e1 ist Elephant.

Ja, die meisten (wenn nicht alle) Compiler verwenden, um die virtuelle Funktionstabelle virtuelle Funktionsaufrufe zu implementieren. Ja, jeder ordentliche Compiler sollte seine Versuche, den dynamischen Versand zur Kompilierzeit aufzulösen, maximieren, wenn er dazu in der Lage ist, und wenn möglich die Verwendung virtueller Tabellen durch direkten Aufruf ersetzen (das ist ein Qualitätsproblem für den Compiler)).

Welcher der Aufrufe in Ihrem Beispiel statisch versendet wird, hängt davon ab, wie "aggressiv" (oder "klug", wenn Sie bevorzugen) der Optimierer Ihres Compilers ist.

Ich würde sagen, dass jeder vernünftige Compiler den Aufruf durch e1 statisch versenden sollte, auch mit deaktivierten Optimierungen. Es wäre eine völlig unnötige pessimization, den dynamischen Dispatch-Mechanismus dort aufzurufen.

Was die Anrufe über aptr und tptr, es hängt davon ab, ob der statische Analysator des Compilers Optimierers ist in der Lage aptr und tptr eliminiert, so dass sie mit der Nutzung des eigentlichen Objekts ersetzt sie weisen auf (da diese Informationen verfügbar sind Kompilierzeit). Ein anständiger Optimierer sollte dazu in der Lage sein und alle 3 Aufrufe statisch versenden.

Um sicher zu sein, wie der Compiler den Anruf behandelt, überprüfen Sie die generierte Assembly.

0

Wie die anderen Kommentare sagen, wird die Verwendung von vtables vom Compiler gehandhabt, der versuchen könnte, seinen Zugriff zu optimieren, solange die produzierte Ausgabe die erwartete ist.

Jedoch können wir über vtables als Tabellen denken, dass die Adressen der virtuellen Methoden enthalten.Jeder Aufruf einer Methode, die in einer übergeordneten Klasse als "virtual" deklariert wurde, sollte die vtable zur Laufzeit überprüfen, um die spezifische Adresse zu kennen, an die gesprungen werden soll.

Dies ist das Verhalten Programmierer erwarten, trotz der spezifischen Mechanismus kann schwieriger sein, und möglicherweise nicht auf Abfrage Vtables überhaupt verlassen, wenn Compiler die Adresse zur Kompilierzeit bestimmen kann.

Also, in all diesen Fällen kann der Compiler klug genug sein, die Adresse zur Kompilierzeit zu setzen. Man sollte sich aber im "worst case" darauf verlassen, dass in jedem Fall ein Zugriff auf die vtable erfolgt, da man virtuelle Methoden - das ist das erwartete Verhalten - aufruft und den Compiler die gewünschten Optimierungen machen lässt es muss tun.

Nur zur Klarstellung dessen, was Sie in Fall 1 sagen, hat vtable access nichts darüber, ob das Objekt im Heap oder im Stack zugeordnet wurde. Dies sind völlig unterschiedliche Konzepte.