之前在本地有写过一篇很长的关于C++内存分布的文档(里面有很多调试例子),想一次性移到博客当中。但是排版起来非常麻烦,索性现在分情况来写吧。一来排版比较容易,二来同学们如果看到了我的博客,也不会觉得冗长。博客中如果有错误的地方,欢迎大家指正,我们共同进步。
现在来介绍一下我的环境:
1.系统:Ubuntu 12.04.2 LTS
2.编译器:gcc version 4.4.7 (Ubuntu/Linaro 4.4.7-1ubuntu2)
3.调试器:GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
后面的的文章也都是在这个环境的基础上测试的。后续的验证过程中会用的AT汇编,所以大家可以熟悉一下AT汇编的语法。
一、单继承
现在步入正题,我们来分析一下C++单继承的内存分布。这种情况是最简单的,我们要探究的是子类对象中包含的父类对象的数据到底是怎么分布的。这里我会一步步调试查看的。先附上一个继承关系图,放眼看去,真的是太简单了吧!!!
图1.类继承关系
源代码:
#include<iostream>
using namespace std;
class father{
private:
int c;
public:
int a;
int* get_c(void){return &c;}
};
class son :public father
{
public:
int d;
int e;
};
int main()
{
father *ba = new father();
son *er = new son();
cout <<"&er :"<<er<<endl;
cout <<"er.c:"<<er->get_c()<<endl;
cout <<"er.a:"<<&er->a<<endl;
cout <<"er.d:"<<&er->d<<endl;
cout <<"er.e:"<<&er->e<<endl;
cout <<"\n"<<endl;
cout << "sizeof(father):"<<sizeof(*ba)<<endl;
cout << "sizeof( -son-):"<<sizeof(*er)<<endl;
return 1;
}
输出结果:
er.c:0xac8030
er.a:0xac8034
er.d:0xac8038
er.e:0xac803c
sizeof(father):8
sizeof( -son-):16
从上面的打印结果可以看到在没有虚函数的单继承的情况下,对象的大小和成员变量有关系与方法没有关系。方法是该类所有对象共有的。针对我们的测试代码可以看到。父类成员变量:a、c。方法有get_c().但是我们打印的father对象大小为8个字节,即a,c成员变量各占有4字节的空间。子类成员变量:d、e、c、a,方法为继承父类的get_c().之前在网上听人说,子类是不继承父类的private成员的。但是这里我们的打印结果可以看出,子类是继承父类的private成员的,只是子类对象不可访问而已。
总结:在这种情况下,子类对象可以直接转换成父类对象,因为从子类对象的内存布局上看,上面是父类数据,下面是子类对象新添加的数据。转换的过程只是将子类对象指针赋给父类对象指针的过程。但是将父类对象转换成子类对象是很危险的,也是一般不被允许的。在android binder机制中获取本地代理对象时,拿到的就是最顶层父类IBinder的引用,其实这里就是子类对象的地址。
图2.子类内存分布
二、多继承
没有虚函数的多继承也是很简单的(不是菱形继承)。先来上类的继承关系图,有些同学可能会看到son怎么会继承uncle的东东呢。Uncle和father都继承于爷爷,也算是间接继承吧,哈哈....开个玩笑了。这里只是一个例子而已,不要见怪。
图3.多继承类图
上面的类图就像弹弓一样,看上去也是太简单了。代码是在上面的代码中添加了一个uncle类,修改如下:
class uncle{
private:
int cc;
public:
int aa;
int* get_cc(void){return &cc;}
};
class son :public father,public uncle{}
main函数中修改:
cout <<"er.a:"<<&er->a<<endl;
//+++++++++++++++++++++++
cout <<"er.get_cc:"<<er->get_cc()<<endl;
cout <<"er.aa:"<<&er->aa<<endl;
//+++++++++++++++++++++++
cout <<"er.d:"<<&er->d<<endl;
这样编译之后打印结果:
&er :0x1387030
er.c:0x1387030
er.a:0x1387034
er.get_cc:0x1387038
er.aa:0x138703c
er.d:0x1387040
er.e:0x1387044
sizeof(father):8
sizeof( -son-):24
上面我们惊奇的发现,子类er对象的大小变成了24个字节,这是由于对象内容多了uncle类的数据域。子类继承father和uncle类,所以可以看出子类对象数据的分布和声明的继承顺序有关,越靠近子类越在上面(class son :public father,public uncle)。内存如下表所示:
图4.多继承内存分布图