C++对象模型之内存布局三

经过两天的摸索,今天终于搞清楚C++对象模型.前两篇已经讲解了单继承,多重继承和多继承的对象模型.今天讲解菱形继承,虽然过程艰难,但是收获丰富.

简单虚继承对象模型

首先编写如下的测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
using namespace std;
class A
{
public:
A(int a1=0,int a2=0):a1(a1),a2(a2){}
virtual void f(){cout<<"A::f()"<<endl;}
virtual void af(){cout<<"A::af()"<<endl;}
int a1;
int a2;
};
class C: virtual public A
{
public:
C(int a1=1,int a2=2,int c1=4):A(a1,a2),c1(c1){
}
virtual void f1(){cout<<"C::f1()"<<endl;}
virtual void cf(){cout<<"C::cf()"<<endl;}
int c1;
};
typedef void (*pfun)();
int main(void)
{
C *cp=new C;
pfun fun=NULL;
cout<<"The C's virtual table->"<<endl;
for(int i=0;i<2;i++)
{
fun=(pfun)*((long*)*(long*)cp+i);
fun();
}
cout<<"c1="<<*((int*)cp+2)<<endl;
cout<<"The A's virtual table->"<<endl;
long* p=(long*)cp+2;
for(int i=0;i<2;i++)
{
fun=(pfun)*((long*)*(long*)p+i);
fun();
}
cout<<"a1="<<*((int*)p+2)<<endl;
cout<<"a2="<<*((int*)p+3)<<endl;
return 0;
}

上述程序的输出如下: 简单虚基类验证程序输出 简单解释下:

  1. 当存在虚基类时,先是子类的成员,然后才是虚基类的成员.

以下是C对象的对象模型: 简单虚继承对象模型

通过在gdb下,输入指令:

1
2
3
set p obj on
set p pretty on
p *this(要运行到成员函数里面)

也可以输出C对象的对象模型.截图如下: 通过gdb显示C对象模型

我在理解这个的时候,有分析过c对象调用虚基类的成员方法.通过反汇编代码,我发现当cp调用A中方法时,它先从C类的虚函数表首地址-24字节处获取A子对象相对于cp的偏移量16.所以C的虚函数表首地址负方向的空间还是有研究的地方。.

当我把C对象的函数f1改成f时,即重写A中的f方法,这时cp中A的子对象中f方法将被C的f方法替换,但是程序输出有错,原因不明。如下: C重写A的f方法

菱形继承下的对象模型

编写如下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
using namespace std;
class A
{
public:
A(int a1=0,int a2=0):a1(a1),a2(a2){}
virtual void f(){cout<<"A::f()"<<endl;}
virtual void Af(){cout<<"A::Af()"<<endl;}
int a1;
int a2;
};
class B1:virtual public A
{
public:
B1(int a1=0,int a2=0,int b1=0):A(a1,a2),b1(b1){}
virtual void f(){cout<<"B1::f()"<<endl;}
virtual void f1(){cout<<"B1::f1()"<<endl;}
virtual void Bf1(){cout<<"B1::Bf1()"<<endl;}
int b1;
};
class B2:virtual public A
{
public:
B2(int b2=0):b2(b2){}
virtual void f(){cout<<"B2::f()"<<endl;}
virtual void f2(){cout<<"B2::f2()"<<endl;}
virtual void Bf2(){cout<<"B2::Bf2()"<<endl;}
int b2;
};
class C: public B1,public B2
{
public:
C(int a1=0,int a2=0,int b1=0,int b2=0,int c1=0):B1(1,2,3),B2(6),c1(7){}
virtual void f(){cout<<"C::f()"<<endl;}
virtual void f1(){cout<<"C::f1()"<<endl;}
virtual void f2(){cout<<"C::f2()"<<endl;}
virtual void Cf(){cout<<"C::Cf()"<<endl;}
int c1;
};
typedef void (*pfun)();
int main(void)
{
C *cp=new C;
cout<<sizeof(*bp)<<endl;
pfun fun=NULL;
cout<<"The B1's virtual table->"<<endl;
for(int i=0;i<5;i++)
{
fun=(pfun)*((long*)*(long*)cp+i);
fun();
}
long* p=(long*)cp+2;
cout<<"The B2's virtual table->"<<endl;
for(int i=0;i<3;i++)
{
fun=(pfun)*((long*)*(long*)p+i);
fun();
}
cout<<"The A2's virtual table->"<<endl;
A *ap=reinterpret_cast<A*>((long*)cp+4);
for(int i=0;i<2;i++)
{
fun=(pfun)*((long*)*(long*)ap+i);
fun();
}
return 0;
}

此时程序输出仍然有错,因为c重写了A中的方法。原因不明。c对象模型为: 菱形虚继承的对象模型

如果c不重写A的f方法,即将A的f方法改为f0,则程序输出如下: 将A的f方法改为f0

我都实在ubuntu下,g++编译器实现的。但是vs的编译器实现是不同,大家可以看陈皓大哥的博客,附上陈皓大哥的博客。

陈皓专栏http://blog.csdn.net/haoel/article/details/3081328/