dynamic_cast

Wir haben ja schon einige casts kennengelernt, jetzt lernen wir auch noch dynamic_cast dazu. Den dynamic_cast braucht man (oh Wunder) wenn man mit Klassen arbeitet. Es wird verwendet um zur Laufzeit polymorphe Typen zu konvertieren. Das bedeutet auf deutsch nichts anderes als Downcasts in einer Klassenhierachie zu ermöglichen.

Ein Upcast wird implizit ausgeführt: ein Derived wird zu einem Base.

    class Base
    {
    };

    class Derived : public Base
    {
    };

    void foo(Base& b)
    {}

    void foo2(Derived& d)
    {}

    int main()
    {
      Base b;
      Derived d;

      foo(b); //geht!
      foo(d); //geht! Derived kann in Base konvertiert werden (upcast)

      foo2(d); //geht!
      foo2(b); //geht nicht! Base kann nicht in Derived konvertiert werden (downcast)
    }
    

Ein Downcast ist problematischer: denn wenn wir ein Base in ein Derived konvertieren, dann können wir nicht zur Compilezeit feststellen ob dieses Base wirklich ein Derived ist:

    class Base
    {
    public:
      virtual ~Base() {}
    };

    class Derived : public Base
    {
    };

    class Derived2 : public Base
    {
    };

    void foo(Derived* d)
    {}

    void foo(Base* b)
    {
      foo(dynamic_cast<Derived*>(b)); //cast schlägt fehl!
    }



    int main()
    {
      Derived2* d=new Derived2();
      foo(d);
      delete d;
    }
    

dynamic_cast funktioniert logischerweise nur mit Zeigern und Referenzen. Wenn die Konvertierung nicht durchgeführt wurde, wie zB in unserem Beispiel (weil Base ein Derived2 war und kein Derived) wird bei Zeigern 0 zurück gegeben, bei Referenzen fliegt eine Exception vom Typ bad_cast. Allerdings lernen wir erst später was Exception sind, deshalb bleiben wir vorerst bei Zeigern.

Aufgrund der Laufzeitauswertung kann man dynamic_cast auch verwenden um den echten Typ eines Objektes festzustellen.

    class Base
    {
    public:
      virtual ~Base() {}
    };

    class Base2 : public Base
    {
    public:
      virtual ~Base2() {}
    };

    class Derived : public Base
    {
    };

    class Derived2 : public Base
    {
    };

    class Derived3 : public Base2
    {
    };

    class Derived4 : public Base2
    {
    };

    void select(Base* p)
    {
      if(dynamic_cast<Derived*>(p))
      {
        cout<<"Derived\n";
      }
      else if(dynamic_cast<Derived2*>(p))
      {
        cout<<"Derived2\n";
      }
      else if(dynamic_cast<Base2*>(p))
      {
        cout<<"Base2 -> ";
        if(dynamic_cast<Derived3*>(p))
        {
          cout<<"Derived3\n";
        }
        else if(dynamic_cast<Derived4*>(p))
        {
          cout<<"Derived4\n";
        }
        else
        {
          cout<<"unbekannt oder exakt ein Base2\n";
        }
      }
      else
      {
        cout<<"unbekannt\n";
      }
    }

    int main()
    {
      Base* d2=new Derived2();
      Base* d4=new Derived4();

      select(d2);
      select(d4);

      delete d4;
      delete d2;
    }
    

dynamic_cast verwendet ein C++ Feature genannt RTTI (Runtime Type Information). Bei manchen Compilern muss man dies extra aktivieren (siehe Compilerdokumentation). RTTI ermöglicht es, den Typ eines Objektes zur Laufzeit festzustellen - es funktioniert ähnlich wie virtuelle Methoden.

top