Udostępnij za pośrednictwem


Operator dynamic_cast

Konwertuje argument operacji expression do obiektu typu type-id.

dynamic_cast < type-id > ( expression )

Uwagi

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

Zobacz statyczne rzutowanie, aby znaleźć wyjaśnienie różnicy między statycznymi i dynamicznymi konwersjami rzutowania, a także kiedy właściwe jest ich wykorzystanie.

Istnieją dwie najświeższe zmiany w zachowaniu dynamic_cast w kodzie zarządzanym:

  • dynamic_cast wskaźnika na podstawowy tabelaryczny typ wyliczeniowy zakończy się niepowodzeniem w czasie wykonywania, zwracając 0 zamiast przekonwertowanego wskaźnika.

  • dynamic_cast nie spowoduje zgłoszenia wyjątku, podczas gdy type-id jest wewnętrznym wskaźnikiem na typ wartości z rzutowaniem kończącym się niepowodzeniem w czasie wykonywania. Rzutowanie zwraca wartość 0 wskaźnika zamiast zgłaszania wyjątku.

Jeśli type-id jest wskaźnikiem do jednoznacznej, dostępnej bezpośrednio lub pośrednio klasy podstawowej expression, wynikiem jest wskaźnik do unikatowego podobiektu typu type-id.Na 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 nazywa się "przypisaniem elementu nadrzędnego", ponieważ przesuwa wskaźnik w górę hierarchii klas, od klasy pochodnej do klasy, z której się wywodzi.Przypisanie elementu nadrzędnego jest niejawną konwersją.

Jeśli type-id to void*, wykonywane jest sprawdzenie w czasie wykonania w celu określenia rzeczywistego typu expression.Wynik jest wskaźnikiem do kompletnego obiektu wskazywanego przez expression.Na 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 to nie void*, wykonywane jest sprawdzenie w czasie wykonania, aby zobaczyć czy obiekt wskazywany przez expression może zostać skonwertowany na typ wskazywany przez type-id.

Jeśli typ expression jest klasą podstawową typu type-id, wykonywane jest sprawdzenie w czasie wykonania, aby zobaczyć czy expression faktycznie wskazuje na kompletny obiekt typu type-id.Jeśli to prawda, wynikiem jest wskaźnik do kompletnego obiektu typu type-id.Na 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 nazywa się "przypisaniem elementu podrzędnego", ponieważ przesuwa wskaźnik w dół hierarchii klas z danej klasy do klasy z niej pochodzącej.

W przypadku wielokrotnego dziedziczenia, pojawia się możliwość wystąpienia niejednoznaczności.Należy wziąć pod uwagę hierarchię klas pokazaną na poniższym rysunku.

Dla typów CLR dynamic_cast skutkuje brakiem operacji, jeśli konwersja może zostać wykonana w sposób niejawny lub wykonaniem instrukcji MSIL isinst, która wykonuje dynamiczne sprawdzenie i zwraca nullptr jeśli konwersja się nie powiodła.

Następujące przykłady używają dynamic_cast 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);
}

Hierarchia klas pokazująca wielokrotne dziedziczenie

Hierarchia klas, zawierającego wiele dziedziczenia

Wskaźnik do obiektu typu D może być bezpiecznie rzutowany na B lub C.Jednakże jeśli D jest rzutowane na wskaźnik do obiektu A, które wystąpienie A będzie wynikiem?Spowodowałoby to błąd rzutowania niejednoznacznego.Aby obejść ten problem, można wykonać dwa jednoznaczne rzutowania.Na przykład:

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

void f() {
   D* pd = new D;
   B* pb = dynamic_cast<B*>(pd);   // first cast to B
   A* pa2 = dynamic_cast<A*>(pb);   // ok: unambiguous
}

Korzystanie z wirtualnych klas podstawowych, może spowodować dalsze niejednoznaczności.Należy wziąć pod uwagę hierarchię klas pokazaną na poniższym rysunku.

Hierarchia klas pokazująca wirtualne klasy podstawowe

Hierarchia klas, pokazujący wirtualnego bazowe klasy

W tej hierarchii A jest wirtualną klasą podstawową.Zobacz Wirtualne klasy podstawowe, aby zapoznać się z definicją wirtualnej klasy podstawowej.Biorąc pod uwagę wystąpienie klasy E i wskaźnik do podobiektu A, dynamic_cast wskaźnika do B zakończy się niepowodzeniem z powodu niejednoznaczności.Najpierw należy rzutować z powrotem do kompletnego obiektu E, a następnie przejść w górę hierarchii w sposób jednoznaczny, aby dotrzeć do poprawnego obiektu B.

Należy wziąć pod uwagę hierarchię klas pokazaną na poniższym rysunku.

Hierarchia klas pokazująca zduplikowane klasy podstawowe

Hierarchia klas, który pokazuje zduplikowane klasy podstawowej

Podany obiekt typu E i wskaźnik do podobiektu D, aby przejść z podobiektu D do pierwszego z lewej podobiektu A mogą wykonać trzy konwersje.Można wykonać konwersję dynamic_cast z wskaźnika D do wskaźnika E, a następnie konwersję (dynamic_cast albo niejawną konwersję) z E do B i w końcu niejawną konwersję z B do A.Na 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ż zostać użyty do przeprowadzenia "rzutowania krzyżowego". Korzystając z tej samej hierarchii klasy, można rzutować wskaźnik, na przykład z podobiektu B do podobiektu D, tak długo, jak długo kompletny obiekt jest typu E.

Biorąc pod uwagę rzutowania krzyżowe, możliwe jest wykonanie konwersji wskaźnika do D na wskaźnik do pierwszego z lewej podobiektu A w zaledwie dwóch krokach.Można wykonać rzutowanie krzyżowe z D do B, a następnie niejawną konwersję z B do A.Na 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
}

dynamic_cast konwertuje wartość pustego wskaźnika do wartości pustego wskaźnika typu docelowego.

Gdy użytkownik wykorzystuje dynamic_cast < type-id > ( expression ), jeśli expression nie może być bezpiecznie przekonwertowane na typ type-id, sprawdzenie w czasie wykonania spowoduje zakończenie rzutowania niepowodzeniem.Na 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ścią nieudanego rzutowania na typ wskaźnika jest pusty wskaźnik.Nieudane rzutowanie na typ referencyjny zgłasza wyjątek bad_cast. Jeśli expression nie wskazuje na prawidłowy obiekt, ani się do niego nie odwołuje, zgłaszany jest wyjątek __non_rtti_object.

Zobacz typeid, aby zapoznać się z wyjaśnieniem wyjątku __non_rtti_object.

Przykład

Poniższy przykład tworzy wskaźnik klasy podstawowej (struct A) do obiektu (struct C). To, plus fakt istnienia funkcji wirtualnych, umożliwia działanie polimorfizmu w trakcie wykonania.

Przykład wywołuje również niewirtualną funkcję 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);

   // will fail because B knows nothing about C
    B BonStack;
    Globaltest(BonStack);
}
  

Zobacz też

Informacje

Operatory rzutowania

Słowa kluczowe języka C++