12

次のコードではobj、ケース 1 の構築中にderivedクラス オブジェクトも構築しますが、そのメンバー関数にはobj. そのため、ソースとして使用してダウンキャストしている間 (つまり、ケース 2)、既にobj構築されています。derivedなぜobjポリモーフィックである必要があるのでしょうか?

上記の説明であなたを混乱させた場合、アップキャスト時に がポリモーフィックである必要がないのにobj、ダウンキャスト中はを使用している間はポリモーフィックである必要があるのはなぜですか?dynamic_cast

class base
{
public:
    base()
    {
        cout<< " \n base constructor \n";
    }
};

class derived : public base
{
public:
    derived()
    {
        cout << " \n derived constructor \n";
    }
};

base *obj = dynamic_cast<base*> (new derived); // case 1: explicitly upcasting
derived *OBJ = dynamic_cast<derived*> (obj);   // case 2: error
4

4 に答える 4

22

dynamic_castが機能するには、オブジェクトがポリモーフィックである必要があります。これは、dynamic_castがキャストの実行に使用する型情報を格納する場所が必要であり、クラスの vtable と一緒に情報を格納することでこれを行うためです。vtable を作成するには、少なくとも 1 つのメソッドを仮想にする必要があります。

これを回避する最も簡単な方法は、基本クラスのデストラクタを仮想としてフラグを立てることです。

コンパイラはコンパイル時にキャストが機能することを確認できるため、アップキャスト (つまり、ベースに派生) にはキャストは必要ありません。ただし、ダウンキャストの場合はそうではありません。

于 2011-01-10T08:25:26.507 に答える
6

5.2.7 / 1 [expr.dynamic.cast]から:

式のdynamic_cast<T>(v)結果は、式vをタイプTに変換した結果です。

[...]

Tが「cv1Bへのポインタであり、vのタイプが「cv2 Dへのポインタ」であり、BがDの基本クラスである場合、結果はvが指すDオブジェクトの一意のBサブオブジェクトへのポインタになります。

[...]

それ以外の場合、vはポリモーフィック型へのポインタまたは左辺値になります。

この標準は、ポリモーフィック型の要件が派生からベースへの変換を表していないことを示す次の例も提供しています。

struct B {};
struct D : B {};
void foo(D* dp)
{
    B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
于 2011-01-10T08:10:10.650 に答える
0
B* b = new D();
D* d = dynamic_cast<D*>(b);

上記の例では、ほとんどのコンパイラは、b の vtable ポインターが派生クラス D の vtable を指しているかどうかをチェックすることによって、動的キャストを実装します。はいの場合、単純に b のアドレスを戻り値として返します。それ以外の場合は nullptr を返します。これは、動的キャストが実行されるときに舞台裏で行われる可能性があることです:-

class car
{
    public:
    virtual void drive()
    {
         std::cout <<"car"<<std::endl;
    }
};
class toyota: public car
{
    public:
    virtual void drive()
    {
        std::cout <<"toyota"<<std::endl;
    }
};

class honda: public car
{
    public:
        virtual void drive()
    {
        std::cout <<"honda"<<std::endl;
    }
};

template <typename Tderived>
Tderived* dynamicCast(void* pBase)
{
    //compare the vptr of the class pointed by pBase with a temporary Tderived class. 
    //If vptr of pBase and vptr of Tderived() are pointing to the same vtable 
    //then it can be safely deduced that pBase is indeed pointing to an instance of Tderived
    if (*(int**)pBase == *(int**)&Tderived())
    {
        return (Tderived*)pBase;
    }
    else
    {
        return nullptr;
    }
}


int main()
{
    car* pCar;
    honda hondaCar;
    toyota toyotaCar;

    pCar = &toyotaCar;

    honda* pHonda = dynamicCast<honda>(pCar);
    if (nullptr != pHonda)
    {
        pHonda->drive();
    }
    else
    {
        toyota* pToyota = dynamicCast<toyota>(pCar);
        if (nullptr != pToyota)
        {
            pToyota->drive();
        }
    }
}

ここで、クラスがポリモーフィックでない場合、pCar がホンダ車またはトヨタ車のどちらを指しているかをコンパイラーが検出する方法はありません。C++ 標準は vtables について何も述べていないため、これは dynamic_cast を実装する方法の 1 つにすぎないことに注意してください。

于 2016-03-04T05:56:53.540 に答える
0

Dynamic_cast

  • ベースポインターを派生ポインターにキャストするために使用されます。ベース ポインターが派生型のオブジェクトを指していない場合は、戻り値が返されます。
  • ベース参照を派生参照にキャストするために使用されます。参照が派生オブジェクトを指していない場合、std::bad_cast がスローされます。
  • これは、指しているオブジェクトが本当に派生型であるかどうかをチェックするという点で、static_cast と同等のチェック済みキャストと見なすことができます。

Dynamic_cast (例を含む) の詳細については、こちらを参照してください

于 2011-01-10T09:21:00.777 に答える