关于 shared_ptr
和 weak_ptr
的详细介绍及使用示例:
1. shared_ptr
(共享所有权智能指针)
核心特性
- 引用计数:记录当前有多少个
shared_ptr
共享同一个对象。 - 自动释放:当引用计数归零时,自动释放对象内存。
- 线程安全:引用计数的增减是原子操作(但对象本身的访问需自行同步)。
基本用法
#include <memory>
#include <iostream>class MyClass {
public:MyClass() { std::cout << "MyClass 构造\n"; }~MyClass() { std::cout << "MyClass 析构\n"; }void print() { std::cout << "Hello\n"; }
};int main() {// 创建 shared_ptr(推荐使用 make_shared)std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();// 共享所有权(引用计数+1)std::shared_ptr<MyClass> ptr2 = ptr1;// 使用 -> 操作符访问成员ptr1->print(); // 输出: Hello// 引用计数查看(调试用)std::cout << "引用计数: " << ptr1.use_count() << std::endl; // 输出: 2// ptr1 和 ptr2 离开作用域,引用计数归零,对象自动析构return 0;
}
输出结果
MyClass 构造
Hello
引用计数: 2
MyClass 析构
2. weak_ptr
(弱引用智能指针)
核心特性
- 不增加引用计数:仅观察对象,不影响其生命周期。
- 需转换为
shared_ptr
:通过lock()
获取临时shared_ptr
来访问对象。 - 解决循环引用:打破
shared_ptr
的循环依赖,避免内存泄漏。
3. 循环引用问题与 weak_ptr
解决方案
循环引用示例
#include <memory>
#include <iostream>
class Node {
public:std::shared_ptr<Node> next;Node() { std::cout << "Node 构造\n"; }~Node() { std::cout << "Node 析构\n"; }
};int main() {auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2; // node2 引用计数=2node2->next = node1; // node1 引用计数=2// 退出作用域后,node1/node2 引用计数=1,无法释放!return 0;
}
输出结果(内存泄漏)
Node 构造
Node 构造
使用 weak_ptr
解决循环引用
#include <memory>
#include <iostream>
class SafeNode {
public:std::weak_ptr<SafeNode> next; // 使用 weak_ptrSafeNode() { std::cout << "SafeNode 构造\n"; }~SafeNode() { std::cout << "SafeNode 析构\n"; }
};int main() {auto node1 = std::make_shared<SafeNode>();auto node2 = std::make_shared<SafeNode>();node1->next = node2;node2->next = node1;// 退出作用域后,引用计数归零,正确析构return 0;
}
输出结果
SafeNode 构造
SafeNode 构造
SafeNode 析构
SafeNode 析构
4. weak_ptr
的典型使用场景
(1) 访问共享对象前检查存活状态
#include <memory>
#include <iostream>class MyClass {
public:MyClass() { std::cout << "MyClass 构造\n"; }~MyClass() { std::cout << "MyClass 析构\n"; }void print() { std::cout << "Hello\n"; }
};void checkObject(std::weak_ptr<MyClass> weak) {if (auto shared = weak.lock()) { // 转换为 shared_ptrshared->print();} else {std::cout << "对象已被释放\n";}
}int main() {std::weak_ptr<MyClass> weak;{auto shared = std::make_shared<MyClass>();weak = shared;checkObject(weak); // 输出: Hello}checkObject(weak); // 输出: 对象已被释放return 0;
}
输出结果
MyClass 构造
Hello
MyClass 析构
对象已被释放
(2) 观察者模式(缓存)
class DataCache {std::weak_ptr<MyClass> cachedData;
public:void updateCache(std::shared_ptr<MyClass> data) {cachedData = data;}void useCache() {if (auto data = cachedData.lock()) {data->print();} else {std::cout << "缓存无效\n";}}
};
5. shared_ptr
与 weak_ptr
操作总结
操作 | shared_ptr | weak_ptr |
---|---|---|
所有权 | 拥有对象所有权 | 仅观察对象 |
引用计数影响 | 增加 | 不影响 |
访问对象 | 直接通过 -> 或 * | 需调用 lock() 获取 shared_ptr |
检查有效性 | if (ptr) | if (weak.expired()) 或 lock() |
6. 最佳实践
- 优先使用
make_shared
:更高效(单次内存分配对象+控制块)。 - 避免循环引用:成员指针优先考虑
weak_ptr
。 - 不要用
new
初始化:直接传递裸指针可能导致多次释放。 - 谨慎传递
shared_ptr
:仅在需要共享所有权时传递,否则传递原始引用或指针。