类的构造(2):对象的内存布局

来源:互联网 发布:手机上解压软件 编辑:程序博客网 时间:2024/06/01 20:24

一个类对象在内存中总占几个字节,哪些内容分别占多少字节呢? 我们举个例子来瞧下.

//由于存在啥内存对齐,所以你用sizeof去检查类的size时会不准确.所以这里使用个#pragma pack(1)显式指定内存对齐模式会使结果比较准确点.

//内存对齐详细解释见http://baike.baidu.com/view/4786260.htm

简单示例1:静态变量,函数不占用类对象内存

#pragma pack(1)

class human{

int ID;  //占4个字节

char ch; //占1字节

static int age; //占4字节

};

 

class father : public human{

void Test();

};

 

void main()

{

   cout<< sizeof human;   //大小为5,而不是你期望的9,因为static型的成员变量不属于类对象,它是放在静态存储区里面去了.

  cout<<sizeof father ; //大小也是5,把human中的内容原封不动的拷贝一份放到father里面来的.要是类比下嘛,跟头文件类似,引用头文件时实际上相当于把所有内容复制过来.

                                //当然只是类似,有时是原封不动拷,但有时只是选择性的拷贝的.

                                //我们看到函数是不占用类对象的内存的,函数是放到代码区.

}

 

示例2:虚函数的的特殊处理

假如类中有虚函数那么类对象中会额外隐式添加一个指针,指向一个虚拟函数表.

class human{

 int ID; //4字节

 char ch; //1字节

 virtual void Test();

}

 

cout<<sizeof human; //大小为9,这里假设是32位系统,一个指针占4字节.

 

你可能会问那么虚拟函数表是啥东东啊?怎么实现的啊.

具体怎么实现的我也不清楚,没看到过相关资料.不过我们可以从MFC构架的一些思想里猜想下.MFC中最爱干的事就是在类中整一堆宏嘛,来实现啥动态类型识别,动态创建之类的.实际上就是在类中声明一个static的结构体CRuntimeClass,把相关信息都放这结构体里.

我们这里来猜想下,类human中可能会这样写

class human{

int ID;

char ch;

virtual void Test();

static list<FunNode> funList; //这里就简化处理了,一个链表,每个节点是个函数指针.比如可以添加节点funList.add(Test);

list<FunNode>* vptr; //指向上面那个funList的指针.所以有虚函数时多出的4个字节内存就是它了.不管有多少个虚函数也只多它一个指针

}

 

那假如有类father继承类human会怎么样呢?

father会创建自己的虚函数表,只不过会把human中虚函数表内容拷过来.然后创建一个指针vptr指向自己创建的虚函数表.

 

示例3:虚拟继承

我们知道多重继承中,有个典型的缺陷就是会有歧义产生,而解决办法就是用虚拟继承.示例

非虚拟继承

class human{

public:

int ID; //4字节

char ch; //1字节

};

class father: public  human{

};

class mather: public human{

};

 

class son: public father, public mather{

};

 

cout<<sizeof human;  //5字节

cout<<sizeof father <<sizeof mather; //都是5字节

cout<<sizeof son; //10字节father与mather的和嘛

上面的内存布局很好理解,father和mather拷贝一份human的内容.而son又把father与mather的拷贝过来.

我们知道如果father与mather的继承中如果不加virutal这关键字的话,则这样使用会报错: Son so;

int num = so.ID; //因为son中有从father拷过的ID,也有从mather拷来的ID,编译器不知道去找哪个了.不过你可以这样显式的调用

int num = so.father::ID;//或者so.mather::ID;

 

虚拟继承

当然上面这样做很麻烦,所以有这样一个解决方法

class father: public virtual human{

};

class mather: public virtual human{

};

上面其他不变,只是加上virtaul关键字.

cout<<sizeof human; //仍为5

cout<<sizeof  father<<sizeof mather; //都为9

cout<<sizeof son<<为13

这个结果有点不可思议吧.

 

当使用虚拟继承之后,father除了拷贝human中的5个字节外还增加一个指针(4字节),指向human的指针.所以大小为9

而son是先从human中拷贝5字节过来,然后拷贝father中非human的部分4字节,拷贝mather中非human的部分4字节.所以加起来是13.

 

假如father中还另外定义个成员,比如int money; //则son也还拷过来.这样会在13基础上再加4.

反正记住的一点就是human的在father,mather中会拷去两份相同的内容.而son只会拷一份过来.当然为了保证这个操作额外的代价是多出些指针来了啊.

原创粉丝点击