dynamic_cast – operátor
Převede operand expression
na objekt typu type-id
.
Syntaxe
dynamic_cast < type-id > ( expression )
Poznámky
Musí type-id
to být ukazatel nebo odkaz na dříve definovaný typ třídy nebo ukazatel na void. Typ expression
musí být ukazatel, pokud type-id
je ukazatel, nebo l-hodnota, pokud type-id
je odkaz.
Vysvětlení rozdílu mezi statickými a dynamickými převody přetypování a jejich použití najdete v static_cast .
Chování spravovaného dynamic_cast
kódu má dvě zásadní změny:
dynamic_cast
na ukazatel na základní typ krabicového výčtu selže za běhu a vrátí hodnotu 0 místo převedeného ukazatele.dynamic_cast
už nebude hodit výjimku, pokudtype-id
je vnitřní ukazatel na typ hodnoty; místo toho se přetypování nezdaří za běhu. Přetypování místo vyvolání vrátí hodnotu ukazatele 0.
Pokud type-id
je ukazatel na jednoznačnou přístupnou přímou nebo nepřímou základní třídu expression
, pak ukazatel na jedinečný podobjekt typu type-id
je výsledkem. Příklad:
// 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
}
Tento typ převodu se nazývá "upcast", protože přesune ukazatel nahoru hierarchii třídy, od odvozené třídy do třídy, ze které je odvozena. Upcast je implicitní převod.
Pokud type-id
je void*, provede se kontrola za běhu, která určuje skutečný typ expression
. Výsledkem je ukazatel na úplný objekt, na který expression
odkazuje . Příklad:
// 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
}
Pokud type-id
není void*
, provede se kontrola za běhu, která zjistí, zda lze objekt odkazovaný expression
na typ odkazovaný type-id
na .
Pokud je typ expression
základní třídy typu type-id
, provede se kontrola doby běhu, která zjistí, zda expression
skutečně odkazuje na úplný objekt typu type-id
. Pokud je to pravda, výsledkem je ukazatel na úplný objekt typu type-id
. Příklad:
// 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
}
Tento typ převodu se nazývá "downcast", protože přesune ukazatel dolů hierarchii třídy z dané třídy do třídy odvozené z ní.
V případech vícenásobné dědičnosti jsou zavedeny možnosti nejednoznačnosti. Představte si hierarchii tříd zobrazenou na následujícím obrázku.
U typů CLR je výsledkem buď no-op, dynamic_cast
pokud je možné provést převod implicitně, nebo instrukci jazyka MSIL isinst
, která provádí dynamickou kontrolu a vrací nullptr
v případě selhání převodu.
Následující ukázka používá dynamic_cast
k určení, jestli je třída instancí určitého 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 znázorňuje hierarchii tříd s A jako základní třídou B, což je základní třída D. A je také základní třídou jazyka C, což je základní třída pro D. Třída D dědí z B i C.
Ukazatel na objekt typu D
lze bezpečně přetypovat na B
nebo C
. Pokud D
je však přetypován tak, aby odkazoval na A
objekt, která instance A
by výsledek? Výsledkem by byla nejednoznačná chyba přetypování. Chcete-li tento problém obejít, můžete provést dva jednoznačné přetypování. Příklad:
// 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
}
Další nejednoznačnosti lze zavést při použití virtuálních základních tříd. Představte si hierarchii tříd zobrazenou na následujícím obrázku.
Diagram znázorňuje třídy A, B, C, D a E uspořádané takto: Třída A je základní třídou B. Třídy C a E jsou odvozeny od třídy B. Třída E také dědí z třídy D, která dědí z třídy B, která dědí ze třídy A.
Hierarchie tříd, která zobrazuje virtuální základní třídy
V této hierarchii A
je virtuální základní třída. Vzhledem k instanci třídy E
a ukazateli A
na podobjekt dynamic_cast
se ukazatel na ukazatel B
nezdaří kvůli nejednoznačnosti. Nejprve je nutné přetypovat zpět na úplný E
objekt a pak provést zálohu hierarchie jednoznačným způsobem, aby se dostal ke správnému B
objektu.
Představte si hierarchii tříd zobrazenou na následujícím obrázku.
Diagram znázorňuje třídy A, B, C, D a E uspořádané takto: Třída B je odvozena od třídy A. Třída C je odvozena od třídy A. třída D je odvozena od třídy B. Třída E je odvozena od třídy C, která je odvozena od třídy A. V tomto případě je duplicitní základní třída třída A, která je přímo nebo nepřímo zděděna všemi ostatními třídami. Třída A je zděděna přímo třídami B a C a nepřímo třídami D prostřednictvím třídy B a nepřímo třídou E prostřednictvím třídy C a nepřímo ve třídě D prostřednictvím třídy B.
Hierarchie tříd, která zobrazuje duplicitní základní třídy
Vzhledem k objektu typu E
a ukazateli D
na podobjekt se dá přejít z podobjektu D
do levého A
největšího podobjektu, je možné provést tři převody. Můžete provést dynamic_cast
převod z D
ukazatele na E
ukazatel, pak převod ( dynamic_cast
nebo implicitní převod) z E
do B
a nakonec implicitní převod z B
do A
. Příklad:
// 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
}
Operátor dynamic_cast
lze také použít k provedení "křížového přetypování". Pomocí stejné hierarchie tříd je možné přetypovat ukazatel, například z B
podobjektu D
na podobjekt, pokud je úplný objekt typu E
.
Při zvažování křížového přetypování je možné provést převod z ukazatele na D
ukazatel na podřízený objekt nejvíce vlevo A
v pouhých dvou krocích. Můžete provést křížové přetypování z D
do B
, pak implicitní převod z B
na A
. Příklad:
// 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
}
Hodnota ukazatele null je převedena na hodnotu nulového ukazatele cílového typu podle dynamic_cast
.
Pokud použijete dynamic_cast < type-id > ( expression )
, pokud expression
nelze bezpečně převést na typ type-id
, kontrola za běhu způsobí selhání přetypování. Příklad:
// 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
}
Hodnota neúspěšného přetypování na typ ukazatele je nulový ukazatel. Neúspěšné přetypování na typ odkazu vyvolá výjimku bad_cast. Pokud expression
neodkazuje na platný objekt nebo na ho neodkazuje, __non_rtti_object
vyvolá se výjimka.
Vysvětlení výjimky najdete v typeidu__non_rtti_object
.
Příklad
Následující ukázka vytvoří ukazatel základní třídy (struktury A) na objekt (struktura C). To plus skutečnost, že existují virtuální funkce, umožňuje polymorfismus modulu runtime.
Ukázka také volá nevirtuální funkci v 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
Viz také
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro