1. 智能指针的内存布局概述
C++ 中的智能指针如 std::shared_ptr
、std::unique_ptr
和 std::weak_ptr
,都是为了自动化内存管理,减少手动管理内存的负担。它们各自的内存布局在管理对象时有所不同,特别是涉及到多态和继承时,布局会变得更为复杂。
1.1 std::shared_ptr
的内存布局
std::shared_ptr
是引用计数智能指针,它在管理对象时会包含:
- 控制块指针:指向控制块,用于管理引用计数、弱引用计数等信息。
- 对象指针:指向实际的对象。
在 64 位系统上,std::shared_ptr
的内存布局大致如下:
控制块指针 (control block ) | 对象指针 (object pointer ) |
---|---|
8B | 8B |
- 控制块存储引用计数(强引用计数和弱引用计数)及其他管理信息。
- 对象指针指向实际管理的对象。
控制块的内存布局
控制块通常会包含以下部分:
强引用计数 | 弱引用计数 | 其他管理信息(可选) |
---|---|---|
4B | 4B | 可能有额外的信息 |
- 强引用计数:记录有多少个
shared_ptr
实例共享该对象。 - 弱引用计数:记录有多少个
weak_ptr
引用该对象。
1.2 std::unique_ptr
的内存布局
std::unique_ptr
是独占所有权智能指针,它仅包含一个指向实际对象的指针,而不涉及引用计数,因此内存布局非常简单:
对象指针 (object pointer ) |
---|
8B |
- 对象指针:指向实际管理的对象。
1.3 std::weak_ptr
的内存布局
std::weak_ptr
是观察者智能指针,它不增加引用计数,只是观察由 std::shared_ptr
管理的对象。weak_ptr
仅包含一个指向控制块的指针:
控制块指针 (control block ) |
---|
8B |
- 控制块指针:指向控制块,
weak_ptr
通过它来观察shared_ptr
管理的对象,但不会增加引用计数。
2. 继承结构中的智能指针
在有继承关系的情况下,智能指针依然遵循上述内存布局,但需要考虑多态性和对象的实际类型。假设我们有以下的继承结构:
class Base {
public:
virtual void func() {}
int a;
};
class Derived : public Base {
public:
void func() override {}
double b;
};
并且使用智能指针管理这些对象:
std::shared_ptr<Base> sptr = std::make_shared<Derived>();
std::weak_ptr<Base> wptr = sptr;
std::unique_ptr<Base> uptr = std::make_unique<Derived>();
2.1 std::shared_ptr<Base>
的内存布局
std::shared_ptr<Base>
会包含以下两个指针:
- 控制块指针:指向控制块,管理引用计数和弱引用计数等。
- 对象指针:指向
Derived
类型对象的指针(由于多态,实际对象类型是Derived
)。
内存布局如下:
控制块指针 (control block ) | 对象指针 (object pointer ) |
---|---|
8B | 8B |
2.2 std::weak_ptr<Base>
的内存布局
std::weak_ptr<Base>
只包含一个指向控制块的指针,它不会直接管理对象,也不会增加引用计数。
内存布局如下:
控制块指针 (control block ) |
---|
8B |
2.3 std::unique_ptr<Base>
的内存布局
std::unique_ptr<Base>
仅包含一个指向对象的指针,指向实际的 Derived
类型对象。
内存布局如下:
对象指针 (object pointer ) |
---|
8B |
3. 总结与对比
智能指针类型 | 内存布局 | 说明 |
---|---|---|
std::shared_ptr | 控制块指针 + 对象指针 | shared_ptr 存储两个指针:控制块指针和指向对象的指针。 |
std::unique_ptr | 对象指针 | unique_ptr 仅存储原始指针,指向唯一管理的对象。 |
std::weak_ptr | 控制块指针 | weak_ptr 仅存储一个指向控制块的指针,不增加引用计数。 |
控制块 | 强引用计数 + 弱引用计数 + 其他管理信息(可选) | 控制块存储引用计数、弱引用计数等信息。 |
std::shared_ptr
:存储两个指针,一个指向控制块(用于管理引用计数、弱引用计数等),一个指向实际的对象。适用于多个对象共享同一资源的场景。std::weak_ptr
:只存储一个指向控制块的指针,不增加引用计数。它用于观察由shared_ptr
管理的对象,解决循环引用问题。std::unique_ptr
:只存储原始指针,指向唯一管理的对象,确保资源独占,适用于需要单一所有权的场景。
4. 使用场景
std::shared_ptr
:适用于多个对象共享同一资源时,可以安全地增加和减少引用计数。std::weak_ptr
:通常用于解决循环引用问题,它不增加引用计数,只是观察一个对象,避免对象无法被销毁。std::unique_ptr
:用于独占资源的场景,确保一个对象只有一个所有者。