Udostępnij za pośrednictwem


Operator dynamic_cast

Konwertuje operand expression na obiekt typu type-id.

Składnia

dynamic_cast < type-id > ( expression )

Uwagi

Musi type-id być wskaźnikiem lub odwołaniem do wcześniej zdefiniowanego typu klasy lub "wskaźnik do pustki". Typ expression musi być wskaźnikiem, jeśli type-id jest wskaźnikiem lub wartością l, jeśli type-id jest odwołaniem.

Zobacz static_cast , aby zapoznać się z wyjaśnieniem różnicy między konwersjami rzutowania statycznego i dynamicznego, a kiedy jest to odpowiednie do użycia.

Istnieją dwie istotne zmiany w zachowaniu kodu zarządzanego dynamic_cast :

  • dynamic_cast wskaźnik do bazowego typu wyliczenia pola zakończy się niepowodzeniem w czasie wykonywania, zwracając wartość 0 zamiast przekonwertowanego wskaźnika.

  • dynamic_cast Nie będzie już zgłaszać wyjątku, gdy type-id jest wewnętrznym wskaźnikiem do typu wartości; zamiast tego rzutowanie kończy się niepowodzeniem w czasie wykonywania. Rzutowanie zwraca wartość wskaźnika 0 zamiast rzutowania.

Jeśli type-id jest wskaźnikiem do jednoznacznej dostępnej bezpośredniej lub pośredniej klasy bazowej expressionklasy , wskaźnik do unikatowego podobiektu typu type-id jest wynikiem. Przykład:

// 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
}

Ten typ konwersji jest nazywany "upcast", ponieważ przenosi wskaźnik w górę hierarchii klas z klasy pochodnej do klasy, z niej pochodzi. Emisja upcast jest niejawną konwersją.

Jeśli type-id jest void*, sprawdzanie czasu wykonywania jest wykonywane w celu określenia rzeczywistego expressiontypu . Wynik jest wskaźnikiem do kompletnego obiektu wskazywanego przez expression. Przykład:

// 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
}

Jeśli type-id nie void*jest , jest wykonywane sprawdzanie czasu wykonywania, aby sprawdzić, czy obiekt wskazywany przez expression można przekonwertować na typ wskazywany przez type-id.

Jeśli typ expression jest klasą bazową typu type-id, sprawdzanie czasu wykonywania jest wykonywane w celu sprawdzenia, czy expression rzeczywiście wskazuje pełny obiekt typu type-id. Jeśli jest to prawda, wynik jest wskaźnikiem do kompletnego obiektu typu type-id. Przykład:

// 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
}

Ten typ konwersji jest nazywany "downcast", ponieważ przenosi wskaźnik w dół hierarchii klas z danej klasy do klasy pochodnej z niej.

W przypadkach wielokrotnego dziedziczenia wprowadzane są możliwości niejednoznaczności. Rozważ hierarchię klas pokazaną na poniższym rysunku.

W przypadku typów CLR wynikiem jest brak operacji, dynamic_cast jeśli konwersję można wykonać niejawnie lub instrukcję MSIL isinst , która wykonuje sprawdzanie dynamiczne i zwraca nullptr , jeśli konwersja nie powiedzie się.

W poniższym przykładzie użyto dynamic_cast metody do określenia, czy klasa jest wystąpieniem określonego typu:

// 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.

Diagram przedstawia hierarchię klas z klasą A jako klasą bazową B, która jest klasą bazową D. A jest również klasą bazową dla języka C, która jest klasą bazową dla klasy D. Klasa D dziedziczy zarówno z klasy B, jak i C.

Wskaźnik do obiektu typu D można bezpiecznie rzutować na B lub C. Jeśli jednak D rzutuje się, aby wskazać A obiekt, które wystąpienie A obiektu spowodowałoby? Spowodowałoby to niejednoznaczny błąd rzutu. Aby obejść ten problem, można wykonać dwie jednoznaczne rzutowania. Przykład:

// 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
}

Podczas korzystania z wirtualnych klas bazowych można wprowadzić dalsze niejednoznaczności. Rozważ hierarchię klas pokazaną na poniższym rysunku.

