Scott Meyer
彼の本の中でEffective C++
はdynamic_cast
は、継承階層の下または全体で安全なキャストを実行するために使用されます。つまり、dynamic_castを使用して、基本クラスオブジェクトへのポインタまたは参照を、キャストが成功したかどうかを判断できるように、派生または兄弟の基本クラスオブジェクトへのポインタまたは参照にキャストします。
失敗したキャストは、nullポインター(ポインターをキャストする場合)または例外(参照をキャストする場合)によって示されます。
ポインタをキャストし、参照をキャストする場合に失敗したキャストを示す2つのコードスニペットを取得したいと思います。
ポインタの場合、これは単純なnullチェックです。
A* a = new A();
B* b = dynamic_cast<B*>(a);
if (b == NULL)
{
// Cast failed
}
参考までに、以下をキャッチできます。
try {
SomeType &item = dynamic_cast<SomeType&>(obj);
}
catch(const std::bad_cast& e) {
// Cast failed
}
これは、dynamic_cast
がポインタの生成に失敗する可能性があることを示す完全な例です。
class A
{
public:
virtual void Foo();
};
class B: public A
{
};
class C: public A
{
};
void test()
{
A a;
B b;
A* pA = &b;
B* pB = dynamic_cast<B*>(pA); // this works OK, returns the expected pointer
C* pC = dynamic_cast<C*>(pA); // this returns NULL because B isn't a C
}
現実の世界では、それほど単純に作成されていないポインタをキャストしようとします。たとえば、ポインタはvector
から取得されます。
OPのコメント(「スコットが述べたようにキャストがどのように失敗するかわかりません。」)に基づくと、ここでの本当の質問は、「dynamic_cast
が失敗する可能性があるのはなぜですか?」のようなものです。
失敗するのは、ターゲットタイプがオブジェクトの動的タイプと一致しない場合です。簡単な例:
struct A {
virtual ~A() {}
};
class B : public A {};
int main() {
A *a = new A;
B *b = dynamic_cast<B *>(a); // should fail
if (b == NULL)
std::cout << "First cast failed.\n";
A *c = new B;
b = dynamic_cast<B *>(c); // should succeed
if (b == NULL)
std::cout << "Second cast failed.\n";
return 0;
}
ここで、a
couldはタイプB
のオブジェクトを指しますが、実際にははタイプA
のオブジェクトを指します。 dynamic_castを実行してB
を指すようにしようとすると、失敗します。 2回目の試行では、タイプB
のオブジェクトを指すことができるだけでなくも指すポインターが再びあります。そのため、この場合、B *
へのdynamic_castは成功します。
参照の場合、基本的な状況は(あまり)変わりません。a
、b
、およびc
がポインターではなく参照になり、例外をキャッチすることで失敗に注意します(@ReedCopseyはすでに十分に実証しているので、私は説明していません)。追加する新しいものがあると思います)。