Sun Studio 12 Update 1: C++ ユーザーズガイド

9.4.3 階層の下位または全体にキャストする

階層の下位または全体にキャストする場合、階層に多相性がある必要があります (仮想関数を持つ必要がある)。結果は実行時に検証されます。

階層の下位または全体にキャストする場合、v から T に変換できないことがあります。たとえば、試行された変換があいまいであったり、T に対するアクセスが不可能であったり、あるいは必要な型のオブジェクトを v が指さない (あるいは参照しない) 場合がこれに当たります。実行時検査に失敗し、T がポインタ型である場合、キャスト式の値は型 T のヌルポインタです。T が参照型の場合、何も返されず (C++ にはヌル参照は存在しない)、標準例外 std::bad_cast が送出されます。

たとえば、次の公開派生のコード例は正常に実行されます。


#include <assert.h>
#include <stddef.h> // for NULL

class A {public: virtual void f();};
class B {public: virtual void g();};
class AB: public virtual A, public B {};

void simple_dynamic_casts()
{
  AB ab;
  B* bp = &ab;        // no casts needed
  A* ap = &ab;
  AB& abr = dynamic_cast<AB&>(*bp);  // succeeds
  ap = dynamic_cast<A*>(bp);         assert(ap!= NULL);
  bp = dynamic_cast<B*>(ap);         assert(bp!= NULL);
  ap = dynamic_cast<A*>(&abr);       assert(ap!= NULL);
  bp = dynamic_cast<B*>(&abr);       assert(bp!= NULL);
}

これに対して、次のコード例は正しく実行されません。基底クラス B にアクセスできないからです。


#include <assert.h>
#include <stddef.h> // for NULL
#include <typeinfo>

class A {public: virtual void f() {}};
class B {public: virtual void g() {}};
class AB: public virtual A, private B {};

void attempted_casts()
{
  AB ab;
  B* bp = (B*)&ab; // C-style cast needed to break protection
  A* ap = dynamic_cast<A*>(bp); // fails, B is inaccessible
  assert(ap == NULL);
  try {
    AB& abr = dynamic_cast<AB&>(*bp); // fails, B is inaccessible
  }
  catch(const std::bad_cast&) {
    return; // failed reference cast caught here
  }
  assert(0); // should not get here
}

1 つの基底クラスについて仮想継承と多重継承が存在する場合には、実際の動的キャストは一意の照合を識別することができる必要があります。もし照合が一意でないならば、そのキャストは失敗します。たとえば、次の追加クラス定義が与えられたとします。


class AB_B:     public AB,        public B {};
class AB_B__AB: public AB_B,      public AB {};

次に例を示します。


void complex_dynamic_casts()
{
  AB_B__AB ab_b__ab;
  A*ap = &ab_b__ab;
                    // okay: finds unique A statically
  AB*abp = dynamic_cast<AB*>(ap);
                    // fails: ambiguous
  assert(abp == NULL);
                    // STATIC ERROR: AB_B* ab_bp = (AB_B*)ap;
                    // not a dynamic cast
  AB_B*ab_bp = dynamic_cast<AB_B*>(ap);
                    // dynamic one is okay
  assert(ab_bp!= NULL);
}

dynamic_cast のエラー時のヌル (NULL) ポインタの戻り値は、コード中の 2 つのブロック (1 つは型推定が正しい場合にキャストを処理するためのもの、もう 1 つは正しくない場合のもの) の間の条件として役立ちます。


void using_dynamic_cast(A* ap)
{
  if (AB *abp = dynamic_cast<AB*>(ap))
    {            // abp is non-null,
                 // so ap was a pointer to an AB object
                 // go ahead and use abp
      process_AB(abp);}
  else
    {          // abp is null,
               // so ap was NOT a pointer to an AB object
               // do not use abp
      process_not_AB(ap);
    }
}

互換モード (-compat[=4]) では、-features=rtti コンパイラオプションによって実行時の型情報が有効になっていないと、コンパイラは dynamic_caststatic_cast に変換し、警告メッセージを出します。

実行時型情報が無効にされている場合、コンパイラは dynamic_cast<T&> static_cast<T&> に変換し、警告を発行します。参照型への動的キャストを行う場合は、そのキャストが実行時に無効であると判明したときに送出される例外が必要です例外の詳細については、「7.5.3 問題がある検索の回避」を参照してください。

動的キャストは必然的に、仮想関数による変換のような適切な設計パターンより遅くなります。Erich Gamma 著 (ソフトバンク)『オブジェクト指向における再利用のためのデザインパターン』を参照してください。