ぼく用あれこれまとめ
dynamic_cast演算子
最終更新:
bokuyo
-
view
dynamic_cast演算子
- 動的なバインディングこと、実行時にcastができるC++の機能だよ。
- なにかとweb上のdynamic_cast演算子の項目には「ダウンキャスト」という単語がちらほらでてくる。
- 基底classから派生classへのcastのことを「ダウンキャスト」というそうで。
- その逆、派生classから基底classへのcastを「アップキャスト」ともいうらしい。
- アップキャストは問題なし。
- 派生classは基底classの中身を継承して持ってるから、基底classにcastすることが容易にできちゃう。
- お父さんとお母さんの血を受け継いでいれば、子どもはお父さんにもお母さんにもなれる。
- それとは対照的に、ダウンキャストは問題あり。
- 基底classは、派生classの中身を持っているかどうか保障されない。通常のcastより危険度増加!
- お父さんが子どもになろうとしても、子どもがもつお母さんの血をお父さんは持ってない。
- 危険が伴うダウンキャストを行うための演算子が、dynamic_cast演算子!
使い方。
class Coralian { public: virtual ~Coralian() = 0; }; class Eureka : public Coralian { ; }; int main() { Eureka eureka; //派生classから Coralian& scab = eureka; //基底classへとアップキャスト Eureka& anemone = dynamic_cast<Eureka&>( scab ); //基底classから派生classへのダウンキャスト return 0; }
ポリモーフィックなclassじゃないとdynamic_castできないよ。
class Coralian {}; class Eureka : public Coralian {}; int main() { Coralian scab; Eureka& anemone = dynamic_cast<Eureka&>( scab ); //dynamic_castが取りうる引数が"scab"じゃだめ!って怒られる。 return 0; }
- 基底classの仮想関数を取り除いてみると、コンパイルエラーが起こる。
error C2683: 'dynamic_cast' : 'Coralian' はポリモーフィックな型ではありません。
- MSDN - コンパイラ エラー C2683
- ポリモーフィックなclassと言われても…。ここでいうポリモーフィックなclassとは仮想関数を定義した基底classのこと。
- 仮想関数を定義していないパパclassから、子どもclassへとはダイナミックなキャストは行えない仕様だそうで。
- 動的結合されたclass間でしか、dynamic_castは使えない、ということ。まさに「dynamic」。
- 性的結合…///されたclass間では性的なcastである、static_castを使うといいそうで。
- 仮想関数のように、実行時に「どのclassの関数を使おうかな」って決める継承関係のつながり方を「動的結合」という。
- 非仮想関数のように、コンパイル時にどの関数を使うのか決める関係にある継承関係のつながり方を「性的結合////」という。
- 解決策しては、static_castを用いること。実行時に型チェックが行われないけど…。
MSDNもオススメしているあの話題のstatic_castを用いての型変換
class Coralian {}; class Eureka : public Coralian {}; int main() { Coralian scab; Eureka* anemone = dynamic_cast<Eureka*>( &scab ); //「ポリモーフィックじゃないからだめ><」って言われちゃう! Eureka* anemone = static_cast<Eureka*>( &scab ); //でも、ご心配なくstatic_castならこんなに簡単。 return 0; }
- あれ…?でも奥さん、static_cast演算子でポインタ間の型変換ってできましたっけ?
protected、privateな継承をした派生classから基底classへのアップキャスト
class Coralian {}; class Eureka : protected Coralian {}; int main() { Eureka eureka; //protectedな継承をした派生classから Coralian* scab = &eureka; //基底classへとアップキャストをしてみるもコンパイルエラー。 return 0; }
- これまたコンパイルエラー。protected, privateな継承をしている場合、castについてもアクセスすることができないとのこと。
error C2243: '型キャスト' : 'Eureka *' から 'Coralian *' の変換は存在しますが、アクセスできません。
- MSDN - コンパイラ エラー C2243
- アクセスできない基本class(基底class)への変換はできないとのこと。VC++たんいわく。
dynamic_castに失敗すると
#include <iostream> class ICoralian { public: virtual void method() = 0; //仮想関数によってセレブな動的結合を演出し }; class Eureka : public ICoralian { public: void method(){}; //publicな継承でモテカワスイート愛され系な定義を。 }; class Anemone : public ICoralian { public: void method(){}; //流行にのってゆるふわな定義で。 }; void apprivoiser(ICoralian*& scab) { //基底classから派生classへのダウンキャスト Eureka* eureka = dynamic_cast<Eureka*>(scab); if(eureka != 0){ std::cout << "ダウンキャストに失敗すると0が返される" << std::endl; } else { std::cout << "噂によるとstd::bad_cast型の例外もthrowされるとか" << std::endl; } } int main() { Eureka eureka; //派生classから ICoralian* scab = &eureka; //基底classへのアップキャスト apprivoiser(scab); //このダウンキャストは成功する…はず。 Anemone anemone; //派生classから ICoralian* unco = &anemone; //基底classへのアップキャスト apprivoiser(unco); //このダウンキャストは失敗する…はず。 return 0; }
- dynamic_castに失敗すると、0が返される。
- NULLポインタかどうか適宜チェックしたほうがよさそう。
ちなみにboostでshared_ptrを使ってる場合
- dynamic_castは使えません。その代わり、dynamic_pointer_castってのがあります。
- static_pointer_castも自由関数(friend)として存在してるみたいです。
参考文献
- C++編(言語解説)第24章 C++独自のキャスト
- MSDN - dynamic_cast Operator(英語)
- MSDN - dynamic_pointer_cast 関数
- MSDN - 7.5.1 dynamic_cast
- MSDN - dynamic_cast の互換性に影響する変更点
- VC++2008からdynamic_cast演算子について変更があったようです。