屌丝对微软关键字__declspec (novtable)的浅显理解

今天实验了微软的关键字__declspec ( novtable ) ,查阅MSDN其中有几点说明如下:

1>   该关键字仅能应用到纯虚接口类(pure interface classes)的声明上。

2>   构造函数和析构函数中移除了对类的虚表的初始化,极大的减小了代码的尺寸(可能连虚表所占的内存都没有申请)。

通过实验之后发现,这个关键字的唯一作用就是使编译器移除对虚表初始化。试想一下,如果类没有了虚表,几乎就等于C里面的结构体,那还能叫什么面向对象的类呢,这极大的欺骗了我对C++的感情。特别是在运用到COM对象,MFC和ATL采用了不同形式,又增加了我对于使用这个关键字的疑惑,真是满脑子里面都是问号啊,因此也就猜测这大概就是MFC为什么采用内嵌类这种结构来支持COM对象的原因。

废话不多说下面给出简短的代码来证明:

为了便于观察,首先构造一个存在虚表的基类。请注意:众所周知不被声明为virtual的函数,编译器不会将它添加到虚表中,在编译就静态的生成直接跳转到符号处了。所以在这里都使用virtual声明和定义函数。

struct  CObjVtbl{
public:
	virtual HRESULT QueryInterface(int iid){
		return S_OK;
	};
};
class __declspec(novtable) CPSInterfaceImpl2:public CObjVtbl
{
public:
	virtual HRESULT QueryInterface(int iid){
		return S_FALSE;返回值不同
	}
	virtual HRESULT AddRef(){
		return S_OK;
	}
};
如果按照对C++对象的内存分布(google上有非常多的参考如: http://blog.csdn.net/haoel/article/details/3081328)没有指定该关键字,你一定会认为CPSInterfaceImpl2的虚表里应该是放了两个函数指针。反之结果会如何:

CPSInterfaceImpl2* pInstance2 = new CPSInterfaceImpl2;
void** pVtbl2 = *(void***)(pInstance2);
int iSzVtbl=0;
void* pFunc = *pVtbl2;
while(pFunc){
	iSzVtbl++;结果是1
	pVtbl2 += 1;
	pFunc = *pVtbl2;
};

HRESULT hr = pInstance2->QueryInterface(15);结果是S_OK
__try{
	hr = pInstance->AddRef();抛出AV异常
}__except(EXCEPTION_EXECUTE_HANDLER){
}
实验证明由__declspec (novtable)声明和定义的类,编译器不会初始化它的虚函数表,这种类不能被实例化。(顺便想说对微软的异常过滤EXCEPTION_CONTINUE_EXECUTION印象非常不好,无论我怎么做异常处理总是死循环。)于是心里有疑问那么COM服务对象是如何让调用者使用的呢,只能有待下回分解了。



你可能感兴趣的:(编译选项)