文章目录
-
- 使用 std::shared_ptr 管理具备共享所有权的资源
-
- 引用计数机制
- 控制块
- this 指针问题与 std::enable_shared_from_this
- 自定义删除器
使用 std::shared_ptr 管理具备共享所有权的资源
std::shared_ptr 通过引用计数(reference count)机制实现共享所有权。没有特定的 shared_ptr 独占对象,所有指向同一对象的 shared_ptr 协作管理生命周期——当最后一个 shared_ptr` 不再指向该对象时,自动销毁资源。它结合了垃圾回收的便利性与析构函数的可预测性。
不支持数组
- 没有 std::shared_ptr<T[]> 的特化版本
- 没有 operator[]
- 虽可通过自定义删除器 delete[] 绕过编译,但这是糟糕的设计
- 替代方案:std::array、std::vector、std::string
—
引用计数机制
| 构造函数(拷贝) | 递增(+1) |
| 析构函数 | 递减(−1) |
| 拷贝赋值 sp1 = sp2 | sp1 原对象计数 −1,sp2 对象计数 +1 |
| 移动构造 / 移动赋值 | 不变(源指针置空,所有权转移) |
📌 移动操作更快:拷贝需要原子递增引用计数,移动则完全不需要修改计数。
:::color1 性能开销
:::
控制块
控制块包含:
- 引用计数(强引用)
- 弱计数(weak count,见 Item 20)
- 自定义删除器的拷贝(如果有)
- 自定义分配器的拷贝(如果有)
| 场景 | 是否创建新控制块 |
| std::make_shared | ✅ 总是创建 |
| 从 std::unique_ptr / std::auto_ptr 构造 | ✅ 总是创建 |
| 从裸指针构造 | ✅ 总是创建 |
| 从 std::shared_ptr 或 std::weak_ptr 构造 | ❌ 不创建(复用) |
auto pw = new Widget;
std::shared_ptr<Widget> spw1(pw, loggingDel); // 创建控制块 #1
std::shared_ptr<Widget> spw2(pw, loggingDel); // 创建控制块 #2 ❌
// *pw 会被销毁两次 → double free → 未定义行为!
// 方法一:直接传 new 的结果
std::shared_ptr<Widget> spw1(new Widget, loggingDel);
std::shared_ptr<Widget> spw2(spw1); // 复用 spw1 的控制块 ✅
// 方法二:使用 std::make_shared(无自定义删除器时优先)
auto sp = std::make_shared<Widget>();
this 指针问题与 std::enable_shared_from_this
std::vector<std::shared_ptr<Widget>> processedWidgets;
//如果外部已有 shared_ptr<Widget> 指向该对象,会导致多个控制块,引发 double free!
class Widget {
public:
void process() {
processedWidgets.emplace_back(this); // ❌ 用 this 裸指针创建新的控制块!
}
};
//📌 CRTP(奇异递归模板模式):派生类继承以自身为模板参数的基类模板。
class Widget : public std::enable_shared_from_this<Widget> {
public:
void process() {
processedWidgets.emplace_back(shared_from_this()); // ✅ 复用已有控制块
}
};
⚠️ 前提:调用 shared_from_this() 前,必须已存在至少一个 shared_ptr` 指向当前对象。
class Widget : public std::enable_shared_from_this<Widget> {
public:
template<typename... Ts>
static std::shared_ptr<Widget> create(Ts&&... params) {
return std::shared_ptr<Widget>(new Widget(std::forward<Ts>(params)...));
}
void process() { /* 使用 shared_from_this() */ }
private:
Widget() = default; // 构造函数私有,强制通过工厂创建
};
自定义删除器
auto loggingDel = [](Widget* pw) {
makeLogEntry(pw);
delete pw;
};
std::shared_ptr<Widget> spw(new Widget, loggingDel);
| 特性 | std::unique_ptr | std::shared_ptr |
| 删除器是否是类型的一部分 | ✅ 是 | ❌ 不是 |
| 不同删除器的对象类型是否相同 | ❌ 不同 | ✅ 相同 |
| 对象大小是否因删除器而改变 | ✅ 可能变大 | ❌ 始终两个指针大小 |
| 删除器存储位置 | 智能指针内部 | 控制块中(堆上) |
📌 灵活性优势:不同删除器的 shared_ptr` 类型相同,可放入同一容器、互相赋值。



