dynamic_cast-Operator

Konvertiert den Operanden expression in ein Objekt vom Typ type-id.

Syntax

dynamic_cast < type-id > ( expression )

Hinweise

type-id muss ein Zeiger oder ein Verweis auf einen zuvor definierten Klassentyp oder ein "Zeiger auf void" sein. Der expression-Typ muss ein Zeiger sein, wenn type-id ein Zeiger ist, oder ein lvalue, wenn type-id ein Verweis ist.

Unter static_cast finden Sie eine Erläuterung des Unterschieds zwischen statischen und dynamischen Umwandlungen und wann sie für die jeweilige Verwendung geeignet ist.

Es gibt zwei wichtige Änderungen im Verhalten von dynamic_cast verwaltetem Code:

  • dynamic_cast auf einen Zeiger auf den zugrunde liegenden Typ einer Boxenume schlägt zur Laufzeit fehl und gibt 0 anstelle des konvertierten Zeigers zurück.

  • dynamic_cast löst keine Ausnahme mehr aus, wenn type-id es sich um einen Innenzeiger auf einen Werttyp handelt. Stattdessen schlägt die Umwandlung zur Laufzeit fehl. Die Umwandlung gibt den 0 Zeigerwert zurück, anstatt ihn zu auslösen.

Wenn type-id es sich um einen Zeiger auf eine eindeutig zugängliche direkte oder indirekte Basisklasse handelt expression, ist ein Zeiger auf das eindeutige Unterobjekt des Typs type-id das Ergebnis. Beispiel:

// dynamic_cast_1.cpp
// compile with: /c
class B { };
class C : public B { };
class D : public C { };

void f(D* pd) {
   C* pc = dynamic_cast<C*>(pd);   // ok: C is a direct base class
                                   // pc points to C subobject of pd
   B* pb = dynamic_cast<B*>(pd);   // ok: B is an indirect base class
                                   // pb points to B subobject of pd
}

Dieser Typ der Konvertierung wird als "Upcast" bezeichnet, da er einen Zeiger auf eine Klassenhierarchie verschiebt, von einer abgeleiteten Klasse zu einer Klasse, von der sie abgeleitet wird. Eine Typumwandlung nach oben ist eine implizite Konvertierung.

Wenn type-id "void*" ist, wird eine Laufzeitüberprüfung durchgeführt, um den tatsächlichen Typ von expression zu bestimmen. Das Ergebnis ist ein Zeiger auf das vollständige Objekt, auf das durch expression gezeigt wird. Beispiel:

// dynamic_cast_2.cpp
// compile with: /c /GR
class A {virtual void f();};
class B {virtual void f();};

void f() {
   A* pa = new A;
   B* pb = new B;
   void* pv = dynamic_cast<void*>(pa);
   // pv now points to an object of type A

   pv = dynamic_cast<void*>(pb);
   // pv now points to an object of type B
}

Wenn type-id dies nicht void*der Fehler ist, wird eine Laufzeitüberprüfung durchgeführt, um festzustellen, ob das objekt expression , auf das verwiesen wird, in den Typ konvertiert werden kann, auf type-idden verwiesen wird.

Wenn der Typ von expression eine Basisklasse vom Typ type-id ist, wird eine Laufzeitüberprüfung durchgeführt, um festzustellen, ob expression tatsächlich auf ein vollständiges Objekt vom Typ type-id zeigt. Wenn dies der Fall ist, ist das Ergebnis ein Zeiger auf ein vollständiges Objekt vom Typ type-id. Beispiel:

// dynamic_cast_3.cpp
// compile with: /c /GR
class B {virtual void f();};
class D : public B {virtual void f();};

void f() {
   B* pb = new D;   // unclear but ok
   B* pb2 = new B;

   D* pd = dynamic_cast<D*>(pb);   // ok: pb actually points to a D
   D* pd2 = dynamic_cast<D*>(pb2);   // pb2 points to a B not a D
}

Diese Art der Konvertierung wird als "Typumwandlung" bezeichnet, da ein Zeiger in einer Klassenhierarchie nach unten verschoben wird, von einer angegebenen Klasse zu einer von dieser abgeleiteten Klasse.

Im Fall der Mehrfachvererbung werden Möglichkeiten für Mehrdeutigkeit eingeführt. Betrachten Sie die Klassenhierarchie, die in der folgenden Abbildung gezeigt wird.

