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.

因此仅适用于com这套东西,因为实例化时的类都是 CComObject、CComAggObject、CComPolyObject或CComContainedObject来实现,所以ATL_NO_VTABLE的类都不能实例化自己来使用,因为编译时没初始化虚函数表!


转载地址:http://blog.csdn.net/klarclm/article/details/7289828

你可能感兴趣的:(Class,interface,compiler,Constructor,destructor,linker)