dynamic_cast 演算子

オペランド expressiontype-id 型のオブジェクトに変換します。

構文

dynamic_cast < type-id > ( expression )

解説

type-id は、以前に定義されたクラス型への参照またはポインター、または "void 型へのポインター" である必要があります。 expression の型は、type-id がポインターの場合はポインター、type-id が参照の場合は左辺値である必要があります。

静的キャスト変換と動的キャスト変換の違いと、それぞれを使用するのが適切な場合については、static_castを参照してください。

マネージド コードの dynamic_cast のビヘイビアーには、次の 2 つの破壊的変更があります。

  • dynamic_cast ボックス化された列挙型の基になる型へのポインターへのポインターは実行時に失敗し、変換されたポインターではなく 0 が返されます。

  • dynamic_cast は、値型への内部ポインターである場合 type-id に例外をスローしなくなります。代わりに、キャストは実行時に失敗します。 キャストは、スローする代わりに 0 ポインター値を返します。

明確にアクセス可能な直接または間接的な基底クラスへのポインターの場合type-idは、型type-idexpression一意のサブオブジェクトへのポインターが結果になります。 次に例を示します。

// 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
}

この型の変換は、派生クラスから派生クラスへのポインターをクラス階層の上に移動するため、"アップキャスト" と呼ばれます。 アップキャストは暗黙の変換です。

type-id が void* の場合、expression の実際の型を確認するためにランタイム チェックが行われます。 結果は expression によってポイントされた完全なオブジェクトへのポインターです。 次に例を示します。

// 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
}

そうでないvoid*場合type-id、実行時チェックは、指すオブジェクトを指す型type-idexpression変換できるかどうかを確認します。

expression の型が type-id の型の基底クラスである場合は、ランタイム チェックが実行されて、expressiontype-id の型の完全なオブジェクトを実際に指し示しているかどうかが確認されます。 実際に指し示している場合、結果は type-id の型の完全なオブジェクトへのポインターになります。 次に例を示します。

// 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
}

このような変換は "ダウンキャスト" と呼ばれます。理由は、クラスの階層構造で特定のクラスから下位に (つまり、そのクラスから派生したクラスに) ポインターを移動するためです。

多重継承の場合、あいまいさが生じる可能性があります。 次の図に示すクラスの階層構造の場合を考えてみましょう。

CLR 型の場合、dynamic_cast を実行すると、暗黙的な変換が可能な場合、何も行われません。変換が失敗した場合は、動的検査を実行して nullptr を返す MSIL isinst 命令が実行されます。

次のサンプルでは、クラスが特定の型のインスタンスであるかどうかを判断するために 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);
}

Diagram that shows multiple inheritance.

この図は、D の基底クラスである B の基底クラスとして A を持つクラス階層を示しています。A は C の基底クラスでもあり、これは D の基底クラスです。クラス D は B と C の両方から継承します。

D 型のオブジェクトへのポインターは、B または C に安全にキャストできます。 しかし、DA オブジェクトを指し示すようにキャストされる場合、結果は A のどのインスタンスになるでしょうか。 このような場合に、あいまいなキャスト エラーが発生します。 この問題を回避するには、2 種類の明確なキャストを実行します。 次に例を示します。

// 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
}

仮想基底クラスを使用するときに、あいまいさが発生する可能性もあります。 次の図に示すクラスの階層構造の場合を考えてみましょう。

Diagram of a class hierarchy that shows virtual base classes.

次の図は、クラス A、B、C、D、および E が次のように配置されていることを示しています。クラス A は B の基底クラスです。クラス C と E はそれぞれ B から派生します。クラス E も D を継承し、クラス A から継承するクラス B を継承します。

仮想基底クラスを示すクラスの階層構造

この階層構造では、A は仮想基底クラスです。 クラス E のインスタンスとサブオブジェクトへの A ポインターを指定すると、 dynamic_cast ポインターへのポインター B はあいまいさのために失敗します。 最初に、完全な E オブジェクトにキャストし、その後、明確な方法で階層を上へとたどり、正しい B オブジェクトに到達する必要があります。

次の図に示すクラスの階層構造の場合を考えてみましょう。

Diagram of a class hierarchy that shows duplicate base classes.

次の図は、クラス A、B、C、D、および E が次のように配置されていることを示しています。クラス B はクラス A から派生します。クラス C はクラス A から派生します。クラス D はクラス B から派生します。クラス E はクラス A から派生したクラス C から派生します。この場合、重複する基底クラスはクラス A であり、他のすべてのクラスによって直接または間接的に継承されます。 クラス A は、クラス B と C によって直接継承され、クラス B を介してクラス D によって間接的に継承され、クラス C を介してクラス E によって間接的に継承され、クラス B を介してクラス D で間接的に継承されます。

重複基底クラスを示すクラスの階層構造

E 型のオブジェクトと D サブオブジェクトへのポインターがあるとして、D サブオブジェクトから左端の A サブオブジェクトに移動するには、変換を 3 回行います。 D ポインターから E ポインターへの dynamic_cast 変換を実行してから、E から B への変換 (dynamic_cast または暗黙の型変換) を実行し、最後に B から A への暗黙の型変換を実行することができます。 次に例を示します。

// 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
}

演算子をdynamic_cast使用して、"クロス キャスト" を実行することもできます。同じクラス階層を使用すると、完全なオブジェクトが型Eである限り、たとえば、サブオブジェクトからBサブオブジェクトにDポインターをキャストできます。

クロス キャストを考慮すると、2 つの手順でポインター D から左端 A のサブオブジェクトへのポインターへの変換を行うことができます。 D から B にクロス キャストを実行してから、B から A への暗黙の型変換を実行できます。 次に例を示します。

// 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
}

Null ポインター値は、dynamic_cast によって変換先の型の Null ポインター値に変換されます。

使用dynamic_cast < type-id > ( expression )すると、型type-idに安全に変換できない場合expressionは、実行時のチェックによってキャストが失敗します。 次に例を示します。

// 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
}

ポインター型への失敗したキャストの値は、null ポインターです。 参照型へのキャストに失敗すると、bad_cast 例外がスローされます。 有効なオブジェクトをポイントまたは参照していない場合 expression は、 __non_rtti_object 例外がスローされます。

__non_rtti_object 例外の詳細については、「typeid」を参照してください。

次のサンプルは、オブジェクト (構造体 C) への基底クラス (構造体 A) ポインターを作成します。 このことと、仮想関数があるという事実によって、実行時ポリモーフィズムが可能になります。

このサンプルでは、階層内の非仮想関数も呼び出されます。

// 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

関連項目

キャスト演算子
キーワード