Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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. 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 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
. 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
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
. 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 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. Na 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
. 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ż 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
. 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
}
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. 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ść 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