#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的虚拟表的内容是什么? 它会是空的吗?
答案 0 :(得分:4)
这完全取决于编译器。当我强制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)
答案 1 :(得分:3)
在您发布的示例中,GCC默认会完全优化vtable。因为它只有一个翻译单元,所以一切都是可见的。这是可能的。
我将你的例子改为:
#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);
}
在这种情况下,在输出中生成vtable:
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
。这些工作负责设置vtable(_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
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
这里有趣的是,在两个构造函数中放入vtable的内容基本相同。
答案 2 :(得分:1)
在通用编译器实现中,Y的虚拟表将具有与Z
相同的条目答案 3 :(得分:0)
其他答案中已添加了血腥细节,但从非常高的角度来看,您必须认为派生类型Y
确实拥有Z
中所有继承的虚函数,它只是没有为其中任何一个提供覆盖(嗯,对于单个覆盖)。
整个虚拟功能表的想法是,从该基础派生的所有类型将具有兼容的表。当编译器需要找到要调用的虚方法的特定实现时,它知道它可以依赖于存在的表,并且在表的元素中是指向 final-overrider的指针该特定对象的方法,即使 final-overrider 碰巧是第一个也是唯一的覆盖者,按照你的例子。