Führt für CLR-Typen dynamic_cast entweder zu einer No-Op, wenn die Konvertierung implizit ausgeführt werden kann, oder eine MSIL-Anweisung isinst , die eine dynamische Überprüfung durchführt und zurückgibt nullptr , wenn die Konvertierung fehlschlägt.

Im folgenden Beispiel wird dynamic_cast ermittelt, ob eine Klasse eine Instanz eines bestimmten Typs ist:

// dynamic_cast_clr.cpp
// compile with: /clr
using namespace System;

void PrintObjectType( Object^o ) {
   if( dynamic_cast<String^>(o) )
      Console::WriteLine("Object is a String");
   else if( dynamic_cast<int^>(o) )
      Console::WriteLine("Object is an int");
}

int main() {
   Object^o1 = "hello";
   Object^o2 = 10;

   PrintObjectType(o1);
   PrintObjectType(o2);
}

Diagram that shows multiple inheritance.

Das Diagramm zeigt eine Klassenhierarchie mit A als Basisklasse von B, die eine Basisklasse von D ist. A ist auch eine Basisklasse für C, bei der es sich um eine Basisklasse für D handelt. Klasse D erbt von B und C.

Ein Zeiger auf ein Objekt vom Typ D kann sicher in B oder C umgewandelt werden. Wenn jedoch D umgewandelt wird, um auf ein A-Objekt zu zeigen, welche Instanz von A würde daraus resultieren? Dies führt zu einem Fehler aufgrund mehrdeutiger Umwandlung. Um dieses Problem zu umgehen, können Sie zwei eindeutige Umwandlungen ausführen. Beispiel:

// dynamic_cast_4.cpp
// compile with: /c /GR
class A {virtual void f();};
class B : public A {virtual void f();};
class C : public A {virtual void f();};
class D : public B, public C {virtual void f();};

void f() {
   D* pd = new D;
   A* pa = dynamic_cast<A*>(pd);   // C4540, ambiguous cast fails at runtime
   B* pb = dynamic_cast<B*>(pd);   // first cast to B
   A* pa2 = dynamic_cast<A*>(pb);   // ok: unambiguous
}

Alle anderen Mehrdeutigkeiten können eingeführt werden, wenn Sie virtuelle Basisklassen verwenden. Betrachten Sie die Klassenhierarchie, die in der folgenden Abbildung gezeigt wird.

Diagram of a class hierarchy that shows virtual base classes.

Das Diagramm zeigt die klassen A, B, C, D und E, die wie folgt angeordnet sind: Klasse A ist eine Basisklasse von B. Klassen C und E, die jeweils von B. Klasse E abgeleitet sind, erbt ebenfalls von D, die von Klasse B erbt, die von Klasse A erbt.

Klassenhierarchie mit virtuellen Basisklassen

In dieser Hierarchie ist A eine virtuelle Basisklasse. Aufgrund einer Instanz der Klasse E und eines Zeigers auf das A Unterobjekt schlägt ein dynamic_cast Zeiger B aufgrund von Mehrdeutigkeit fehl. Sie müssen in das vollständige E-Objekt zurückumwandeln und anschließend auf eine eindeutige Weise entlang die Hierarchie arbeiten, um zu dem richtigen B-Objekt zu gelangen.

Betrachten Sie die Klassenhierarchie, die in der folgenden Abbildung gezeigt wird.

Diagram of a class hierarchy that shows duplicate base classes.

Das Diagramm zeigt die klassen A, B, C, D und E, die wie folgt angeordnet sind: Klasse B wird von Klasse A abgeleitet von Klasse A. Klasse C abgeleitet von Klasse A. Klasse D abgeleitet von Klasse B. Klasse E von Klasse C abgeleitet, die von Klasse A abgeleitet wird. In diesem Fall ist die doppelte Basisklasse Klasse A, die direkt oder indirekt von allen anderen Klassen geerbt wird. Klasse A wird direkt von Klassen B und C und indirekt von Klasse D über Klasse B und indirekt von Klasse E über Klasse C und indirekt in Klasse D über Klasse B geerbt.

Klassenhierarchie mit doppelten Basisklassen

Bei einem Objekt vom Typ E und einem Zeiger auf das D-Unterobjekt können drei Konvertierungen vorgenommen werden, um vom D-Unterobjekt auf das A-Unterobjekt zu navigieren, das sich am weitesten links befindet. Sie können eine dynamic_cast Konvertierung vom D Zeiger in einen E Zeiger, dann eine Konvertierung ( dynamic_cast oder eine implizite Konvertierung) von E zu B, und schließlich eine implizite Konvertierung von B zu A. Beispiel:

