目录
在上一篇中,我们理解了类的定义和封装。类是对象的模板,而对象是类的实例。那么,一个对象在内存中究竟长什么样?它占据多大空间?本文将剖析类对象的存储模型和大小计算规则。
类的实例化:从蓝图到建筑
类描述了实体具有哪些属性和方法,但它本身不占用实际的物理存储。只有当类被实例化------即定义出具体的对象时,程序才会在内存中开辟空间存放该对象的成员变量。
一个类可以实例化出多个对象,每个对象拥有独立的成员变量副本。正如建筑蓝图可以造出许多功能相同但地址不同的房子。
cpp
class Stack {
int* _array;
size_t _capacity;
size_t _top;
};
// 实例化两个对象,各占独立内存
Stack st1;
Stack st2;
对象大小的计算与存储模型
一个关键问题是:成员函数会占据对象空间吗? 答案是否定的。C++编译器会将类的成员函数存放在公共代码段(只读区域),所有对象共享同一份函数实现。对象本身只需要存储属于自己独有的数据,即成员变量。
因此,计算一个对象的大小时,只需要考虑其成员变量所占空间,并遵循与C语言结构体相同的内存对齐规则。
特殊情况:空类。如果一个类既没有成员变量也没有虚函数(虚函数会引入虚表指针),它的大小并不是0,而是1字节。这1字节不存储任何实际数据,仅为了在内存中唯一标识该对象,使不同对象的地址不同。
cpp
class A1 {}; // sizeof(A1) == 1
class A2 {
public:
A2() {}
}; // sizeof(A2) == 1 (成员函数不占对象空间)
结构体内存对齐规则
对象大小并不是成员变量大小的简单累加,编译器会进行内存对齐以提高存取效率。主流平台默认对齐数通常为8(VS中即为8)。规则如下(与C语言struct完全一致):
- 第一个成员:放在与对象起始地址偏移量为0的位置。
- 其他成员:每个成员要对齐到某个地址,该地址必须是
对齐数 的整数倍。
对齐数 =min(编译器默认对齐数, 该成员自身大小)。 - 整体大小:对象的总大小必须是最大对齐数的整数倍。
- 嵌套结构体:如果类中包含另一个类的对象作为成员,该成员对齐到自身内部最大对齐数的整数倍位置,整个类的大小也要考虑该子对象的最大对齐数。
示例分析:
cpp
class Example {
char c; // 大小1,对齐数 min(8,1)=1,偏移0
int i; // 大小4,对齐数 min(8,4)=4,下次对齐到偏移4,c后填充3字节
short s; // 大小2,对齐数 min(8,2)=2,当前偏移8,满足对齐,占2字节,总10
}; // 最大对齐数为4,总大小需为4的倍数,末尾填充2字节 => 12
掌握对象内存模型,是我们理解this指针传参、深浅拷贝以及继承多态的物理基础。下一篇将聚焦那个隐藏在幕后却至关重要的角色------this指针。