MSCV使用
one vptr per object and one vtbl per class
实现诸如RTTI和虚拟函数等OO机制。
因此,只有正确设置了vptr,rtti和virtual函数才能正常工作。
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成为a
pure virtual function
B是抽象类。
这个
definition
纯虚函数的
could
(不是
must
失踪。
C++在构造函数中允许虚函数调用,这是我们不推荐的。
更新:
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,
这个& gt;f-();
在取消对未初始化的vptr的引用时将崩溃。
实际上,d2::d2()中的vptr在msvc(msvc8)中是d2。编译器在d2::d2()中执行其他代码之前将vptr设置为d2。
所以
这个& gt;f-();
调用d2::f(),将违反这三个断言。