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, gdytype-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 expression
klasy , 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 expression
typu . 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 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.
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 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_cast
lub niejawną konwersję) z do B
, a na koniec niejawną konwersję z E
B
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ż
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla