C++ 的智能指针(Smart Pointer) 是一类模板类,用来自动管理动态分配的内存(通常是 new 得到的对象)。它的核心思想是:
通过 RAII(Resource Acquisition Is Initialization) 在对象生命周期结束时自动释放资源,避免内存泄漏和悬空指针。
C++11 之后,标准库在 <memory> 中提供了三种主要智能指针:
std::unique_ptrstd::shared_ptrstd::weak_ptr
1. std::unique_ptr(独占所有权)
unique_ptr 表示独占所有权:一个对象只能被一个 unique_ptr 拥有。
基本特点
- 不允许拷贝
- 允许移动
- 生命周期结束时自动
delete
典型用途
unique_ptr 是默认首选的智能指针。
适用于:
- 一个对象只有一个 owner
- RAII 资源管理
- Pimpl 模式
- AST / IR 结构
例如:
class Node {
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
};2. std::shared_ptr(共享所有权)
shared_ptr 允许多个指针共享同一个对象。
内部使用:引用计数(reference count),当引用计数变为 0 时对象被释放。
内存结构
shared_ptr 实际上包含:
shared_ptr
|
v
+-------------------+
| control block |
| |
| ref count |
| weak count |
| deleter |
+-------------------+
|
v
object
典型用途
适用于:
- 多个对象需要共享同一个资源
- 生命周期难以确定
3. std::weak_ptr(弱引用)
weak_ptr 用来解决 shared_ptr 的循环引用问题。
- 不增加引用计数
- 只是观察对象
为什么需要 weak_ptr
看一个经典 bug:
struct B;
struct A {
std::shared_ptr<B> b;
};
struct B {
std::shared_ptr<A> a;
};创建:
A -> B
^ |
| v
+----+
引用计数:
A:1
B:1
但两者互相持有:
A.b -> B
B.a -> A
即使外部释放:
A:1
B:1
对象永远不会释放 → 内存泄漏
解决方法
- 使用
weak_ptr
struct B;
struct A {
std::shared_ptr<B> b;
};
struct B {
std::weak_ptr<A> a;
};这样
A:1
B:1
weak_ptr 不增加引用计数。
- 使用
lock()
weak_ptr 不能直接访问对象,需要:
std::shared_ptr<A> p = weak.lock();示例
std::weak_ptr<int> w;
{
auto sp = std::make_shared<int>(10);
w = sp;
}
if (auto sp = w.lock()) {
std::cout << *sp;
} else {
std::cout << "object destroyed";
}4. 三种智能指针对比
| 特性 | unique_ptr | shared_ptr | weak_ptr |
|---|---|---|---|
| 所有权 | 独占 | 共享 | 无 |
| 引用计数 | ❌ | ✅ | 不增加 |
| 可拷贝 | ❌ | ✅ | ✅ |
| 可移动 | ✅ | ✅ | ✅ |
| 解决循环引用 | ❌ | ❌ | ✅ |
5. 推荐使用规则(非常重要)
C++ 社区普遍建议:
- 默认使用
unique_ptr
原因:
- 零开销
- 没有引用计数
- 语义清晰
- 需要共享时使用
shared_ptr
例如:GUI tree, graph, observer pattern
- 出现循环引用时使用
weak_ptr
例如:
- parent-child graph
- observer pattern
- cache
6. make_unique vs new
推荐:
auto p = std::make_unique<T>();
auto p = std::make_shared<T>();而不是:
std::unique_ptr<T> p(new T);原因:
- 异常安全
- 代码更简洁
make_shared减少一次内存分配