14

または、__declspec(novtable) を使用することによるその他の既知の悪影響はありますか? 問題への参照が見つからないようです。

4

2 に答える 2

11

MSCV はone vptr per object and one vtbl per class、RTTI や仮想関数などの OO メカニズムを実装するために使用します。
そのため、vptr が正しく設定されている場合にのみ、RTTI と仮想機能は正常に動作します。

struct __declspec(novtable) B {
    virtual void f() = 0;
};
struct D1 : B {
    D1() {
    }       // after the construction of D1, vptr will be set to vtbl of D1.
};
D1 d1;      // after d has been fully constructed, vptr is correct.
B& b = d1;  // so virtual functions and RTTI will work.
b.f();      // calls D1::f();
assert( dynamic_cast<D1*>(&b) );
assert( typeid(b) == typeid(D1) );

を使用する場合、 B は抽象クラスである必要があります__declspec(novtable)
D1 のコンストラクタを除いて、B のインスタンスはありません。
また、ほとんどの場合、__declspec(novtable) に悪影響はありません。

しかし、派生クラスの構築中に、__declspec(novtable)ISO C++ セマンティックとは異なるものになります。

struct D2 : B {


    D2() {  // when enter the constructor of D2 \  
            //     the vtpr must be set to vptr of B \
            //     if  B didn't use __declspec(novtable).
            // virtual functions and RTTI will also work.

            this->f(); // should calls B::f();
            assert( typeid(*this) == typeid(B) );
            assert( !dynamic_cast<D2*>(this) );
            assert( dynamic_cast<B*>(this) );

            // but __declspec(novtable) will stop the compiler \
            //    from generating code to initialize the vptr.
            // so the code above will crash because of uninitialized vptr.
    }
};

注: virtual f() = 0; f を a にpure virtual function、B を抽象クラスにします。純粋仮想関数( ではない)
のが欠落している可能性があります。 C++ では、コンストラクターでの仮想関数呼び出しが許可されていますが、これはお勧めできません。definitioncouldmust

更新: D2 の間違い: 派生コンストラクターの vptr。

struct D3 : B {  // ISO C++ semantic
    D3() {       // vptr must be set to vtbl of B before enter
    }            // vptr must be set to vtbl of D2 after leave
};

ただし、 vptr は構築中は不定です。これが、コンストラクターでの仮想関数呼び出しが推奨されない理由の 1 つです。

D2::D2() の vptr が B で、B::f() の定義が欠落しているthis->f();場合、vtbl で関数へのポインターを逆参照するとクラッシュします。
D2::D2() の vptr が B で、B が novtable を使用するthis->f();場合、初期化されていない vptr を逆参照するとクラッシュします。

実際、D2::D2() の vptr は MSVC (msvc8) の D2 です。コンパイラは、D2::D2() で他のコードを実行する前に vptr を D2 に設定します。
D2 this->f();::f() を呼び出すと、3 つのアサーションに違反します。

于 2009-12-02T21:49:50.740 に答える
3

私が正しく理解している場合:ctorまたはdtor内の仮想fn呼び出しは、コンパイル時のリンクに変換されます。(c/d)tor から仮想 fn 呼び出しを行うことはできません。その理由は、基本クラスのオブジェクトが作成される時点で、派生クラスの知識がないため、派生クラスを呼び出して、同じロジックが適用される dtors を作成できないためです。

于 2011-02-23T16:22:36.707 に答える