关于 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 引用计数=2
node2->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_ptr
SafeNode() { 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_ptr
shared->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
:仅在需要共享所有权时传递,否则传递原始引用或指针。