C++中 _declspec(novtable) 的探讨

(1)V TA B L E(虚函数表)和VPTR(指向虚函数标的指针)的区别

编译器到底做了什么实现的虚函数的晚绑定呢?我们来探个究竟。
编译器对每个包含虚函数的类创建一个表(称为V TA B L E)。在V TA B L E中,编译器放置特定类的虚函数地址。在每个带有虚函数的类中,编译器秘密地置一指针,称为v p o i n t e r(缩写为V P T R),指向这个对象的V TA B L E。通过基类指针做虚函数调用时(也就是做多态调用时),编译器静态地插入取得这个V P T R,并在V TA B L E表中查找函数地址的代码,这样就能调用正确的函数使晚捆绑发生。为每个类设置V TA B L E、初始化V P T R、为虚函数调用插入代码,所有这些都是自动发生的,所以我们不必担心这些。利用虚函数,这个对象的合适的函数就能被调用,哪怕在编译器还不知道这个对象的特定类型的情况下。(《C++编程思想》)

(2)微软的描述

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.  
(纯虚类和减少文件大小是这短定义的精华)

(3)静态联编 和 动态联编 的区别

  静态联编是指联编工作出现在编译连接阶段,这种联编又称早期联编,因为这种联编过程是在程序开始运行之前完成的。在编译时所进行的这种联编又称静态束定。在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又称为束定,在编译时束定又称静态束定。

   编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切知道该调用的函数,要求联编工作要在程序运行时进行,这种在程序运行时进行联编工作被称为动态联编,或称动态束定,又叫晚期联编。

(4)例子代码分析

  1. class __declspec(novtable) A

  2. {

  3. public:

  4. A(){}

  5. virtualvoid Fun1(){}

  6. virtualvoid Fun2(){}

  7. };

  8. class __declspec(novtable) B : public A

  9. {

  10. public:

  11. B(){}

  12. virtualvoid Fun1(){}

  13. virtualvoid Fun3(){}

  14. virtualvoid Fun4(){Fun2();}

  15. };

  16. class C : public B

  17. {

  18. public:

  19. C(){}

  20. };

  21. void MyFunc()

  22. {

  23. C c;                            //由于 class C没有__declspec(novtable),VTable会创建成功

  24. c.Fun1();                   //调用 B::Fun1() ; 静态联编

  25. c.Fun2();                   //调用 A::Fun2() ;静态联编

  26. B b;                            //由于 class C有__declspec(novtable),VTable不会创建成功

  27. A aa = b;

  28. aa.Fun1();                //调用 A::Fun1();静态联编

  29. b.Fun1();                  //调用 B::Fun1();静态联编

  30. b.Fun2();                  //调用 A::Fun1();静态联编

  31. b.Fun4();                  //出错. 类的成员函数里调用虚函数需要 vtable指针,但是class B是不允许产生vtable指针的.

  32. B* pb = new B();

  33. pb->Fun1();            // 出错.Class B没有vtable指针; 动态联编

  34. C* pc = new C;      //由于 class C没有__declspec(novtable),VTable会创建成功; A,B,C 三个

  35.                                 //class的Vtable and VPTR是同一个,即class的Vtale and VPtr

  36. A* pa = pc;              //动态联编

  37. pa->Fun1();            //调用 B::Fun1() ; 动态联编

  38. }


希望通过以上分析,能够比较好的理解关键字 “_declspec(novtable)”的用法。纯虚类是因为class不产生虚表,只能在派生类中实现虚表;C++中没有提供类似interface这样的关键字来定义接口,但是Mircrosoft c++中提供了__declspec(novtable)来修饰一个类,来表示该类没有虚函数表,也就是虚函数都(应该)是纯虚的。减小SIZE 是因为虚表是需要空间的,在不需要虚表的情况下,把虚表去掉就可以减少SIZE了。

你可能感兴趣的:(C++,虚函数,――declspec)