Frage zu dynamic_cast



  • Hi

    class A {
      public:
        A () {cerr << " Ich bin ein A!" << endl;}
        void Call() {cerr << " Ich bin ein A-Call!" << endl;}
        };
    
    class B: public A {
      public:
        B () {cerr << "  Ich bin ein B!" << endl;}
        void Call() {cerr << "  Ich bin ein B-Call!" << endl;}
        };
    
    class C: public B {
      public:
        C () {cerr << "   Ich bin ein C!" << endl;}
        void Call() {cerr << "   Ich bin ein C-Call!" << endl;}
        };
    
    void Test1() {
    
        cerr << "test1!" << endl;
    
        cerr << "A!" << endl;
        A my_a;
        cerr << endl;
    
        cerr << "B!" << endl;
        B my_b;
        cerr << endl;
    
        cerr << "C!" << endl;
        C my_c;
        cerr << endl;
    
        A* my_a_ptr = 0;
        B* my_b_ptr = 0;
        C* my_c_ptr = 0;
    
        my_a_ptr = &my_a;
        cerr << "my_a_ptr" << endl;
        my_a_ptr->Call();
    
        my_c_ptr = &my_c;
        cerr << "my_c_ptr" << endl;
        my_c_ptr->Call();
    
        my_b_ptr = &my_b;
        cerr << "my_b_ptr" << endl;
        my_b_ptr->Call();
    
        my_a_ptr = new B();
        cerr << "my_a_ptr = new B()" << endl;
        my_a_ptr->Call();
    
        my_b_ptr = dynamic_cast<B*> (my_a_ptr);
        my_b_ptr->Call();
    
        }
    

    fuehrt zu

    test1.cpp:xx: error: cannot dynamic_cast ‘my_a_ptr’ (of type ‘class A*’) to type ‘class B*’ (source type is not polymorphic)

    ersetzt man das ganze durch einen static_cast klappt alles wie gewuenscht ...
    wieso ist das so?

    Gruesse



  • Gleich noch eine hinterher

    cerr << "my_a_ptr = new B()" << endl;
        my_a_ptr = new B();
        my_a_ptr->Call();
        cerr << endl;
    
        cerr << "my_b_ptr = static_cast<B*> (my_a_ptr)" << endl;
        my_b_ptr = static_cast<B*> (my_a_ptr);
        my_b_ptr->Call();
        cerr << endl;
    
        cerr << "my_c_ptr = static_cast<C*> (my_a_ptr)" << endl;
        my_c_ptr = static_cast<C*> (my_a_ptr);
        my_c_ptr->Call();
        cerr << endl;
    

    fuehrt zu:

    my_a_ptr = new B()
    Ich bin ein A!
    Ich bin ein B!
    Ich bin ein A-Call!

    my_b_ptr = static_cast<B*> (my_a_ptr)
    Ich bin ein B-Call!

    my_c_ptr = static_cast<C*> (my_a_ptr)
    Ich bin ein C-Call!

    😕 Warum kann ich my_a_ptr in einen C* casten (nur statisch natuerlich) obwohl es nur die Information eines B* enthaelt? Wird im Zweifelsfall dann nicht information hinzugedichtet?

    Gruesse



  • Frager schrieb:

    ...

    class A {
      public:
        A () {cerr << " Ich bin ein A!" << endl;}
        void Call() {cerr << " Ich bin ein A-Call!" << endl;}
    

    ...

    Hi,

    schreib da mal "virtual" vor:

    virtual void Call() { ...
    

    (kannst Du bei den "Kindern" uch machen, ist da dann aber egal).
    Du brauchst da gar keinen cast .... 😃

    Und die ganze "Pointerei" ist auch ziemlich überflüssig:

    void Test2(A* a) { a->Call(); }
    
    void Test1() {
        A my_a;
        B my_b;
        C my_c;
    
        my_a.Call();
        my_b.Call();
        my_c.Call();
    
       Test2(&my_a);
       Test2(&my_b);
       Test2(&my_c);
    

    Allerdings scheint nicht ganz klar zu sein, was Du willst: Soll Dein Objekt eine (je nach Klasse speziell zugeschnittene) Funktion haben, dann nimm virtual .... wenn nicht, dann sollten sie auch nicht denselben Namen haben.

    Gruß,

    Simon2.



  • ok,

    dann funktioniert dynamic_cast bei A* -> B* und bei A* -> C* gibt es eine segmention fault.

    Static_cast laeuft wie gehabt ...

    Erklaerungen?



  • Frager schrieb:

    ok,

    dann funktioniert dynamic_cast bei A* -> B* und bei A* -> C* gibt es eine segmention fault.

    Static_cast laeuft wie gehabt ...

    Erklaerungen?

    Ich glaube, Du bekommst da was "kerndurcheinander":
    Wenn eine Klasse B von A erbt, dann will man damit üblicherweise ausdrücken, dass jedes B ein A ist. D.h. Du kannst alles, was Du mit einem A machen kannst, auch mit einem B machen (hier z.B. Call() aufrufen).
    Das weiß der Compiler auch und Du brauchst es ihm nicht mehr über einen (dynamic-)cast mitzuteilen.

    Mit einem "cast" sagst Du dem Compiler "Ich weiß es besser als Du" .... aber das sollte natürlich auch stimmen.
    In Deinem Fall (gibt es wirklich einen "segmentation fault" ? Oder nicht doch eher eine unhandled exception ?) hast Du ihm bestimmt ein X für ein U vorgemacht .. so in der Art:

    A* a = new B(); // das Objekt, auf das a zeigt, ist ein B
    C* c = dynamic_cast<C*>(a); // FEHLER !! Das Objekt ist und bleibt ein B 
          // und egal, was Du davorschreibst: Es wird kein C daraus !
          // Da Du aber dem Compier gesagt hast, dass Du es besser weißt, kann er 
          // nichts dagegen machen.
    

    Vergleich das mit sprechenderen Klassennamen:

    class Lebewesen {}
    class Tier : public Lebewesen { virtual void fressen();}
    class Elefant : public Tier { void ruesselBlasen();}
    
    Lebewesen* tier = new Tier();
    Elefant* e = dynamic_cast<Elefant*>(tier);
    e->ruesselBlasen(); // Autsch ! Ein Elefant ist zwar ein Tier, aber nicht jedes Tier ist ein Elefant
    

    Gruß,

    Simon2.



  • Hallo Frager

    Mit static_cast zwingst Du den Compiler die Umwandlung vorzunehmen. Der Compiler kann im allgemeinen nicht wissen, ob hinter dem Zeiger auf A ein Objekt vom Typ A,B oder C liegt. Du sagst, Du weißt es und der Compiler vertraut Dir. Das Du danach mit dem falsch gecasteten Zeiger noch weiterarbeiten kannst, ist purer Zufall. (Auch undefiniertes Verhalten gennant). Du könntest genauso einen Seg_Fault bekommen, oder es passieren noch schlimmere Dinge.
    Beim Dynamic_cast wird erst zur Laufzeit festgestellt, ob die dynamische Umwandung
    möglich ist. (Dazu brauchst Du mindesten eine virtuelle Methode).
    Wenn die Umwandlung nicht möglich ist bekommst Du einen NULL-Pointer zurück. Und den darfst Du natürlich nicht dereferenzieren. --> SegFault.

    DJohn


Log in to reply