dynamic cast failed



  • Hi,

    ich habe foldendes klassenkonstruct (vereinfacht):
    der dynamic cast schlaegt fehlt, darf ich ein reinterpret cast verwenden oder ist das undefined behavior?

    #include <iostream>
    #include <string>
    
    class Base {
    public:
        virtual ~Base() {}
    };
    
    class DerivedA : public Base {
    public:
        virtual std::string name() = 0;
    };
    
    class FooA : public DerivedA {
    public:
    	virtual std::string name() { return "fooA"; }
    };
    
    class DerivedB : public Base {
    public:
        virtual std::string name() = 0;
    };
    
    class FooB : public DerivedB {
    public:
    	virtual std::string name() { return "fooB"; }
    };
    
    int main() {
    	// your code goes here
    
    	DerivedA *fa = new FooA;
    
    	//DerivedB *fb = reinterpret_cast<FooB*>(fa);
    	DerivedB *fb = dynamic_cast<FooB*>(fa);
    	if (fb) {
    		std::cout << fb->name() << '\n';
    	}
    	else {
    		std::cout << "cast failed\n";
    	}
    
    	return 0;
    }
    

  • Mod

    UB. Ergibt auch nicht viel Sinn.



  • ok, warum darf man dann folgendes auch nicht machen?

    #include <iostream>
    #include <string>
    
    class Base {
    public:
        virtual ~Base() {}
        virtual std::string name() = 0;
    };
    
    class DerivedA : public Base {
    public:
        virtual std::string name() = 0;
    };
    
    class FooA : public DerivedA {
    public:
    	virtual std::string name() { return "fooA"; }
    };
    
    class DerivedB : public Base {
    public:
        virtual std::string name() = 0;
    };
    
    class FooB : public DerivedB {
    public:
    	virtual std::string name() { return "fooB"; }
    };
    
    int main() {
    	// your code goes here
    
    	Base *fa = new FooA;
    
    	Base *fb = dynamic_cast<FooB*>(fa);
    	if (fb) {
    		std::cout << fb->name() << '\n';
    	}
    	else {
    		std::cout << "cast failed\n";
    	}
    
    	return 0;
    }
    


  • Weil ein FooA kein FooB ist?!

    Was hast Du denn vor?



  • ich habe einen FooA* und brauche den wert einer member variable von FooB* ... wie geht das denn ohne das ich die member variable in Base anlege?



  • Garnicht.

    Warum sollte ein Objekt vom Typ FooA auch Member der Klasse FooB haben? Ein FooA hat alles, was FooA hat, plus allem, was die Basen von FooA haben. FooB hat mit dem ganzen nicht das geringste zu tun.

    EDIT:
    Um das mal an den schlechten Beispielen zur Vererbung, die so kursieren, zu verdeutlichen: Ich habe ein Auto (Unterklasse von Fahrzeug) und möchte jetzt an das Member "Ruder" der Klasse Schiff (ebenfalls Unterklasse von Fahrzeug) rankommen. Ergibt auch keinen Sinn, oder?



  • ich habe doch einen pointer vom type Base* ... bringt mir das nichts?



  • casting schrieb:

    ich habe doch einen pointer vom type Base* ... bringt mir das nichts?

    Klar, du kaufst ein Fahrzeug, und kannst dann jeden Morgen entscheiden, ob du einen Bus oder ein Motorrad haben willst.



  • tja...

    mein anwendungsfall ist folgendem beispiel aehnlich:

    https://github.com/lsegal/my_toy_compiler/blob/master/node.h
    

    ich habe einen n-aray tree ... und wollte die neighbors in der Node klasse speichern anstatt das jeder klasse ein eigens lhs und rhs hat...

    class Node {
    public:
        vector<Node*> children;
        virtual ~Node() {}
    };
    

    und beim erzeugen von NExpressionStatement brauche ich einen member von NExpression...



  • casting schrieb:

    Hi,

    ich habe foldendes klassenkonstruct (vereinfacht):
    der dynamic cast schlaegt fehlt, darf ich ein reinterpret cast verwenden oder ist das undefined behavior?

    Das Verhalten ist nicht undefiniert, es funktioniert einfach nicht.

    manni66 zeigt, dass er das Problem versteht, aber es nicht erklären mag. Also mache ich das mal:
    Du forderst ein DerivedA an. Nehmen wir an, dass das ein Bus ist und der Bus von Fahrzeug abgeleitet ist. Ein Bus hat Eigenschaften: Die Anzahl der Sitze, die Anzahl der Türen, Kofferraumvolumen, sowas.

    Nun machst du den Cast. Ein Objekt einer Klasse mit virtuellen Funktionen erhält zusätzlich die Information, welche Klasse sie angehört. Der dynamic_cast vergleicht nun Zieltyp (Motorrad) und den Typ des Objekt, das Du wandeln möchtest. Ein Bus ist ein Fahrzeug, aber eben kein Motorrad. Der dynamic_cast schlägt fehl.
    Du hast einen Bus angelegt, das heißt im Speicher liegen die Werte für Anzahl der Sitze etc. Und diesen Speicherblock kannst Du jetzt nicht sinnvoll fragen, ob ein Helmfach vorhanden ist.
    Genau das macht der reinterpret_cast. Er erlaubt Dir einen Zeiger auf einen Bus zu einem Zeiger auf ein Motorrad zu machen und dann dort nach dem Helmfach zu fragen.

    reinterpret_cast ist daher (fast) immer falsch und wenn Du Dir nicht 100%ig sicher bist, was Du tust, ist reinterpret_cast grundsätzlich falsch.

    Der Cast ändert die Daten im Speicher nicht. Er verwandelt einen Bus nicht in ein Motorrad. Wenn Du also Fragen an ein DerivedB Objekt hast, musst Du auch ein DerivedB Objekt irgendwo anlegen.

    Du kannst den Bus * auf ein Fahrzeug * "casten" (das geht implizit, weil ein Bus ja ein Fahrzeug ist) und mit einem dynamic_cast versuchen dieses Fahrzeug * in einen Motorrad * (klappt nicht) oder Bus * (klappt).
    dynamic_cast prüft, ob der Cast funktioniert, static_cast macht ihn, falls er halbwegs Sinn ergeben könnte und sich der Entwickler sicher ist, dass eine Prüfung überflüssig ist, reinterpret_cast macht ihn auch dann, wenn er keinen Sinn ergibt. reinterpret_cast ist also ein gutes Zeichen, dass man vermutlich etwas Unsinniges macht.


Anmelden zum Antworten