C++ プログラミングガイド ホーム目次前ページへ次ページへ索引


第 7 章

キャスト演算

この章では、C++ 標準で新しいキャスト演算子である、const_castreinterpret_caststatic_castdynamic_cast について説明します。キャストは、オブジェクトまたは値の型を別の型に変換します。

新しいキャスト演算

C++ 標準では、以前のキャスト演算よりキャストの制御が優れた新しいキャスト演算を定義しています。dynamic_cast<> 演算子では、多様なクラスへのポインタの実際の型を調べることができます。古い形式のキャストを検索するには構文解析が必要ですが、新しい形式のキャストはテキストエディタを使用してすべて検索できます (_cast を検索する)。

それ以外では、これらの新しいキャストはすべて、古いキャスト表記で許可されたキャスト演算の一部を実行します。たとえば、const_cast<int*>v は (int*)v と書くことができます。これらの新しいキャストは、利用できる演算の種類を簡潔に分類してプログラマの意図をより明確に示し、コンパイラがより効率のよい検査を行うようにします。

キャスト演算子は常に有効です。無効にはできません。

const キャスト

式 const_cast<T>(v) を使用して、ポインタまたは参照の const 修飾子または volatile 修飾子を変更することができます(新しい形式のキャストの内、const 修飾子を削除できるのは const_cast<> のみ)。T はポインタ、参照、またはメンバー型へのポインタでなければなりません。

class A 
{ 
public: 
  virtual void f();
  int i; 
};
extern const volatile int* cvip;
extern int* ip;
void use_of_const_cast( )
{ 
  const A a1;
  const_cast<A&>(a1).f( );            // const を削除
ip = const_cast<int*> (cvip);         // const と volatile を削除
}     

解釈を変更するキャスト

式 reinterpret_cast<T>(v) は式 v の値の解釈を変更します。この式は、ポインタ型と整数型の間、2 つの無関係なポインタ型の間、ポインタ型からメンバー型へ、ポインタ型から関数型へ、という各種の変換に使用できます。

reinterpret_cast 演算子を使用すると、未定義の結果または実装に依存しない結果を出すことがあります。次に、確実な動作について説明します。

静的キャスト

式 static_cast<T>(v) は式の値 v を型 T の値に変換します。この式は、暗黙的に実行されるすべての型変換に使用できます。さらに、いかなる値でも void にキャストすることができ、いかなる暗黙的型変換でも、そのキャストが旧式のキャストと同様に正当である限り、反転させることができます。

class B            { ... };
class C : public B { ... };
enum E { first=1, second=2, third=3 };
void use_of_static_cast(C* c1 )
    {
      B* bp = c1;                  // 暗黙的な変換
C* c2 = static_cast<C*>(bp); // 暗黙的な変換を反転させる
int i = second; // 暗黙的な変換
E e = static_cast<E>(i); // 暗黙的な変換を反転させる
    }

static_cast 演算子を使用して、constconst 以外の型にするようなキャストを行うことはできません。階層の下位に (基底から派生ポインタまたは参照へ) キャストするには static_cast を使用できますが、変換は検証されず、結果は使用できない場合があります。抽象基底クラスから下位へのキャストには、static_cast は使用できません。

動的キャスト

クラスへのポインタ (または参照) は、そのクラスから派生されたすべてのクラスを実際に指す (参照する) ことができます。場合によっては、オブジェクトの完全派生クラス、またはその完全なオブジェクトの他のサブオブジェクトへのポインタを得る方が望ましいことがあります。動的キャストによってこれが可能になります。


注 - 互換モード (-compat[=4] ) でコンパイルする場合、プログラムが動的キャストを使用している場合は、-features=rtti を付けてコンパイルする必要があります。

動的な型のキャストは、あるクラス T1 へのポインタ (または参照) を別のクラス T2 のポインタ (または参照) に変換します。T1T2 は、同じ階層内になければなりません。両クラスとも (公開派生を介して) アクセス可能でなければならず、変換はあいまいであってはなりません。また、変換が派生クラスからその基底クラスの 1 つに対するものでないかぎり、T1T2 の両方が入った階層の最小の部分は多相性がなければなりません (少なくとも仮想関数が 1 つ存在すること)。

dynamic_cast<T>(v) では、v はキャストされる式であり、T はキャストの対象となる型です。T は完全なクラス型 (定義が参照できるもの) へのポインタまたは参照であるか、あるいは「cv void へのポインタ」でなければなりません。ここで cv は空の文字列、constvolatileconst volatile のいずれかです。

階層の上位にキャストする

階層の上位にキャストする場合で、v が指す (参照する) 型の基底クラスを T が指す (あるいは参照する) 場合、変換は static_cast<T>(v) で行われるものと同じです。

void* にキャストする

Tvoid* の場合、結果はオブジェクト全体のポインタになります。つまり、v はあるオブジェクト全体の基底クラスの 1 つを指す可能性があります。この場合、dynamic_cast<void*>(v) の結果は、v をオブジェクト全体の型 (種類は問わない) に変換した後で void* に変換した場合と同じです。

void* にキャストする場合、階層に多相性がなければなりません (仮想関数が存在すること)。結果は実行時に検証されます。

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

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

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

たとえば、次の例は公開クラスを継承しているため成功します。

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;        // キャストは不要
  A*  ap  = &ab;
  AB& abr = dynamic_cast<AB&>(*bp);  // 成功
  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 にアクセスできないために失敗します。

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;    // B が非公開であるため、C の形式のキャストが必要
  A* ap  = dynamic_cast<A*>(bp); // 失敗。B にはアクセス不能
  assert(ap == NULL);
  AB& abr = dynamic_cast<AB&>(*bp); 
  try {
    AB& abr = dynamic_cast<AB&>(*bp); // 失敗。B にはアクセス不能
  }
  catch(const bad_cast&) {
    return; // ここで失敗した参照キャストが捕獲される
  }
  assert(0); // ここまでは到達しない
}

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;
                    // OK: A を静的に特定できる
      AB*abp = dynamic_cast<AB*>(ap);
                    // 失敗: あいまい
      assert( abp == NULL );
                    // 静的エラー: AB_B* ab_bp = (AB_B*)ap;
                    // 動的キャストではない
      AB_B*ab_bp = dynamic_cast<AB_B*>(ap);
                    // 動的キャストは成功
      assert( ab_bp != NULL ); 
    }

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

void using_dynamic_cast( A* ap )
    {
      if ( AB *abp = dynamic_cast<AB*>(ap) )
          {    // abp は NULL ではない。
               // したがって ap は AB オブジェクトへのポインタである。
               // abp を使用する。
            process_AB( abp ); }
      else
          {    // abp は NULL である。
               // したがって ap は AB オブジェクトへのポインタではない。
               // abp は使用しない。
            process_not_AB( ap ); 
    }

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

実行時型情報が無効にされている場合、すなわち -features=no%rtti の場合には 、コンパイラは dynamic_cast を static_cast に変換し、警告を発行します。参照型への動的キャストを行う場合は、そのキャストが実行時に無効であると判明したときに送出される例外が必要です。

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


サン・マイクロシステムズ株式会社
Copyright information. All rights reserved.
ホーム   |   目次   |   前ページへ   |   次ページへ   |   索引