__declspec(novtable)是否无效?VS2019纯虚类大小测试存疑
为什么加了
__declspec(novtable)的纯抽象类和普通纯抽象类大小一样? 你这个疑惑其实源于对__declspec(novtable)作用的常见误解——它不是用来移除类对象里的虚表指针,而是用来阻止编译器为这个纯抽象类生成实际的虚表数据。
先解释为什么sizeof(A)和sizeof(B)相同
不管有没有加novtable,只要类里声明了虚函数(包括纯虚函数),C对象的内存布局里就会保留一个虚表指针的位置——这是C实现多态的基础机制,和novtable的作用无关。在你的32位编译环境下,指针大小是4字节,所以A和B的大小自然都是4字节。
__declspec(novtable)的真正作用
纯抽象类本来就是用来做接口的,理论上永远不会被实例化,但默认情况下编译器还是会为它生成一个虚表(哪怕里面的纯虚函数没有具体实现)。novtable的作用就是告诉编译器:"这个类不会被实例化,不用给它生成虚表了"。这么做的好处是:
- 减少最终可执行文件的体积,避免冗余的虚表数据;
- 如果不小心尝试实例化这个类(比如代码写错了),链接阶段会直接报错(因为找不到虚表),比运行时崩溃更直观。
结合你的代码看差异
- 对于
struct A:编译器会为它生成一个虚表,里面包含纯虚函数fun()的条目; - 对于
struct __declspec(novtable) B:编译器不会生成B的虚表,但B的对象布局依然保留虚表指针的位置(因为有纯虚函数),所以大小不变; - 对于
struct _declspec(novtable) D : public A:D继承了A的纯虚函数,同样编译器不会为D生成虚表,直到有子类继承D并实现fun(),此时子类的虚表会被正常生成。
验证novtable作用的小实验
如果你尝试在代码里实例化B:
B b; // 编译可能通过,但链接时会报错:无法解析的外部符号 __imp_??_7B@@6B@(B的虚表)
而尝试实例化A的话,编译阶段就会直接报错:无法实例化抽象类——这也能侧面看出两者的区别,novtable把错误时机从编译推迟到了链接,但核心是避免了无用的虚表生成。
总结一下:novtable不影响对象的内存布局(所以大小不变),它管的是虚表这个全局数据结构是否被编译器生成,这才是它的核心作用。
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

