2010-01-02 17 views
6

Ich finde das seltsam. Im ct von Sample_Base rufe ich bar() auf, das intern fun() aufruft, was eine rein virtuelle Funktion ist. Ich bekomme den Fehler "reine virtuelle Funktion" genannt. Was ist in Ordnung. Jetzt, wenn ich fun() direkt von Sample_Bases ctor aufrufen, bekomme ich diesen Fehler nicht. Ich habe es auf VC++ 2010 Beta 2 und auf g ++ 4.4.1 auf Ubuntu 9.10 versucht. Ich stimme zu, dass eine Implementierung für reine virtuelle Funktionen, die keine reinen virtuellen Destruktoren sind, bedeutungslos ist. Aber ich bin ein wenig überrascht über dieses Verhalten.Pure Virtual Function namens Fehler

class Sample_Base 
{ 
public: 
    Sample_Base() 
    { 
     bar(); 
     // fun(); 
    } 
    /* This is code does not throw any error. 
    Sample_Base() 
    { 
     fun(); 
    } 
    */ 

    void bar() 
    { 
     fun(); 
    } 
    virtual void fun() = 0; 
    virtual ~Sample_Base(); 
}; 

Sample_Base::~Sample_Base() 
{ 

} 

void Sample_Base::fun() 
{ 
    std::cout << "Sample_Base::fun\n"; 
} 

class Sample_Derived : public Sample_Base 
{ 
public: 
    Sample_Derived() : Sample_Base() 
    { 
     fun(); 
    } 

    void fun() 
    { 
     std::cout << "Sample_Derived::fun\n"; 
    } 

    ~Sample_Derived() 
    { 

    } 
}; 

Antwort

6

Wenn Sie die Funktion direkt aufrufen, löst der Compiler den statischen Typ Ihres Objekts (Sample_Base) auf und ruft Sample_Base::fun() direkt auf, da Sie sich im Konstruktor befinden. Da Sie eine Implementierung dafür bereitgestellt haben, findet der Compiler die Funktion und sie funktioniert.

Wenn Sie es indirekt über bar() aufrufen, muss der Compiler den dynamischen Typ verwenden, sodass ein virtueller Aufruf zur Laufzeit ausgeführt wird. Und da scheitert es, weil es eine rein virtuelle Funktion nennt.

So ist der Unterschied in dem Moment, in dem es die Funktion an den Anruf bindet.

+0

Die [C++ Standard] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf) §10.4.6 besagt, dass "ein virtueller Aufruf an eine reine virtuelle Funktion ... für das zu erstellende Objekt ... ist undefiniert ". Können Sie das kommentieren? Ich würde es lesen, dass das Aufrufen von 'fun' direkt auch undefiniert sein sollte (selbst wenn der Compiler in diesem speziellen Fall kein abstürzendes Programm erzeugt). Oder ist es ein virtueller Aufruf in diesem Kontext (wenn, warum, ich konnte es nicht im Standard finden). – Xlea

+0

@Xlea Charles Bailey weist Sie in seiner Antwort auf die richtigen Abschnitte des Standards hin. Wohlgemerkt, er hat damals den C++ 03-Standard verwendet, so dass die Abschnittsnummern jetzt anders sind, aber das könnte Sie leiten. – Gorpik

1

Beim Aufrufen einer virtuellen Funktion werden die übergeordneten Funktionen in den abgeleiteten Klassen nicht aufgerufen. Der Aufruf einer reinen virtuellen Funktion in einem Konstruktor oder Destruktor lautet Undefiniertes Verhalten.

Sie interessiert sein könnte this und this.

1

Bei Bauzeit beim Lesen, wenn der Sample_Base Konstruktor aufgerufen wird, wird das Objekt noch nicht vollständig aufgebaut ist. Speziell die Teile, die zu Sample_Derived gehören, sind noch nicht erstellte Aufrufe an virtuelle Funktionen, die von Sample_Derived überschrieben würden, rufen nicht die Implementierung in Sample_Derived auf, sondern die in Sample_Base definierte Version. Und da die Funktion dort keine Implementierung hat, erhalten Sie einen Fehler. Weitere Informationen und mögliche Problemumgehungen finden Sie auch unter this entry in the C++ FAQ Lite.

+0

Ich stimme zu, dass das Objekt nicht vollständig erstellt wird, wenn in Sample_Base bar aufgerufen wird, aber wenn ich fun() von Sample_Base-Konstruktor aufrufen, bekomme ich keinen Fehler. Das interessiert mich. – Jagannath

+0

@Jagannath: Wenn Sie den Fehler erhalten oder nicht erhalten, sollten Sie sich keine Sorgen machen. Sie sollten sich Sorgen machen, dass dieses Design gefährlich ist. Sie sollten versuchen, den Aufruf virtueller Methoden aus Ihrem Konstruktor/Destruktor zu vermeiden. –

+0

@Gal: Ich weiß, dass das Design nicht korrekt ist und es sollte vermieden werden, virtuelle Funktion von einem Konstruktor aufzurufen. Ich bin nur an dem Unterschied im Verhalten interessiert. Trotzdem danke. – Jagannath

4

Die Angabe einer Definition für eine reine virtuelle Funktion ist nicht unbedingt bedeutungslos. Das Markieren einer virtuellen Funktion bedeutet, dass die einschließende Klasse abstrakt ist und dass jede davon abgeleitete Klasse abstrakt ist, es sei denn, die endgültige Überschreibung für diese Funktion ist keine reine virtuelle Funktion. Eine reine virtuelle Funktion kann weiterhin über einen expliziten nicht-virtuellen Aufruf aufgerufen werden.

In den Körper eines Basisklassenkonstruktor (aber nicht von einem Ctor-Initialisierer), um die Version einer virtuellen, durch einen virtuellen Aufruf aufgerufenen Funktion ist eine, in der Klasse definiert sich selbst oder einer ihrer Basen und nicht von irgend Klasse überschreiben (die noch nicht konstruiert worden wäre). Dies ist explizit in 12.7 [class.cdtor]/3 angegeben.

Es ist zulässig, eine reine virtuelle Funktion explizit in einem Konstruktorkörper (dh mit einem expliziten Klassenqualifikationsmerkmal) aufzurufen - obwohl dies die Definition eines Körpers erfordern würde - es ist jedoch undefiniertes Verhalten, eine reine virtuelle Funktion aufzurufen über einen virtuellen Aufruf, der nur vom Konstruktor oder Destruktor einer abstrakten Klasse möglich ist. Dies ist explizit in 10.4 [class.abstract]/6 angegeben.

1

Dieses Verhalten ist nicht definiert, es ist explizit definiert: virtuelle Funktionen sind in Konstruktoren und Destruktoren nicht virtuell. Sie nennen die statische Version der Funktion. Wenn die Funktion rein virtuell ist, führt dies zu dem bekannten Fehler "reiner virtueller Aufruf" in VC.

Ich habe eine amüsante Variation in einem Multithread-Programm gesehen: Ein Objekt wird auf Thread A zerstört, während Thread B versucht, eine virtuelle Funktion aufzurufen. Es gab keinen virtuellen Funktionsaufruf im Konstruktor oder Destruktor, aber wir wurden immer noch mit einem reinen virtuellen Aufruffehler konfrontiert.