ATL_NO_VTABLE详解

ATL_NO_VTABLE详解  

2012-01-28 15:20:26|  分类: COM |  标签:atl_no_vtable详解  |字号 订阅

ATL_NO_VTABLE详解

我们可以注意到在用ATL编写COM组件时,在类前面都有一个宏ATL_NO_VTABLE,那么这个宏到底有什   
么作用呢?这个宏的定义如下:   
#define   __declspec(novtable)   ATL_NO_VTABLE;   
下面这段是MSDN中对这个宏的描述:   
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.   
光看这段讲解可能不容易看懂,还是看个具体的例子:   
class   __declspec(novtable)   A   
{   
public:   
A(){}   
virtual   Test(){}   
virtaul   Test2(){}   
};   
class   __declspec(novtable)   B   :   public   A   
{   
public:   
B(){}   
virtual   Test(){}   
virtual   Test3(){Test2();}   
};   
class   C   :   public   B   
{   
public:   
C(){}   
};   
void   MyFunc()   
{   
C   c;   
c.Test();   <span   class= "remark "> //没错   </span>   
c.Test2(); <span   class= "remark "> //没错   </span>   
B   b;   
b.Test3();   <span   class= "remark "> //出错   </span>   
}   
每个类A、B、C都可以产生自己的虚函数表,我们来看看类C的构造函数里虚函数表的产生过程。   
C::C()   
{   
....   
call   B::B();   
<span   class= "remark "> //初始化虚函数表指针为C::vftable;   </span>   
}   
B::B()   
{   
.....   
call   A::A();   
<span   class= "remark "> //如果没有用__declspec(novtable)的话,初始化虚函数指针为B::vtfable   </span>   
}   
}   
A::A()   
{   
....   
<span   class= "remark "> //如果没有用__declspec(novtable)的话,初始化虚函数指针为A::vtfable   </span>   
}   
特别值得注意的是这三个构造函数中涉及到的虚函数表指针都是同一个指针,都是c的指   
针!!!所以不管类A、类B有没有初始化这个指针,最后类C都会初始化它!!即使它们   
初始了也没有用,最后指向的还是类C的虚函数表。   
而对于类B来说,因为类A、类B都没有初始化虚函数表指针,所以在类的成员函数里调用   
虚函数就会产生错误!!!   
另外,如果我们把类A的__declspec(novtable)去掉,这样B   b;这句话就不会出错,但是   
调用b.Test()时会发现它调用的是A::Test(),而不是我们希望的B::Test();这是因为类B   
没有自己初始化它的虚函数表指针为B::vftable,而是由类A初始化它的,并且初始它为   
类A的虚函数表了,而在类A的虚函数表中,Test()函数当然是指向A::Test()了,所以虽   
然编译没错,但实际执行的结果却是错的,这也是一个生动的例子。   
结合到我们的ATL中,因为我们最后实际创建的类并不是我们在ATL中实现的类,而是从   
从这个类继承的CComObject,CComAggObject,CComPolyObject,或CComContainedObject,   
(参阅ATL接口映射宏详解)(这几个类都没有使用ATL_NO_VTABLE)。我们在上面也已经看到,   
在基类中初始化虚函数表是没有用的,因为它会被派生类覆盖掉,所以我们可以对它使用   
ATL_NO_VTABLE,   免得它产生虚函数表!!   因为ATL是通过多重继承来实现COM组件的,   
继承层次中的每个类都有自己的虚函数表,所以在继承层次很深的情况下,虚函数表会变   
得非常宏大,如果用ATL_NO_VTABLE宏来阻止生成虚函数表,就会有限的减少组件的长度。   
事实上ATL中预先实现的接口类基本上都使用了这个宏。只要有继承树结构的最下层的几个类   
CComObject、CComAggObject、CComPolyObject或CComContainedObject能够正确生成   
VTABLE就可以了。也正因为这个原因,这几个类都没有使用ATL_NO_VTABLE.   
ATL_NO_TABLE就是不让COM类有虚函数表,   因为类要作为CComObject <> 的模板参数才能实例化,在CComObject生成虚函数表。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值