web-dev-qa-db-ja.com

dynamic_cast演算子を使用して失敗したキャストを識別する方法は?

Scott Meyer彼の本の中でEffective C++dynamic_castは、継承階層の下または全体で安全なキャストを実行するために使用されます。つまり、dynamic_castを使用して、基本クラスオブジェクトへのポインタまたは参照を、キャストが成功したかどうかを判断できるように、派生または兄弟の基本クラスオブジェクトへのポインタまたは参照にキャストします。

失敗したキャストは、nullポインター(ポインターをキャストする場合)または例外(参照をキャストする場合)によって示されます。

ポインタをキャストし、参照をキャストする場合に失敗したキャストを示す2つのコードスニペットを取得したいと思います。

16
nitin_cherian

ポインタの場合、これは単純な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
}
27
Reed Copsey

これは、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から取得されます。

4
Mark Ransom

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;
}

ここで、acouldはタイプBのオブジェクトを指しますが、実際にはタイプAのオブジェクトを指します。 dynamic_castを実行してBを指すようにしようとすると、失敗します。 2回目の試行では、タイプBのオブジェクトを指すことができるだけでなくも指すポインターが再びあります。そのため、この場合、B *へのdynamic_castは成功します。

参照の場合、基本的な状況は(あまり)変わりません。ab、およびcがポインターではなく参照になり、例外をキャッチすることで失敗に注意します(@ReedCopseyはすでに十分に実証しているので、私は説明していません)。追加する新しいものがあると思います)。

3
Jerry Coffin