1. ホーム
  2. c++

[解決済み] C++のdynamic_castとstatic_cast

2022-04-23 07:39:14

質問

について、かなり混乱しています。 dynamic_cast というキーワードを C++ で使用することができます。

struct A {
    virtual void f() { }
};
struct B : public A { };
struct C { };

void f () {
    A a;
    B b;

    A* ap = &b;
    B* b1 = dynamic_cast<B*> (&a);  // NULL, because 'a' is not a 'B'
    B* b2 = dynamic_cast<B*> (ap);  // 'b'
    C* c = dynamic_cast<C*> (ap);   // NULL.

    A& ar = dynamic_cast<A&> (*ap); // Ok.
    B& br = dynamic_cast<B&> (*ap); // Ok.
    C& cr = dynamic_cast<C&> (*ap); // std::bad_cast
}

と定義されています。

は、その dynamic_cast キーワードは、あるポインタまたは参照からデータをキャストします。 型から別の型へのキャストの妥当性を実行時にチェックします。

に相当するものを書けるか? dynamic_cast C++をCで書くと、もっとよく理解できるようになりますか?

どのように解決するのですか?

以下は、その概要です。 static_cast<>dynamic_cast<> 特にポインターに関連するものです。 これは101レベルの概要であり、すべての複雑な部分をカバーしているわけではありません。

static_cast< Type* >(ptr)

のポインタを取得します。 ptr という型のポインタに安全にキャストしようとします。 Type* . このキャストはコンパイル時に行われます。 このキャストは 型が関連している . 型が関連していない場合、コンパイラーエラーが発生します。 例えば

class B {};
class D : public B {};
class X {};

int main()
{
  D* d = new D;
  B* b = static_cast<B*>(d); // this works
  X* x = static_cast<X*>(d); // ERROR - Won't compile
  return 0;
}

dynamic_cast< Type* >(ptr)

これはまた、ポインタを ptr 型のポインタに安全にキャストします。 Type* . しかし、このキャストは、コンパイル時ではなく、実行時に実行されます。 これは実行時のキャストなので,特にポリモーフィックなクラスと組み合わせたときに便利です. 実際,ある種のケースでは が必要です。 を使用すると、キャストが有効になります。

キャストには、ベースから派生へ(B2D)と派生からベースへ(D2B)の2つの方向があります。 D2Bのキャストが実行時にどのように機能するかは簡単だ。 どちらかというと ptr から派生したもので Type か、そうでなかったのか。 D2Bのdynamic_cast<>の場合、ルールは単純です。 何かを他の何かにキャストしようとし、もし ptr から派生したもので、実際には Type を取得します。 Type* からポインタを返します。 dynamic_cast . そうでない場合は、NULLポインタを取得することになります。

しかし、B2Dのキャストはもう少し複雑です。 次のようなコードを考えてみましょう。

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void DoIt() = 0;    // pure virtual
    virtual ~Base() {};
};

class Foo : public Base
{
public:
    virtual void DoIt() { cout << "Foo"; }; 
    void FooIt() { cout << "Fooing It..."; }
};

class Bar : public Base
{
public :
    virtual void DoIt() { cout << "Bar"; }
    void BarIt() { cout << "baring It..."; }
};

Base* CreateRandom()
{
    if( (rand()%2) == 0 )
        return new Foo;
    else
        return new Bar;
}


int main()
{
    for( int n = 0; n < 10; ++n )
    {
        Base* base = CreateRandom();

            base->DoIt();

        Bar* bar = (Bar*)base;
        bar->BarIt();
    }
  return 0;
}

main() どのようなオブジェクトかわからない CreateRandom() が返されるので、C言語スタイルのキャスト Bar* bar = (Bar*)base; は明らかに型安全ではありません。 どうしたらこれを解決できるでしょうか。 一つの方法は、boolのような関数を追加することだ。 AreYouABar() const = 0; をベースクラスに追加し true から Barfalse から Foo . しかし、もう1つの方法があります。 dynamic_cast<> :

int main()
{
    for( int n = 0; n < 10; ++n )
    {
        Base* base = CreateRandom();

        base->DoIt();

        Bar* bar = dynamic_cast<Bar*>(base);
        Foo* foo = dynamic_cast<Foo*>(base);
        if( bar )
            bar->BarIt();
        if( foo )
            foo->FooIt();
    }
  return 0;

}

キャストは実行時に実行され、オブジェクトに問い合わせ(今のところ方法は気にする必要はありません)、それが探している型かどうかを尋ねることで動作します。 もしそうなら dynamic_cast<Type*> はポインタを返し、それ以外の場合はNULLを返します。

このベースから派生へのキャスティングを行うために dynamic_cast<> ベース、Foo、Barは、標準では ポリモーフィック型 . 多相型であるためには,クラスは少なくとも1つの virtual 関数を使用します。 クラスが多相型でない場合、ベースから派生への使用は dynamic_cast はコンパイルされません。 例

class Base {};
class Der : public Base {};


int main()
{
    Base* base = new Der;
    Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile

    return 0;
}

baseに仮想dtorのような仮想関数を追加すると、BaseとDerの両方が多相型になります。

class Base 
{
public:
    virtual ~Base(){};
};
class Der : public Base {};


int main()
{
    Base* base = new Der;
    Der* der = dynamic_cast<Der*>(base); // OK

    return 0;
}