Diagram of a class hierarchy that shows virtual base classes.

Na diagramie przedstawiono klasy A, B, C, D i E uporządkowane w następujący sposób: Klasa A jest klasą bazową B. Klasy C i E pochodzą z klasy B. Klasa E dziedziczy również z klasy D, która dziedziczy z klasy B, która dziedziczy po klasie A.

Hierarchia klas pokazująca wirtualne klasy bazowe

W tej hierarchii A jest wirtualną klasą bazową. Biorąc pod uwagę wystąpienie klasy E i wskaźnik do A podobiektu, dynamic_cast wskaźnik do B wskaźnika kończy się niepowodzeniem z powodu niejednoznaczności. Najpierw należy rzutować z powrotem do kompletnego E obiektu, a następnie wykonać kopię zapasową hierarchii w sposób jednoznaczny, aby uzyskać prawidłowy B obiekt.

Rozważ hierarchię klas pokazaną na poniższym rysunku.

Diagram of a class hierarchy that shows duplicate base classes.

Diagram przedstawia klasy A, B, C, D i E uporządkowane w następujący sposób: Klasa B pochodzi z klasy A. Klasa C pochodzi z klasy A. Klasa D pochodzi z klasy B. Klasa E pochodzi z klasy B. Klasa E pochodzi z klasy C, która pochodzi z klasy A. W takim przypadku zduplikowana klasa bazowa to klasa A, która jest bezpośrednio lub pośrednio dziedziczona przez wszystkie pozostałe klasy. Klasa A jest dziedziczona bezpośrednio przez klasy B i C, a pośrednio przez klasę D za pośrednictwem klasy B, a pośrednio przez klasę E za pośrednictwem klasy C i pośrednio w klasie D za pośrednictwem klasy B.

Hierarchia klas pokazująca zduplikowane klasy bazowe

Biorąc pod uwagę obiekt typu E i wskaźnik do D podobiektu, aby przejść z D podobiektu do lewego A obiektu podrzędnego, można wykonać trzy konwersje. Można wykonać konwersję dynamic_cast ze D wskaźnika do E wskaźnika, a następnie konwersję (dynamic_castlub niejawną konwersję) z do B, a na koniec niejawną konwersję z EB do A. Przykład:

// 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
}

Operator dynamic_cast może również służyć do wykonywania "rzutowania krzyżowego". Korzystając z tej samej hierarchii klas, można rzutować wskaźnik, na przykład z B podobiektu do D podobiektu, o ile kompletny obiekt jest typu E.

Biorąc pod uwagę rzutowania krzyżowe, można wykonać konwersję ze wskaźnika na D wskaźnik do wskaźnika do wskaźnika do lewego A obiektu podrzędnego w zaledwie dwóch krokach. Można wykonać rzutowanie krzyżowe z D do B, a następnie niejawną konwersję z B do A. Przykład:

// 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
}

Wartość wskaźnika null jest konwertowana na wartość wskaźnika null typu docelowego przez dynamic_cast.

Jeśli używasz metody dynamic_cast < type-id > ( expression ), jeśli expression nie można bezpiecznie przekonwertować na typ type-id, sprawdzanie czasu wykonywania powoduje niepowodzenie rzutowania. Przykład:

// 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
}

Wartość rzutowanego typu wskaźnika nie powiodła się jest wskaźnikiem o wartości null. Rzutowanie typu odwołania nie powiodło się, zgłasza wyjątek bad_cast. Jeśli expression nie wskazuje lub odwołuje się do prawidłowego obiektu, __non_rtti_object zgłaszany jest wyjątek.

Zobacz typeid , aby uzyskać wyjaśnienie wyjątku __non_rtti_object .

Przykład

Poniższy przykład tworzy wskaźnik klasy bazowej (struktury A) do obiektu (struct C). To, plus fakt, że istnieją funkcje wirtualne, umożliwia polimorfizm środowiska uruchomieniowego.

Przykład wywołuje również funkcję niewirtualną w hierarchii.

// 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

Zobacz też

Operatory rzutowania
Słowa kluczowe