这是我的代码。我只是想看一下虚拟继承的内存布局。
#include<iostream>
using namespace std;
class A{
private:
int a;
public:
virtual void print() const{
cout << a << endl;
}
};
class B:public virtual A{
private:
int b;
public:
void print() const{
cout << b << endl;
}
};
int main(){
A a;
B b;
return 0;
}
然后在gdb中,我使用了
p a
p b
输出
(gdb) p a
$1 = {
_vptr.A = 0x400b40 <vtable for A+16>,
a = 0
}
(gdb) p b
$2 = {
<A> = {
_vptr.A = 0x400b18 <vtable for B+56>,
a = 4196384
},
members of B:
_vptr.B = 0x400af8 <vtable for B+24>,
b = 0
}
(gdb)
我知道_vptr.A和_vptr.B的含义,但我不明白v +对于B + 24或A + 16是什么意思。
感谢您的回答!
答案 0 :(得分:6)
a
的列表表明它是一个包含两个字段的类:一个vptr,指向一个字节,其中A的vtable中包含偏移量16,包含零的数据元素a
。
第二个是相同的行,但由于虚拟基类而更复杂。它说b由3个字段组成:一个A的实例,其字段与上面的字段类似,然后是B的两个元素,如上所述。这次vptr指向偏移量24。
为什么vptr指向表的中间而不是它的开始?你必须要了解更多关于gcc内存布局细节的信息,我不知道。初始字节可能是RTTI(运行时类型信息),给定的偏移量是虚函数指针数组的第一个元素。有无数的其他选择。
NB。有一些很好的信息in this earlier article表示总有2个指针指向vtable:一个用于多重继承顶部指针,另一个用于RTTI。如果您的机器和编译器是64位(您真的应该提到它;这很重要),这与16个字节的指针重合。 B可以是24个字节,因为有一个额外的指针可以访问虚拟基类。
答案 1 :(得分:0)
:
IMO,首先,在B中,您正在使用 virtual 关键字进行隐式阴影处理,这是一种不好的做法,它会在运行时给不同的编译器带来惊喜。
基于64位arch。
<vtable for A+16>
表示将A的vtable地址偏移16,这是第一个函数项。按照该帖子的assembly code应用您的代码:
vtable for A:
.quad 0
.quad typeinfo for A
.quad A::print() const
一个四边形是4个字,表示8。因此,A::print()
的地址是地址(标签)vtable for A
加16个偏移量,位于文本部分。
与B相同,这更加复杂,因为您使用的是纯虚拟继承。
vtable for B:
.quad 16
.quad 0
.quad typeinfo for B
.quad B::print() const
.quad -16
.quad -16
.quad typeinfo for B
.quad virtual thunk to B::print() const
通常,B仅有一个vtable,如上所示。但是您可以将其视为2个在概念上串联的vtable。
对于B vtable的B部分
vtable for B:
.quad 16
.quad 0
.quad typeinfo for B
.quad B::print() const
对于B vtable的A部分:
.quad -16
.quad -16
.quad typeinfo for B
.quad virtual thunk to B::print() const
这就是<vtable for B>+56
所指。