6

この質問はこれと非常によく似ています。複数の継承中にdynamic_castを「横向き」にできないのはなぜですか。、キャストが機能することを除いて-コンストラクターの内部ではありません。

ヘッダ:

class A  
{  
public:  
    virtual                ~A() {}
    void                    printA();
};

class B
{
public:
                            B();
    virtual                ~B() {}
    void                    printB();

private:
    std::string             message_;
};

class C : public A, public B
{
public:
                        C() {}
    virtual                ~C() {}
};

ソース:

void A::printA() { cout << "A" << endl; }
B::B()
{
    A* a = dynamic_cast< A* >( this );
    if ( a ) {
        message_ = std::string( "A and B" );
    } else {
        message_ = std::string( "B" );
    }
}
void B::printB() { cout << message_.c_str() << endl; }

主要:

int main( int argc, char* argv[] )
{
    cout << "Printing C..." << endl;
    C c;
    c.printA();
    c.printB();

    cout << "Checking again..." << endl;
    cout << !!dynamic_cast< A* >( &c ) << endl;

    return EXIT_SUCCESS;
}

結果:

Printing C...
A
B
Checking again...
1

したがって、dynamic_castは多重継承に対して機能しますが(驚くことではありません!)、実行時にB :: B()内の'this'ポインターに対して呼び出された場合はどうでしょうか。オブジェクトはコンストラクターの本体内で完全に形成されたと思いました。つまり、すべてのメモリがコンポーネントオブジェクトに割り当てられましたが、まだ初期化されていません。これはスーパークラスのコンストラクターの順序に依存することを理解していますが、この例では、AはBの前に呼び出されます。

私は明らかに内部で何が起こっているのか正確には理解していません、誰かが私を教えてくれますか?

ありがとう、カムバンバー。

4

5 に答える 5

7

基本的に、標準では、オブジェクトの構築中は機能しない(dynamic_cast)とされています。<引用>

編集:以下のVJoコメントに基づいて追加されました。

注:タイプ「C」のオブジェクトをキャストしているため、動的キャストを使用した「B」から「A」へのキャストは機能するはずです。mainに次のコードを追加した場合:

B  bObj;
B& bRef = c;
B* bPtr = &c;
std::cout << !!dynamic_cast<A*>(&bObj) << std::endl;
std::cout << !!dynamic_cast<A*>(&bRef) << std::endl;
std::cout << !!dynamic_cast<A*>( bPtr) << std::endl;

追加の出力は次のようになります。

0   // Can not convert a B to an A
1   // Can convert this B to an A because it is really a C.
1   // This is  what we are reeling doing in B::B() that fails
    // It is not the dynamic_cast<> that fails but the conversion of this from C* to B*
    // That is causing UB

オブジェクトが完全に形成されていないため、コンストラクターで失敗します。これを使用して、Cコンストラクター(ユーザーが定義したコード)が開始する前に、CポインターをBポインターに変換しようとしています。したがって、thisCオブジェクトへのポインタとしてのB :: B()の使用は失敗します。したがって、これでdynamic_cast <>が呼び出されると、UBが原因で目的の処理を実行できません。

12.7建設と破壊[class.cdtor]

パラグラフ3

クラスXのオブジェクトを参照するポインター(glvalue)をXの直接または間接ベースクラスBへのポインター(参照)に明示的または暗黙的に変換するには、Xの構築とそのすべての直接または間接ベースの構築Bから直接的または間接的に派生するものが開始され、これらのクラスの破棄が完了していないものとします。そうでない場合、変換によって未定義の動作が発生します。オブジェクトobjの直接の非静的メンバーへのポインタを形成する(またはその値にアクセスする)には、objの構築が開始され、その破棄が完了していない必要があります。そうでない場合、ポインタ値の計算(またはメンバーへのアクセス)が行われます。 value)は未定義の動作になります。

[ 例:

struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X 
{ 
    E() : D(this),  // undefined: upcast from E* to A*
                    // might use path E* → D* → A* 
                    // but D is not constructed 
                    // D((C*)this), 
                    // defined: 
                    // E* → C* defined because E() has started 
                    // and C* → A* defined because
                    // C fully constructed 
      X(this) { // defined: upon construction of X,
                    // C/B/D/A sublattice is fully constructed
      } 
};

—終了例]

</ quote>

于 2011-06-09T21:21:57.737 に答える
3

各基本クラスコンストラクターは、派生クラスコンストラクターの前に実行され、Bコンストラクターの間、オブジェクトの動的型はB;です。コンストラクターCに入るまではになりません。Cしたがって、動的タイプを必要とすることはできません。他の基本クラスのCいずれにもクロスキャストできません。C仮想関数を呼び出した場合、によって提供されるオーバーライドは取得されませんC

内部的には、動的型は(少なくともほとんどの実装では)オブジェクト内のポインター(「vptr」と呼ばれる)によって決定されます。このポインターは、仮想関数のテーブルなど、クラスのプロパティを指定する静的データを指します( 「vtable」として知られています)およびに必要なdynamic_cast情報typeid。各コンストラクターの前に、これは現在構築中のクラスの情報を指すように更新されます。

于 2011-06-09T21:18:59.233 に答える
2

の構築中A、動的タイプはA関係ありません。これは、派生クラスのメンバー関数の呼び出しと派生メンバー変数へのアクセスを、構築される前に開始するためです。これはUBであり、非常に悪い結果になります。

于 2011-06-09T21:08:12.990 に答える
2

はからB継承しないためAB親のほとんどのクラス)、Bコンストラクター中の動的タイプはですBAB親の両方が構築された場合にのみ、子Cを構築でき、横向きになりdynamic_castます。

于 2011-06-09T21:08:22.607 に答える
0

BはAから継承しないため、B内では機能しません。

于 2011-06-09T21:09:39.347 に答える