介绍
因为c++只规定了 虚继承/ 虚函数/ 多继承/ 的行为, 但将实现方法留给编译器作者. 所以各个平台的实现并不相同, 得出的结果也不尽相同.
经测试, vs和gcc目前比较统一的情况只有2种 :
- 无继承+无虚函数
- 无继承+虚函数
故本文只讨论这2种, 以及了解虚函数和虚继承的含义.
关于虚函数
当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;
关于虚继承
当涉及到虚继承,会增加vbPtr指针指向虚基表vbTable
单继承对象模型
类的继承关系为:
class Derived : public Base
无继承但有虚函数示例
测试环境为Windows/VS, 32位.
1 | class A |
再测试一个复杂点的情况:
1 | struct MyStructA{}; |
总结
首先,平时所声明的类只是一种类型定义,它本身是没有大小可言的。 因此,如果用sizeof运算符对一个类型名操作,那得到的是具有该类型实体的大小。
计算一个类对象的大小时的规律:
空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);
一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;因此一个对象的大小≥所有非静态成员大小的总和;
这是为什么呢?
实际上,这是类结构体实例化的原因,空的类或结构体同样可以被实例化,如果定义对空的类或者结构体取sizeof()的值为0,那么该空的类或结构体实例化出很多实例时,在内存地址上就不能区分该类实例化出的实例,,,所以,为了实现每个实例在内存中都有一个独一无二的地址,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。
类对象的大小 =
各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和 +
虚函数表指针(多继承有几个含有虚函数的父类就有几个虚函数表指针)+虚基表指针(多继承下可能不止一个, 不赘述了, 虚继承的对象的内存布局,在不同编译器实现有所区别) +
编译器因为要内存对齐而额外增加的字节。
详情可参考: 陈皓的 C++ 虚函数表解析
测试常用平台的不同
测试了 Windows10 / VS2015 和 Ubuntu14.04.3 / gcc4.8.4 , 都是64位
测试有多继承的情况
1 |
|
打印对比如下 :
1 | A=1 |
1 | A=1 |
测试有虚拟继承的情况
1 |
|
打印对比如下 :
1 | Derived's size is 80 |
1 | Derived's size is 48 |
测试总结
这两个都还算是比较常用的平台了, 测试之后发现vs和gcc目前比较统一的情况只有2种 :
- 无继承+无虚函数
- 无继承+虚函数