// dynamic_cast_5.cpp
// compile with: /c /GR
class A {virtual void f();};
class B : public A {virtual void f();};
class C : public A { };
class D {virtual void f();};
class E : public B, public C, public D {virtual void f();};

void f(D* pd) {
   E* pe = dynamic_cast<E*>(pd);
   B* pb = pe;   // upcast, implicit conversion
   A* pa = pb;   // upcast, implicit conversion
}

Der dynamic_cast Operator kann auch zum Ausführen eines "Kreuzgusses" verwendet werden. Mit derselben Klassenhierarchie können Sie beispielsweise einen Zeiger vom B Unterobjekt in das D Unterobjekt umwandeln, solange das vollständige Objekt vom Typ Eist.

Bei Kreuzumwandlungen ist es möglich, die Konvertierung von einem Zeiger zu einem Zeiger D zu einem Zeiger auf das linksste A Unterobjekt in nur zwei Schritten auszuführen. Sie können eine übergreifende Umwandlung von D in B und dann eine implizite Konvertierung von B in A ausführen. Beispiel:

// dynamic_cast_6.cpp
// compile with: /c /GR
class A {virtual void f();};
class B : public A {virtual void f();};
class C : public A { };
class D {virtual void f();};
class E : public B, public C, public D {virtual void f();};

void f(D* pd) {
   B* pb = dynamic_cast<B*>(pd);   // cross cast
   A* pa = pb;   // upcast, implicit conversion
}

Ein Nullzeigerwert wird in den Nullzeigerwert des Zieltyps umgerechnet dynamic_cast.

Wenn Sie , dynamic_cast < type-id > ( expression )wenn expression sie nicht sicher in typ type-idkonvertiert werden können, führt die Laufzeitüberprüfung dazu, dass die Umwandlung fehlschlägt. Beispiel:

// dynamic_cast_7.cpp
// compile with: /c /GR
class A {virtual void f();};
class B {virtual void f();};

void f() {
   A* pa = new A;
   B* pb = dynamic_cast<B*>(pa);   // fails at runtime, not safe;
   // B not derived from A
}

Der Wert eines Fehlers bei einer Umwandlung in einen Zeigertyp ist der NULL-Zeiger. Ein Fehler beim Umwandeln des Verweistyps löst eine bad_cast Ausnahme aus. Wenn expression sie nicht auf ein gültiges Objekt verweist oder darauf verweist, wird eine __non_rtti_object Ausnahme ausgelöst.

Eine Erläuterung der __non_rtti_object Ausnahme finden Sie unter typeid.

Beispiel

Im folgenden Beispiel wird der Basisklassenzeiger (Struktur A) auf ein Objekt (Struktur C) erstellt. Dies sowie die Tatsache, dass virtuelle Funktionen vorhanden sind, ermöglicht Laufzeitpolymorphie.

Im Beispiel wird auch eine nichtvirtuale Funktion in der Hierarchie aufgerufen.

// dynamic_cast_8.cpp
// compile with: /GR /EHsc
#include <stdio.h>
#include <iostream>

struct A {
    virtual void test() {
        printf_s("in A\n");
   }
};

struct B : A {
    virtual void test() {
        printf_s("in B\n");
    }

    void test2() {
        printf_s("test2 in B\n");
    }
};

struct C : B {
    virtual void test() {
        printf_s("in C\n");
    }

    void test2() {
        printf_s("test2 in C\n");
    }
};

void Globaltest(A& a) {
    try {
        C &c = dynamic_cast<C&>(a);
        printf_s("in GlobalTest\n");
    }
    catch(std::bad_cast) {
        printf_s("Can't cast to C\n");
    }
}

int main() {
    A *pa = new C;
    A *pa2 = new B;

    pa->test();

    B * pb = dynamic_cast<B *>(pa);
    if (pb)
        pb->test2();

    C * pc = dynamic_cast<C *>(pa2);
    if (pc)
        pc->test2();

    C ConStack;
    Globaltest(ConStack);

   // fails because B knows nothing about C
    B BonStack;
    Globaltest(BonStack);
}
in C
test2 in B
in GlobalTest
Can't cast to C

Siehe auch

Umwandlungsoperatoren
Schlüsselwörter