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

注意:虚拟 f() = 0;使 f 成为 apure virtual function和 B 成为抽象类。缺少
一个definition纯虚函数could(不是)。 C++ 允许在我们不推荐的构造函数中调用虚函数。must

更新: 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 在构造过程中是不确定的,这也是不推荐在构造函数中调用虚函数的原因之一。

如果 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。
所以this->f();调用 D2::f() 并且这三个断言将被违反。

于 2009-12-02T21:49:50.740 回答
3

如果我理解正确:ctor 或 dtor 内的任何虚拟 fn 调用都将转换为编译时链接。我们不能从 (c/d)tors 进行虚拟 fn 调用。原因是,在创建基类的对象时,它不知道派生类,因此无法调用派生类并写入 dtors 相同的逻辑适用。

于 2011-02-23T16:22:36.707 回答