本文介绍了在抽象基类上使用__declspec(novtable)会以什么方式影响RTTI吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

或者,有没有任何其他已知的负面影响使用__declspec(novtable)?我似乎找不到任何问题的提及。

解决方案

MSCV使用每个对象一个vptr,每个类一个vtbl 实现OTI机制,如RTTI和虚函数。

因此,只有当vptr设置正确时,RTTI和虚函数才能正常工作。

  struct __declspec(novtable)B {
virtual void f()= 0;
};
struct D1:B {
D1(){
} //构造D1后,vptr将被设置为D1的vtbl。
};
D1 d1; //在d完全构造之后,vptr是正确的。
B& b = d1; //所以虚函数和RTTI将工作。
b.f(); // calls D1 :: f();
assert(dynamic_cast< D1 *>(& b));
assert(typeid(b)== typeid(D1));

当使用 __ declspec(novtable)

除了在D1的构造函数中没有B的实例。

和__declspec(novtable)在大多数情况下没有负面影响。 p>

但是在构造派生类 __ declspec(novtable)期间将使其与ISO C ++语义不同。

  struct D2:B {


D2(){//当输入D2的构造函数时, \\
//如果B没有使用__declspec(novtable),则vtpr必须设置为B \
的vptr。
//虚函数和RTTI也可以工作。

this-> f(); //应该调用B :: f();
assert(typeid(* this)== typeid(B));
assert(!dynamic_cast< D2 *>(this));
assert(dynamic_cast< B *>(this));

//但__declspec(novtable)将停止编译器\
//生成代码以初始化vptr。
//因此,上面的代码会因为未初始化的vptr而崩溃。
}
};

注意:virtual f() = 0 ;使f为纯虚函数,B为抽象类。

定义的一个纯虚函数可能(不必须)缺失。

C ++允许在我们不推荐的构造函数中调用虚函数。



更新:
D2中的错误:派生构造函数中的vptr

  struct D3:B {// ISO C ++语义
D3(){// vptr必须设置为B的vtbl, b $ b} // vptr在离开
后必须设置为D2的vtbl;};

但是vptr在构造过程中是不确定的。这是虚函数调用在构造函数中的原因之一,



如果D2 :: D2()中的vptr为B且缺少B :: f()的定义,则如果在D2 :: D2()中的vptr是B和B,那么在vtbl中取消引用指针到函数的时候,this-> this-> f(); 使用novtable, this-> f(); 将在解除引用未初始化的vptr时崩溃。



事实上,D2 :: D2()中的vptr是MSVC(msvc8)中的D2。在D2 :: D2()中执行其他代码之前,编译器将vptr设置为D2。

因此 this-> f(); 调用D2 :: f(),并且三个断言将被违反。


Or, are there any other known negative affects of employing __declspec(novtable)? I can't seem to find references to any issues.

解决方案

MSCV uses one vptr per object and one vtbl per class to implement OO mechanism such as RTTI and virtual functions.
So RTTI and virtual functions will work fine if and only if the vptr has been set correctly.

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 should be an abstract class when use __declspec(novtable).
There will be no instance of B except in the constructor of D1.
And __declspec(novtable) has no negative affects in most case.

But during the construction of derived class __declspec(novtable) will make it different from ISO C++ semantic.

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

Note: virtual f() = 0; makes f to be a pure virtual function and B to be an abstract class.
The definition of a pure virtual function could (not must) be missing.
C++ allows virtual function call in constructor which we don't recommend.

Update:A mistake in D2 : the vptr in derived constructor.

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

But vptr is indeterminate during the construction.It's one of the reason that virtual function call in constructor aren't recommend .

If vptr in D2::D2() was B and definition of B::f() was missing, this->f(); will crash when dereference pointer-to-function in the vtbl.
If vptr in D2::D2() was B and B use novtable, this->f(); will crash when dereference an uninitialized vptr.

In fact , vptr in D2::D2() is D2 in MSVC(msvc8).The compiler set vptr to D2 before execute other code in D2::D2().
So this->f(); calls D2::f() and the three assertions will be violated.

这篇关于在抽象基类上使用__declspec(novtable)会以什么方式影响RTTI吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-21 09:36