dynamic_cast – operátor
Převede operand expression na objekt typu type-id.
dynamic_cast < type-id > ( expression )
Poznámky
type-id musí být ukazatel nebo odkaz na dříve definovaný typ třídy nebo "ukazatel na void".Typ expression musí být ukazatel, pokud je type-id ukazatel, nebo hodnota l, pokud je type-id odkaz.
Viz static_cast pro vysvětlení rozdílu mezi statickými a dynamickými převody přetypování a kdy je vhodné použít každého z nich.
Ve spravovaném kódu existují v chování dynamic_cast dvě rozbíjející změny:
dynamic_cast na ukazatele na nadřazený typ zabaleného výčtu selže za běhu, vrácením 0 místo převedeného ukazatele.
dynamic_cast již nevyvolá výjimku, pokud je type-id vnitřním ukazatelem na typ hodnoty s přetypováním, selhávajícím v době běhu. Přetypování nyní místo vyvolání vrátí hodnotu ukazatele 0.
Pokud je type-id ukazatel na jednoznačně přístupnou přímou nebo nepřímou základní třídu z expression, je výsledkem ukazatel na jedinečný podřízený objekt typu type-id.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 druh převodu se nazývá "přetypování nahoru", protože přesunuje ukazatel v hierarchii tříd z odvozené třídy do třídy, ze které je odvozen.Přetypování nahoru je implicitní převod.
Pokud je type-id void*, provede se kontrola v době běhu pro určení skutečného typu expression.Výsledkem je ukazatel na celý objekt odkazovaný skrze expression.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 není type-id void*, je provedena kontrola v době běhu pro zobrazení, zda může být objekt odkazovaný skrze expression převeden na typ odkazovaný skrze type-id.
Pokud je typ expression základní třída typu type-id, je provedena kontrola v době běhu pro zjištění, zda opravdu expression odkazuje na celý objekt typu type-id.Je-li tomu tak, je výsledkem ukazatel na celý 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á přetypování dolů, protože dochází k přesunu ukazatele hierarchie tříd od dané třídy do třídy, která je z něj odvozená.
V případě vícenásobné dědičnosti jsou zavedeny možnosti nejednoznačnosti.Zvažte hierarchii tříd zobrazenou na následujícím obrázku.
Pro typy CLR vede dynamic_cast buď k operaci no-op, v případě, že převod může být proveden implicitně, nebo k instrukci MSIL isinst, která provede dynamickou kontrolu a vrátí nullptr, pokud se převod nezdaří.
Následující příklad používá k určení, zda je třída instancí určitého typu, dynamic_cast:
// 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);
}
Hierarchie třid znázorňující vícenásobnou dědičnost
Ukazatel na objekt typu D lze bezpečně přetypovat na B nebo na C.Avšak pokud je D přetypován na odkazování se na objekt A, která instance A bude výsledkem?Příklad může vést k chybě nejednoznačného přetypování.Tento problém lze vyřešit použitím dvou nejednoznačných přetypování.Příklad:
// 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
}
Při použití virtuálních základních tříd by mohly být zavedeny další nejednoznačnosti.Zvažte hierarchii tříd zobrazenou na následujícím obrázku.
Hierarchie tříd znázorňující virtuální základní třídy.
V této hierarchii je A virtuální základní třída.Viz Virtuální základní třídy pro definici virtuální základní třídy.Při předání instance třídy E a ukazatele na podřízený objekt A, dynamic_cast na ukazatel B selže z důvodu nejednoznačnosti.Nejdříve je zapotřebí provést přetypování zpět na celý objekt E a následně se jednoznačným způsobem dostat zpět na vrchol hierarchie pro dosažení správného objektu B.
Zvažte hierarchii tříd zobrazenou na následujícím obrázku.
Hierarchie třídy znázorňující duplicitní základní třídy
Předáním objektu typu E a ukazatele na podřízený objekt D pro přechod z podřízeného objektu D na objekt nejvíce vlevo A, se mohou provést tři převody..Je možné provést převod dynamic_cast z ukazatele D na ukazatel E, následně převod (buď dynamic_cast nebo implicitní převod) z E na Ba nakonec implicitní převod z ukazatele B na 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 vykonání "křížového přetypování". Pomocí stejné hierarchie tříd je možné přetypovat ukazatel, například z podřízeného objektu B na podřízený objekt D, pokud je celý objekt typu E.
Bereme-li v úvahu křížové přetypování, je možné provést převod z ukazatele D na ukazatel nejvíce vlevo A použitím pouze dvou kroků.Křížový odkaz lze přetypovat z D na B a následně implicitním přetypováním 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 nulového ukazatele je převedena na hodnotu nulového ukazatele cílového typu použitím dynamic_cast.
Použitím dynamic_cast < type-id > ( expression ) a pokud expression nelze bezpečně převést na typ type-id, dojde z důvody kontroly v době běhu k selhání převodu.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 82f1eehz(v=vs.120).md. Pokud expression necílí nebo neodkazuje na platný objekt, je vyvolána výjimka __non_rtti_object.
Viz typeid pro více informací o výjimce __non_rtti_object.
Příklad
Následující příklad vytvoří ukazatele základní třídy (struct A) na objekt (struct C). Toto a virtuální funkce umožňují polymorfismus modulu runtime.
Příklad také v hierarchii volá nevirtuální funkci.
// 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);
}