Unterschied zwischen dem Aufruf einer virtuellen Funktion und einer nicht virtuellen Funktion?

8

Das ist in der Tat eine Interviewfrage, ich kann die Antwort nicht herausfinden. Weiß jemand davon? Sie können über jeden Unterschied sprechen, zum Beispiel die Daten, die in den Stapel geschoben werden.

    
cheng 08.01.2012, 09:18
quelle

5 Antworten

24

Obwohl Virtualisierung / dynamischer Versand strikt implementationsdefiniert ist, implementieren die meisten (lesen alle bekannten ) Compiler dies, indem sie vptr und vtable verwenden.

Abgesehen davon ist der Unterschied zwischen dem Aufruf einer nicht virtuellen Funktion und einer virtuellen Funktion:

Nicht-virtuelle Funktionen werden aufgelöst statically at Compile-time , während virtuelle Funktionen aufgelöst werden dynamically at Run-time .

Um diese Flexibilität zu erreichen, um entscheiden zu können, welche Funktion zur Laufzeit aufgerufen wird, Bei virtuellen Funktionen gibt es einen kleinen Overhead.

Ein zusätzlicher fetch -Aufruf, der ausgeführt werden muss, und dies ist der Overhead / Preis, den Sie für den dynamischen Versand bezahlen.

Im Falle einer nicht virtuellen Funktion lautet die Reihenfolge der Aufrufe:

%Vor%

Der Compiler muss fetch -Adresse der Funktion und dann call it haben.

Im Falle von virtuellen Funktionen ist die Reihenfolge:

%Vor%

Der Compiler muss fetch die vptr von this , dann fetch die Adresse der Funktion von vptr und dann call die Funktion.

Dies ist nur eine vereinfachte Erklärung der tatsächlichen Sequenz möglicherweise viel komplexer als das, aber das ist, was Sie wirklich wissen müssen, Man muss nicht wirklich die Implementierung nitty kiesig wissen.

Gut gelesen:

Vererbung & amp; Virtuelle Funktionen

    
Alok Save 08.01.2012, 09:25
quelle
5

Wenn Sie eine Basisklasse 'Basis' und eine abgeleitete Klasse 'Abgeleitet' haben und eine Funktion 'func ()' in der Basisklasse als virtuell definiert haben. Diese Funktion wird von der Klasse Abgeleitete überschrieben.

Angenommen, Sie definieren

%Vor%

Dann wird der 'Func' der abgeleiteten Klasse aufgerufen. Wenn "func ()" in Base nicht als virtuell definiert wurde, würde es von der Klasse "Base" aufgerufen werden. Dies ist der Unterschied, wie sich der Funktionsaufruf für virtuelle und nicht-virtuelle Funktionen unterscheidet     

Shraddha 08.01.2012 09:29
quelle
3

Beim Aufruf einer virtuellen Methode muss nachschlagen, welche Funktion in einer virtuellen Funktionstabelle aufgerufen werden soll.

    
icktoofay 08.01.2012 09:21
quelle
3

Der Aufwand beim Aufruf einer virtuellen Methode ist erheblich.

Auch das.

    
quelle
1

Nicht virtuelle Member-Funktionen werden statisch aufgelöst. Die Memberfunktion ist statisch zur Kompilierzeit basierend auf dem Typ des Zeigers (oder der Referenz) auf das Objekt.

Im Gegensatz dazu sind virtuelle Member-Funktionen zur Laufzeit dynamisch bindend . Wenn die Klasse mindestens eine virtuelle Member-Funktion besitzt, fügt der Compiler während der Konstruktion des Objekts einen versteckten Zeiger in das Objekt ein, das als vptr (virtuelle Tabellenadresse) bezeichnet wird.

Der Compiler erstellt eine v-Tabelle für jede Klasse mit mindestens einer virtuellen Funktion. Virtuelle Tabelle enthält die Adresse der virtuellen Funktion. Es kann ein Array oder eine Liste (abhängig vom Compiler) des virtuellen Funktionszeigers sein Während einer Verteilung einer virtuellen Funktion folgt das Laufzeitsystem dem V-Zeiger des Objekts (holt die Adresse von dem Klassenobjekt) in die V-Tabelle der Klasse, dann wird Offset zu der Basisadresse (vptr) hinzugefügt und ruft die Funktion auf.

Der space-cost Overhead der obigen Technik ist nominal: ein zusätzlicher Zeiger pro Objekt (aber nur für Objekte, die eine dynamische Bindung durchführen müssen) plus einen zusätzlichen Zeiger pro Methode (aber nur für virtuelle Methoden). Der time-cost Overhead ist auch ziemlich nominal: verglichen mit einem normalen Funktionsaufruf erfordert ein virtueller Funktionsaufruf zwei zusätzliche Aufrufe (einen, um den Wert des V-Pointers zu erhalten, eine Sekunde, um die Adresse zu erhalten der Methode).

Diese Laufzeitaktivität tritt bei nicht virtuellen Funktionen nicht auf, da der Compiler nicht-virtuelle Funktionen ausschließlich zur Kompilierungszeit basierend auf dem Typ des Zeigers auflöst.

Ich habe ein einfaches Beispiel genommen, um besser zu verstehen, wie die Bindung für nicht-virtuelle Funktion & amp; virtuelle Funktion und Funktionsweise des virtuellen Funktionsmechanismus.

%Vor%

Wie die vtable für Base & amp; abgeleitete Klasse

Der Assemblercode wird zum besseren Verständnis generiert.

%Vor%

Ich habe die Informationen von vtable aus virtuals.s für die Base- bzw. Derived-Klasse abgerufen:

%Vor%

Wie Sie sehen können, Spaß & amp; fun1 sind nur zwei virtuelle Funktionen in der Basisklasse. Vtable der Basisklasse (_ZTV4Base) hat Einträge beider virtueller Funktionen. Vtable hat keinen Eintrag für nicht-virtuelle Funktion. Bitte verwechseln Sie nicht den Namen des Spaßes (ZN4Base3funEv) & amp; fun1 (ZN4Base4fun1Ev), ihr Name wurde verstümmelt.

Abgeleitete Klasse vtable hat Baumeinträge

  1. fun (_ZN7Derived3funEv) überschreiben Funktion
  2. fun1 (_ZN4Base4fun1Ev) von der Basisklasse
  3. geerbt
  4. fun3 (_ZN7Derived4fun3Ev) neue Funktion in der abgeleiteten Klasse

Wie nicht virtuelle Funktion & amp; virtuelle Funktion genannt?

für nicht-virtuelle Funktion

%Vor%

Einfach sagen, holen und aufrufen (Bindung erfolgte zur Kompilierzeit)

für nicht-virtuelle Funktion

%Vor%

hole das vptr, füge den Funktionsoffset hinzu, rufe die Funktion auf (Bindung zur Laufzeit erfolgt)

Assembly von 64 ist verwirrend die meisten C ++ - Programmierer, aber wenn jemand gerne diskutieren würde dann begrüßen

    
Ajay yadav 25.12.2015 12:14
quelle