2012-04-02 10 views
10

Ja, ich weiß, dass Downcast mit dynamic_cast kann nicht kompilieren, wenn die Base nicht polymorph ist, aber mein Problem ist nicht darüber.`dynamic_cast` von Base zu Derived

class Base { 
    public: 
     virtual void bar() 
     { 
      cout << "bar\n"; 
     } 
}; 

class Derived: public Base { 
    public: 
     void foo() 
     { 
      cout << "foo\n"; 
     } 
}; 

int main() 
{ 
    Base *pb; 
    Derived *pd; 

    pb = new Derived; //Base* points to a Derived object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo 

    pb = new Base; //Base* points to a Base object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo, too. Why? 
} 

Ich dachte, wenn pb = new Derived;, pb auf ein Objekt tatsächlich Punkte Derived in Haufen liegt. Nach pd = dynamic_cast<Derived*>(pb);, pd verweist auch auf das Derived Objekt, also pd->foo() sollte OK sein.

Aber wenn pb = new Base;, was pb Punkte ist ein Base Objekt in Haufen, dann nach pd = dynamic_cast<Derived*>(pb);, wie konnte pd->foo() funktioniert? Hat dynamic_cast das Objekt Base in Heap in ein Derived Objekt verwandelt?

Antwort

17

In C++ hat jede Instanz einer Klasse ihre eigene Version von Datentypen, aber alle Klassen haben die gleiche Funktion im Speicher (außer für Inline-Funktionen). In Ihrem Fall, wenn Sie sagen, so etwas wie:

pd->foo(); 

Sie im Wesentlichen Derived::foo anrufen, die eine Funktion im Speicher ist und der Compiler weiß, wo es ist. Die Sache ist, es ist überhaupt nicht abhängig von pd. Allerdings, wenn Sie so etwas wie dieses hatte:

class Derived : public Base { 
    private: 
     int a; 

    public: 
     Derived() { a = 100; } 

     void foo() { 
      std::cout<<a<<std::endl; 
     } 
}; 

Dann verursacht pd->foo() einen Segmentation fault. Hier ist Ihr dynamischer Cast fehlgeschlagen und wenn Derived::foo aufgerufen wird, wird 0 als das this Objekt übergeben. Es war in dem vorherigen Fall in Ordnung, als das this Objekt nie verwendet wurde. Im zweiten Fall wird es jedoch verwendet und verursacht daher einen Segmentierungsfehler.

+0

Wenn ich 'pd-> foo();' sage, wird 'foo()' aufgerufen, egal, 'pd' ist' NULL' oder nicht? – Alcott

+0

@Alcott ja, aber der 'this' Parameter wird als NULL übergeben (da ist der Wert von' pd'). Daher der erwartete Absturz bei der Dereferenzierung durch den Zugriff auf "a" in Rohans Beispiel. – littleadv

+1

Es ist weitgehend Compiler abhängig, und wie @Luchian Grigore erwähnt hat, kann alles passieren. Also, in den meisten Fällen ja, aber darauf kann man nicht zählen. –

7

In Ihrem foo greifen Sie nicht auf this zu, was in diesem Fall NULL sein sollte. Das ist, was dynamic_cast zurückgibt, wenn die Besetzung nicht getan werden kann.

Grundsätzlich sind Sie hier im Bereich "undefined Verhalten".

+0

FYI, ich + 1'd Ihre Antwort, weil es korrekt ist. * Aber * sehe immer noch nicht, wie so etwas glücklich ist. Sie können auf böse Fehler stoßen, die schwer zu verfolgen sind, weil das Programm nicht abstürzt, wenn es sein sollte. –

+1

@LuchianGrigore [Luck] (http://en.wiktionary.org/wiki/luck#Noun) (n) 1. Etwas, das zufälligerweise zufällig geschieht. – Potatoswatter

6

Sie geraten in undefiniertes Verhalten. Sie sollten den Rückgabetyp dynamic_cast überprüfen.

pd = dynamic_cast<Derived*>(pb); 

Dieser Wert null zurück, und Sie rufen eine Funktion auf einem NULL Zeiger. Alles kann passieren.

+0

Aber wenn 'pd == NULL', dann warum kein Laufzeitfehler? – Alcott

+0

@Alcott es ist undefiniertes Verhalten. Alles kann passieren. –

+1

@Alcott technisch Luchians Antwort ist korrekt. Aber praktisch liegt der Grund darin, dass es für die Compiler-Entwickler einfacher ist, dies einfach zu ignorieren, da sie erlaubt sind (weil der Standard in diesem Fall nicht definiert, was zu tun ist), anstatt komplizierte und Laufzeitkostenprüfungen durchzuführen jeder Zeigerzugriff. Also was auch immer passiert - passiert, und alles ist richtig. – littleadv

1

Bitte überprüfen Sie immer, ob die Besetzung erfolgreich ist, bevor Sie versuchen, sie zu verwenden. Ich denke, es ist der Vorteil des Castings. Sie können überprüfen, ob es erfolgreich ist oder nicht.

pd = dynamic_cast<Derived*>(pb); 
if(pd!=NULL) 
    pd->foo(); 

Wenn die Umwandlung fehlschlägt pd hat den Wert NULL. Verwenden Sie nicht pd, außer Sie sind sicher, dass es einen Wert hat. und dann nur de Referenz it

Verwandte Themen