是否存在空虚表?
Can an empty virtual table exist?
#include <iostream>
using namespace std;
class Z
{
public:
int a;
virtual void x () {}
};
class Y : public Z
{
public:
int a;
};
int main()
{
cout << "nZ: " << sizeof (Z);
cout << "nY: " << sizeof (Y);
}
因为Y继承了Z,所以它也有一个虚表。很好。但是,它没有虚函数,那么Y的虚表的内容是什么呢?
会是空的吗?
这完全依赖于编译器。当我强制实例化Y
和Z
时,g++ 4.4.5
为Y
和Z
产生两个不同的虚拟表,它们具有相同的大小。
两个表指向相同的x()
,但指向不同的typeinfo
结构体:
;=== Z's virtual table ===
_ZTV1Z:
.quad 0
.quad _ZTI1Z ; Z's type info
.quad _ZN1Z5xEv ; x()
_ZTI1Z:
; Z's type info (omitted for brevity)
;=== Y's virtual table ===
_ZTV1Y:
.quad 0
.quad _ZTI1Y ; Y's type info
.quad _ZN1Z5xEv ; x()
_ZTI1Y:
; Y's type info (omitted for brevity)
在你发布的例子中,默认情况下GCC将完全优化掉虚表。因为它只有一个翻译单元,所有内容对它都是可见的,所以这是可能的。
我把你的例子改成:
#include <iostream>
using namespace std;
class Z
{
public:
int a;
virtual void x () const {}
};
class Y : public Z
{
public:
int a;
};
int main()
{
Y y;
const Z& z1=y;
const Z& z2=Z();
z1.x(),z2.x();
cout << "nZ: " << sizeof (Z);
cout << "nY: " << sizeof (Y);
}
在本例中,输出中生成了一个虚值表:
nm a.out|c++filt|grep -i vtable
08048880 V vtable for Y
08048890 V vtable for Z
0804a040 V vtable for __cxxabiv1::__class_type_info@@CXXABI_1.3
0804a120 V vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3
如果我们用-S
生成程序集,那么我们可以找到构造函数(在我的系统上分别被修改为_ZN1ZC2Ev
和_ZN1YC2Ev
)。它们负责设置虚表(_ZTV1Z
和_ZTV1Y
):
Z
:
_ZN1ZC2Ev:
.LFB970:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
movl $_ZTV1Z+8, (%eax)
popl %ebp
.cfi_def_cfa 4, 4
.cfi_restore 5
ret
And Y
:
_ZN1YC2Ev:
.LFB972:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call _ZN1ZC2Ev
movl 8(%ebp), %eax
movl $_ZTV1Y+8, (%eax)
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
这里有趣的是,在两个构造函数中放入虚表的内容本质上是相同的。
在一般编译器实现中,Y的虚表将具有与Z
这些细节已经在其他答案中添加了,但是从一个非常高级的角度来看,您必须认为派生类型Y
确实继承了Z
的所有虚函数,它只是没有为它们中的任何一个提供覆盖(好吧,对于单个)。
整个虚函数表的思想是,所有从该基基派生的类型都有一个与兼容的表。当编译器需要找到要调用的虚方法的特定实现时,它知道它可以依赖于存在的表,并且表的元素是指向该特定对象的方法的final-overrider的指针,即使final-overrider碰巧是第一个也是唯一的重写(如您的示例)。
相关文章:
- 虚拟表如何处理纯虚函数
- 虚表中虚函数的地址
- 通过虚表接口使用Excel
- 在静态分配对象上调用虚函数时使用的虚表
- 为什么在析构函数中将虚表设置回该级别
- 编译一个Qt单文件从命令行:未定义的引用到虚表
- Qt与代码块-未定义的引用虚表
- 在多重继承中,被重写的虚方法保存在c++的虚表中
- 对虚表的未定义引用.Q_OBJECT宏
- 在c++中禁用虚函数的动态绑定(创建虚表)
- 尽管没有虚函数,对虚表的未定义引用
- c++虚表查询
- 虚表的顺序重要吗?
- 对虚表、构造函数和析构函数的未定义引用
- 我试图访问一个c++多态类的虚表,但失败的核心转储,为什么
- 对虚表的未定义引用
- 不带虚表的c++动态分派
- 为什么在虚表中需要一个纯虚函数表项
- 未定义对类的虚表的引用
- 虚表/调度表