__declspec(novtable)以及虚函数调用

MSDN上描述如下

This is a __declspec extended attribute.

This form of __declspec can be applied to any class declaration, but should only be applied to pure interface classes, that is, classes that will never be instantiated on their own. The__declspec stops the compiler from generating code to initialize the vfptr in the constructor(s) and destructor of the class. In many cases, this removes the only references to the vtable that are associated with the class and, thus, the linker will remove it. Using this form of __declspec can result in a significant reduction in code size.

If you attempt to instantiate a class marked with novtable and then access a class member, you will receive an access violation (AV).

简单来说如果一个类A有__declspec修饰,那么这个类A将不会生成虚函数表,也不会初始化虚函数指针。证明如下,新建DLL,添加如下代码


struct __declspec( dllexport ) __declspec(novtable) A
{
       virtual void func()=0;
};
struct __declspec( dllexport ) B: public A
{

};

然后将生成的DLL用Dependency Walker打开,将看到如下信息

A::A(struct A const &)
A::A(void)
B::B(struct B const &)
B::B(void)
struct A & A::operator=(struct A const &)
struct B & B::operator=(struct B const &)
const B::`vftable'

类A没有虚函数表,而类B有。


虚函数

虚函数是用来实现运行期多态的,是不是调用了虚函数就一定会访问到虚表呢,答案是否定的。《深度探索C++模型》中提到“经一个由class object调用的virtual function,这种操作总是被编译器像对待一般的nonstatic member function一样加以决议”,所以如果是通过对象来调用虚函数,并不会访问到虚表和虚指针。如果是通过指针或引用都会访问到虚表(无论指向基类对象还是派生类对象)。代码如下,当然下述代码没有涉及到派生类

class __declspec(novtable) A 
{ 
public: 
	A(){} 
	virtual void Test(){} 
	virtual void Test2(){Test();} 
};

A a;
a.Test(); //(1)OK
a.Test2();//(2)Error

A &a1 = a;
a1.Test(); (3)//Error;

A *pA = &a;
pA->Test();(4)//Error;

由于类A有__declspec(novtable)修饰,所以不会生成虚表,也不会初始化虚指针,所以(1)是正常的,(3)和(4)调用是非法的,(2)的调用在非虚函数里再调用虚函数,起初以为编译器会把虚函数的调用决议成nonstatic member function,但很明显第二个调用的虚函数调用访问到了虚表。

class A 
{ 
public: 
	A(){} 
	void Test()
	{
		Test2();
	} 

	virtual void Test2()
	{
		
	} 
};

class B : public A 
{ 
public: 
	B(){} 
	void Test()
	{

	} 
	
	virtual void Test2()
	{
		
	} 
	
};

int main()
{
	B b;
	A *a = &b;
	a->Test();

	getchar();
	return 0;
} 
pa先调用A::Test()再调用B的Test2,可见通过对象间接调用虚函数也是会访问到虚表的。

你可能感兴趣的:(ATL,c++)