1. 类对象的内存布局
在 C++ 中,一个类的对象在内存中的布局取决于类的成员变量类型、继承关系、以及是否涉及虚函数等因素。类对象的内存分布通常分为以下几个部分:
- 非静态成员变量:类的每个实例都会拥有这些成员变量,它们占据对象的实际内存空间。
- 虚函数表指针(vptr):如果类包含虚函数,编译器会为该类生成虚函数表(vtable)和一个指向该虚函数表的指针(vptr),指针通常是类对象的第一个成员。
- 继承:当类继承自基类时,派生类对象通常会包括基类的成员数据。
1.1 单一类对象的内存布局
假设我们有一个简单的类:
class MyClass {
public:
int a;
double b;
};
在 64 位系统中,MyClass
对象的内存布局可能如下:
成员名 | 类型 | 大小 | 说明 |
---|---|---|---|
a | int | 4B | 整数类型,通常占用 4 字节 |
b | double | 8B | 双精度浮点数,通常占用 8 字节 |
内存对齐:为了满足 CPU 对内存访问的效率要求,编译器通常会对数据进行对齐。在 64 位系统上,double
通常会要求按 8 字节对齐。因此,可能会有一些填充字节,确保内存对齐。
内存布局(假设没有填充字节的情况下):
a (4B) | b (8B) |
---|---|
4B | 8B |
实际内存可能会有填充(例如 4 字节填充),以满足对齐要求。
1.2 带有虚函数的类对象
如果类中有虚函数,编译器会为该类生成一个虚函数表(vtable),并在每个对象中包含一个指向该虚函数表的指针(vptr)。例如:
class MyClass {
public:
virtual void foo() {}
int a;
};
在该情况下,类的对象的内存布局可能如下:
vptr 指针 (8B) | a (4B) |
---|---|
8B | 4B |
- vptr:指向虚函数表的指针,通常是对象内存的第一个部分。每个包含虚函数的类都会有一个虚函数表(vtable),虚函数表存储指向该类虚函数的指针。
a
:类的成员变量。
2. 类的嵌套结构的内存布局
类对象的内存布局不仅受自身成员变量的影响,还会受到嵌套类(成员类)影响。如果一个类中定义了另一个类作为成员,外部类的内存布局将包含嵌套类的对象。
2.1 简单嵌套类
假设有一个类 Outer
,它包含一个类 Inner
作为成员:
class Outer {
public:
int a;
class Inner {
public:
double b;
};
Inner inner;
};
在这个例子中,Outer
类包含 int
类型的成员 a
和 Inner
类型的成员 inner
。其中,Inner
类有一个 double
类型的成员 b
。
内存布局:
a (4B) | inner (嵌套类) |
---|---|
4B | b (8B) |
a
:外部类的成员,类型为int
,占 4 字节。inner
:嵌套类的对象,占 8 字节(包括double
类型的成员b
)。
3. 继承类对象的内存布局
继承关系中的类对象布局比单一类和嵌套类更加复杂。继承涉及到基类的成员数据和派生类的成员数据,此外,还可能涉及虚函数表(vtable)。
3.1 没有虚函数的继承
如果没有虚函数,派生类对象的内存布局通常包括基类的成员变量和派生类的成员变量。假设有如下的基类和派生类:
class Base {
public:
int a;
};
class Derived : public Base {
public:
double b;
};
Derived
类对象的内存布局将包括 Base
类的成员 a
和 Derived
类的成员 b
:
a (4B) | b (8B) |
---|---|
4B | 8B |
a
:基类的成员,类型为int
,占 4 字节。b
:派生类的成员,类型为double
,占 8 字节。
3.2 有虚函数的继承
如果基类或派生类中有虚函数,则每个类的对象都会包含一个指向虚函数表的指针(vptr)。假设我们有如下的基类和派生类:
class Base {
public:
virtual void foo() {}
int a;
};
class Derived : public Base {
public:
double b;
};
Derived
类的对象内存布局会更复杂,包含基类的 vptr
和成员变量,以及派生类的成员变量。内存布局可能如下:
vptr (8B) | a (4B) | b (8B) |
---|---|---|
8B | 4B | 8B |
- vptr:指向虚函数表的指针,通常是对象内存的第一个部分,基类的虚函数会放在虚函数表中,派生类会继承或覆盖它。
a
:基类的成员,类型为int
,占 4 字节。b
:派生类的成员,类型为double
,占 8 字节。
4. 智能指针管理的类对象的内存布局
智能指针(如 std::shared_ptr
、std::unique_ptr
和 std::weak_ptr
)的内存布局会影响类对象的内存分配方式,尤其是在管理继承对象时。
std::shared_ptr
:存储指向控制块的指针和对象指针。控制块包含引用计数和弱引用计数等信息。std::unique_ptr
:仅存储指向对象的原始指针,确保对象唯一所有权。std::weak_ptr
:仅存储指向控制块的指针,不增加引用计数。
5. 总结
情况 | 内存布局 |
---|---|
单一类对象 | 类的成员变量(按类型和内存对齐规则排列) |
带有虚函数的类 | vptr (虚函数表指针) + 类的成员变量 |
类嵌套结构 | 外部类的成员 + 嵌套类对象的成员 |
继承类对象 | 基类成员 + 派生类成员 + 虚函数表指针(如有虚函数) |
智能指针 | std::shared_ptr :控制块指针 + 对象指针;std::weak_ptr :控制块指针;std::unique_ptr :对象指针 |