Sdílet prostřednictvím


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, pokud type-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ý expressionodkazuje . 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-idna .

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 that shows multiple inheritance.

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 of a class hierarchy that shows virtual base classes.

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 of a class hierarchy that shows duplicate base classes.

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 Ba 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é

Operátory přetypování
Klíčová slova