C++对象的内存布局

一篇写的比较好的博客 http://blog.csdn.net/haoel/article/details/3081328

这篇文章中主要想说以下几个问题

1 如何通过对象获得虚函数表中虚函数的地址

2 分几种情况讨论内存布局

1》单一继承

2》多重继承

3》重复继承

4》钻石虚拟继承(为了解决重复继承中出现问题而产生的虚拟继承)

虚函数主要是通过一张虚函数的地址表来实现的,简称V-Table,在有虚函数的实例中这个表被分配在这个实例的内存中,

当我们用一个父类指针来操作一个子类的时候,这张虚函数表就显的非常重要,告诉我们应该调用哪个函数。当然这么重要的表

的初始化是在对象的构造函数中,这样才能反映对象真实的意愿,指针才能调用到正确的函数。

c++编译器保证虚函数表指针存在于对象实例中最前面的位置。其他的编译器也有不同的安排

这样我们可以通过对象来获取虚函数表的地址,然后便利其中的函数指针。

class Base {
     public:
		     int a;
			 
            virtual void f() { cout << "Base::f" << endl; }
            virtual void g() { cout << "Base::g" << endl; }
            virtual void h() { cout << "Base::h" << endl; }
 

};

void main()
{
	Base b;
    cout << "&b:" << &b << endl;
    cout << "&b+1:" << &b+1 << endl;
	cout << "(int*)(&b):" << (int*)(&b) << endl;
    cout << "(int*)(&b)+1:" << (int*)(&b)+1 << endl;
    cout <<"*(int*)(&b)"<<*(int*)(&b)<<endl;
    cout <<"*(int*)(&b)+1"<<*(int*)(&b)+1<<endl;
	cout << "(int*)*(int*)(&b)" << (int*)*(int*)(&b) << endl;
    cout << "(int*)*(int*)(&b)+1" << (int*)*(int*)(&b)+1 << endl;
 结果


&b对象地址

(int*)&b虚指针的地址(存放虚函数表的地址的指针)

*(int*)&b虚函数表的地址中存放的内容,及虚函数表的地址

(int*)*(int*)&b 第一个虚函数的地址


1 单一继承

程序就不写了,可以看那篇博客的

继承关系描述:


GrandChild 的内存布局:


结论:

1 虚函数在最前面

2 成员变量的根据其继承和声明顺序依次放在后面

3 在单一继承中,被overwrite的虚函数在虚函数表中得到了更新。函数的顺序按照继承声明的顺序依次存放。


2 多重继承




我们可以看到:

1  每个父类都有自己的虚表。

2  子类的成员函数被放到了第一个父类的表中。

3  内存布局中,其父类布局依次按声明顺序排列。

4     每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

3  重复继承

所谓重复继承,也就是某个基类被间接地重复继承了多次。






我们可以看见,最顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:

 

D d;

d.ib = 0;               //二义性错误

d.B1::ib = 1;           //正确

d.B2::ib = 2;           //正确


注意,上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。

钻石型虚拟继承


图说明:


总结:

1 把B这个超类放在了最后,VC++有一个NULL分隔符把B和B1和B2的布局分开,图中忘记了画

2 顺序先是B1 ,然后是B2 ,接着是D ,而B这个超类的实例都放在最后的位置。

3 对于不同的编译器,函数处理的方式可能不同,有的将D::f这个函数,B,B1,B2这三个vptr中都有,而vc++的只将它放在

B的vptr中。编译器可以计算偏移量得出想要的结果









 


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值