キャストを調べまわってわかった事をメモ


こうですか?分かりません!><

従来のキャストは超危険

  • 無条件で変換できちゃう
  • そもそもどういった目的でキャストしているのか判らない
  • 括弧で括るだけなので、grepで探しづらい
  • 設計側としては非推奨

新しいcast

  • 目的別にキャストを分ける
    • どういった理由でキャストを行っているかわかりやすい
    • 危険なキャストを発見しやすい
  • 以下の4種類
    • dynamic_cast
    • static_cast
    • reinterpret_cast
    • const_cast

dynamic_cast

目的

安全な基底クラスから派生クラスへのキャスト(ダウンキャスト)

キャスト対象
  • 基底クラスから派生クラスへのキャスト(ダウンキャスト)
  • 派生クラスから基底クラスへのキャスト(アップキャスト)も特に問題なくできるが、そちらはstatic_cast推奨
  • 実体はキャストしない
特徴
  • 動的なキャスト(実行時にキャストの可不可をチェックする)
    • 駄目なら、エラー処理に移れるので安心して使える
    • 実行時、不正ならnullポインタを返却
      • 参照のキャストならbad_cast例外をthrow
  • 基底クラスはポリモーフィッククラス(仮想関数を持つクラス)であること
    • キャスト時に型情報をチェックするRTTI(実行時型情報)ポリモーフィッククラスが持つ仮想関数テーブルに存在するため
      • RTTIが仮想関数テーブルにあるのは、全部のオブジェクトに持たせるのはオーバーヘッドになるため
      • 仮想関数テーブルを持たないクラスはstatic_castすべきという思想→C++の設計と進化
  • 従来のキャストではできなかったクロスキャストや、抽象基底クラスからのダウンキャストも可能

static_cast

目的

危険なキャストをコンパイルエラーではじく事

キャスト対象
  • 主に派生クラスから基底クラスへのキャスト(アップキャスト)用
  • ダウンキャストもできるが、正しくないキャストの戻り値は不定
  • 暗黙の変換のキャストもできる。
    • static_cast(e)なら、コンストラクタ T(e) があるならば、キャスト可能
  • 他にもキャスト可能な例があるけど細かい話なのでこちらを参照→演算:static_cast
  • それ以外の無関係なキャストはコンパイルエラー

reinterpret_cast

目的

他のキャストが使えない場合の最終手段

キャスト対象
  • ポインタ同士のキャスト
  • ポインタと整数型のキャスト
特徴
  • 当然ながら非常に危険な上、上手く動いても実装依存になりやすい
    • 「リスクを理解した上で行うなら、コンパイラに何もいいません」というC++の思想を表したキャストだと思う。

const_cast

目的

constを取り除くことだけ

キャスト対象
  • constを持つオブジェクトから、同一の型を持つ非constのオブジェクトへのキャスト
特徴
  • 他のキャストはconstは取り除けない。
  • 使いやすいけど、使いすぎると、constの意味がなくなるので注意
  • 実はconst_castの戻り値は不定実装依存
    • 実際の所VCでもgccでも普通に使えるけど一応知っとくべき

キャストは最終手段

  • 本来はポリモーフィズムで回避すべき
    • 特定の派生クラスにしかないメソッドを実行したい場合、dynamic_castで判別せずに、基底クラスに何もしないvirtualクラスを作るべき。
悪い例
class Animal
{
	…
}

class Bird : public Animal
{
	// 飛ぶ処理
	void fly()
	{
		…
	}
	…
}

これだと、bridなら飛ぶ場合、余計な分岐が必要になる。

// 飛ぶイベント発生
void event( Animal * amnimal )
{
	// 鳥なら飛ぶ
	Bird * bird = dynamic_cast< Bird * >( amnimal );
	if( 0 != bird )
	{
		amnimal->fly();
	}
}
良い例
class Animal
{
	// 何もしない
	virtual void fly()
	{
	}
	…
}

class Bird : public Animal
{
	// 飛ぶ処理
	void fly()
	{
		…
	}
}

こうすれば、分岐がいらなくなる。

// 飛ぶイベント発生
void event( Animal * amnimal )
{
	// 鳥なら飛ぶ
	amnimal->fly();
}
  • それだと余りに冗長だったり、基底クラスがライブラリとかで弄れない場合にcastを使うべし


そんな感じ