2015-06-11 11 views
10

Ich habe versucht, das Beispiel in this answer zu "reparieren", um zu demonstrieren, wie eine reine virtuelle Funktion aufgerufen werden kann.Warum ist das kein Aufruf einer rein virtuellen Funktion?

#include <iostream> 
using namespace std; 

class A 
{ 
    int id; 
public: 
    A(int i): id(i) {} 
    int callFoo() { return foo(); } 
    virtual int foo() = 0; 
}; 

class B: public A 
{ 
public: 
    B(): A(callFoo()) {} 
    int foo() { return 3; } 
}; 

int main() { 
    B b; // <-- this should call a pure virtual function 
    cout << b.callFoo() << endl; 
    return 0; 
} 

Aber ich bekomme keine Laufzeitfehler here (with C++ 4.9.2), aber der Ausgang 3. habe ich versucht, das gleiche mit Borland C++ 5.6.4, aber es eine Zugriffsverletzung Ich erhalte. Ich denke, dass foo() rein virtuell im Aufruf des Konstruktors der Basisklasse sein sollte.

Wer ist falsch? Soll ich mehr Compiler ausprobieren? Habe ich recht, wenn ich virtuelle Funktionen verstehe?

+4

Ich würde die Ergebnisse eines Tests mit Borland C++ nicht verwenden, um zu sehen, ob ein Codeschnipsel gültig und/oder konform mit dem Standard ist;) – CoryKramer

+0

@CoryKramer Virtuelle Funktionen werden seit Jahrzehnten verwendet. – Wolf

+1

Ich bin mir dessen bewusst, ich machte eine freche Bemerkung über jemanden, der immer noch Borland IDEs verwendet – CoryKramer

Antwort

14

Ihr Code hat Undefined Behavior: UB ruft eine Memberfunktion für ein Objekt (auch ein nicht virtuelles) auf, bevor alle Basisklassen initialisiert wurden. C++ 14 (n4140) 12.6.2/14, Hervorhebung meins:

Elementfunktionen (einschließlich virtueller Elementfunktionen, 10.3) können für ein Objekt im Bau aufgerufen werden. Ähnlich kann ein Objekt im Aufbau der Operand des Operators typeid (5.2.8) oder eines dynamic_cast (5.2.7) sein. Wenn jedoch diese Operationen in einem Ctor-initializer ausgeführt werden (oder in einer Funktion direkt oder indirekt von einem Ctor-initializer genannt) vor allen mem-initializers für Basisklassen abgeschlossen haben, das Ergebnis der Vorgang ist nicht definiert. ...

Ctor-initializer ist die gesamte Liste folgende :. Mem-Initialisierer ist ein Element dieser Liste.

+0

Wahr und diese UB-Regel ist wohlbegründet. Aber das bringt nur Kopfschmerzen - kein Compiler und kein statischer Code-Checker findet alle Fehler. Ich war schockiert, dass solche einfachen Inline-Fälle nicht sofort abgelehnt wurden. – Wolf

+0

@Wolf: Der Compiler [ist nicht erforderlich, um eine vollständige Kontrolle-Flow-Analyse durchzuführen] (http://blogs.msdn.com/b/oldnewthing/archive/2013/10/11/10455907.aspx), denn das könnte statisch zur Kompilierzeit nicht möglich sein. – Kevin

+0

@Kevin Implementieren rein virtueller Funktionen, um * reine virtuelle Funktion namens * Nachrichten loszuwerden scheint mir wie Missbrauch, oder war es genau für diesen Zweck konzipiert? – Wolf

5

Die Anweisung B b; ruft den Standardkonstruktor auf B.

Bei der Erstellung von B wird nichts zu B erstellt, bis A vollständig aufgebaut ist.

So in Versuch, callFoo() aufzurufen, ist das Verhalten undefiniert, da Sie sich nicht darauf verlassen können, dass die V-Tabelle für die Klasse B eingerichtet wird.

Zusammenfassend: Das Verhalten beim Aufrufen einer reinen virtuellen Funktion während der Konstruktion einer abstrakten Klasse ist undefiniert.

+0

Ich vermutete, dass dies der Fall wäre, weil. So, es scheint, ich war ein bisschen zu faul mit meinem "Reparatur" Vorschlag. – Wolf