C++ weak_ptr
外观
C++ weak_ptr 是 C++ 标准库中提供的一种智能指针,用于解决共享所有权模型中的循环引用问题。它是 `std::shared_ptr` 的伴随指针,本身不增加引用计数,但可以安全地观察 `shared_ptr` 管理的对象是否存在。
介绍[编辑 | 编辑源代码]
`std::weak_ptr` 是一种不控制对象生命周期的智能指针,它指向一个由 `std::shared_ptr` 管理的对象。与 `shared_ptr` 不同,`weak_ptr` 不会增加对象的引用计数,因此不会阻止对象的销毁。其主要用途包括:
- 打破 `shared_ptr` 之间的循环引用
- 临时访问 `shared_ptr` 管理的对象(需通过 `lock()` 方法)
- 观察资源是否存在而不影响其生命周期
基本用法[编辑 | 编辑源代码]
创建 weak_ptr[编辑 | 编辑源代码]
`weak_ptr` 通常从 `shared_ptr` 构造或赋值:
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<int> shared = std::make_shared<int>(42);
std::weak_ptr<int> weak(shared); // 从 shared_ptr 构造
// 检查对象是否还存在
if (auto temp = weak.lock()) {
std::cout << "Value: " << *temp << "\n"; // 输出: Value: 42
} else {
std::cout << "Object already destroyed\n";
}
shared.reset(); // 释放所有权
if (weak.expired()) {
std::cout << "Object has been destroyed\n"; // 输出: Object has been destroyed
}
}
主要成员函数[编辑 | 编辑源代码]
- `lock()`: 返回一个 `shared_ptr`,若原对象存在则共享所有权,否则返回空
- `expired()`: 检查被观察对象是否已被释放
- `use_count()`: 返回共享该对象的 `shared_ptr` 数量
解决循环引用[编辑 | 编辑源代码]
循环引用是 `shared_ptr` 的常见问题,会导致内存泄漏。以下示例展示如何使用 `weak_ptr` 解决此问题:
问题示例[编辑 | 编辑源代码]
struct Node {
std::shared_ptr<Node> next;
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2; // node1 引用 node2
node2->next = node1; // node2 引用 node1 → 循环引用
// 退出作用域时,引用计数不为0,内存泄漏!
}
解决方案[编辑 | 编辑源代码]
将其中一个指针改为 `weak_ptr`:
struct Node {
std::weak_ptr<Node> next; // 改为 weak_ptr
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // 不会增加引用计数
// 退出作用域时对象会被正确销毁
// 输出: Node destroyed\nNode destroyed
}
实际应用场景[编辑 | 编辑源代码]
缓存系统[编辑 | 编辑源代码]
`weak_ptr` 适合实现对象缓存,当外部不再需要缓存项时允许自动清理:
class Cache {
std::unordered_map<int, std::weak_ptr<Resource>> cache;
public:
std::shared_ptr<Resource> get(int id) {
if (auto it = cache.find(id); it != cache.end()) {
if (auto res = it->second.lock()) {
return res; // 缓存命中
}
}
// 缓存未命中,创建新资源
auto res = std::make_shared<Resource>(id);
cache[id] = res;
return res;
}
};
观察者模式[编辑 | 编辑源代码]
观察者可以使用 `weak_ptr` 观察主体,避免影响主体生命周期:
class Subject {
std::vector<std::weak_ptr<Observer>> observers;
public:
void notify() {
for (auto& weak_obs : observers) {
if (auto obs = weak_obs.lock()) {
obs->update();
}
}
}
};
性能与安全考虑[编辑 | 编辑源代码]
- **性能**: `lock()` 操作包含原子操作,比裸指针访问稍慢
- **线程安全**: 与 `shared_ptr` 类似,`weak_ptr` 的基本操作是线程安全的
- **空指针**: 解引用 `weak_ptr` 前必须通过 `lock()` 获取 `shared_ptr`
与其它智能指针对比[编辑 | 编辑源代码]
指针类型 | 所有权 | 是否影响生命周期 | 主要用途 |
---|---|---|---|
`unique_ptr` | 独占所有权 | 是 | 单一所有者场景 |
`shared_ptr` | 共享所有权 | 是 | 多所有者场景 |
`weak_ptr` | 无所有权 | 否 | 解决循环引用/观察资源 |
高级主题[编辑 | 编辑源代码]
自定义删除器[编辑 | 编辑源代码]
`weak_ptr` 可以观察带有自定义删除器的 `shared_ptr`:
auto deleter = [](int* p) {
std::cout << "Custom delete\n";
delete p;
};
std::shared_ptr<int> shared(new int(10), deleter);
std::weak_ptr<int> weak(shared);
继承关系[编辑 | 编辑源代码]
`weak_ptr` 支持派生类到基类的转换:
struct Base {};
struct Derived : Base {};
std::shared_ptr<Derived> d = std::make_shared<Derived>();
std::weak_ptr<Base> b = d; // 隐式转换
常见问题[编辑 | 编辑源代码]
何时使用 weak_ptr[编辑 | 编辑源代码]
- 当需要观察对象但不应延长其生命周期时
- 解决可能产生循环引用的设计场景
- 实现缓存、观察者等模式
weak_ptr 能否单独使用[编辑 | 编辑源代码]
不能。`weak_ptr` 必须与 `shared_ptr` 配合使用,不能直接管理内存。
总结[编辑 | 编辑源代码]
`std::weak_ptr` 是 C++ 内存管理工具链中的重要组成部分,它:
- 解决了 `shared_ptr` 的循环引用问题
- 提供了安全观察共享对象的方式
- 不会增加引用计数,不影响对象生命周期
- 必须通过 `lock()` 方法访问实际对象
正确使用 `weak_ptr` 可以显著提高 C++ 程序的资源管理安全性和设计灵活性。