std::shared_ptr
是 C++ 11 引入的智能指针之一,用于管理动态分配的内存,其核心功能是通过引用计数机制实现共享所有权。
1. 概述
std::shared_ptr
是一个模板类,用于管理动态分配对象的生命周期。当最后一个 shared_ptr
对象销毁时,所管理的对象会被自动释放。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sp1 = std::make_shared<int>(10); // 创建 shared_ptr
std::cout << *sp1 << std::endl;
{
std::shared_ptr<int> sp2 = sp1; // 引用计数增加
std::cout << "Use count: " << sp1.use_count() << std::endl; // 输出 2
} // sp2 作用域结束,引用计数减少
std::cout << "Use count: " << sp1.use_count() << std::endl; // 输出 1
return 0;
}
2. 内部机制
- 引用计数:
std::shared_ptr
内部维护一个引用计数(use_count
),表示当前有多少个shared_ptr
对象共享同一个资源。 - 当引用计数变为
0
时,std::shared_ptr
会自动删除所管理的对象。
3. 主要功能
(1)构造函数
- 默认构造:创建空的
shared_ptr
。 - 拷贝构造:共享同一对象,引用计数加
1
。 - 移动构造:将资源从另一个
shared_ptr
移动过来。 - 自定义删除器:可以通过删除器指定如何销毁资源。
std::shared_ptr<int> sp1 = std::make_shared<int>(42); // 推荐
std::shared_ptr<int> sp2(sp1); // 拷贝构造,引用计数 +1
// 自动逸删除器
std::shared_ptr<int> sp3(new int(100), [](int* p){
std::cout << "Custom deleter called" << std::endl;
delete p;
});
(2)成员函数
use_count()
:返回当前引用计数。reset()
:释放当前持有的资源,引用计数减 1.get()
:返回裸指针(不推荐直接使用)。unique()
:判断是否是唯一拥有者。swap()
:交换两个shared_ptr
的内容。
std::shared_ptr<int> sp1 = std::make_shared<int>(50);
std::cout << sp1.use_count() << std::endl; // 输出 1
sp1.reset(); // 释放资源
if(!sp1) {
std::cout << "sp1 is empty" << endl;
}
(3)运算符重载
operator*
和operator->
:方便访问所管理对象。- 赋值运算符:支持拷贝赋值和移动赋值。
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::cout << *sp << std::endl; // 解引用输出 10
4. 与 std::make_shared
配合
std::make_shared
是推荐的方式,具有以下优点:
- 提高性能:分配内存时只需要一次操作(对象和控制块在一起)。
- 安全性:避免了显式使用裸指针可能带来的问题。
auto sp = std::make_shared<int>(10); // 推荐方式
5. 常见问题
(1)循环引用
当两个 shared_ptr
对象互相引用时,会导致内存泄漏,因为引用计数永远不会归零。
解决方法:
- 使用
std::weak_ptr
打破循环引用。 - 示例:
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 避免循环引用
};
(2)不必要的拷贝
避免在函数参数中传递 std::shared_ptr
。如果需要共享所有权,传递 std::shared_ptr
的拷贝;如果仅访问,传递 const std::shared_ptr&
。
6. 优缺点
优点
- 自动管理资源,避免手动释放内存。
- 适合共享所有权的场景。
- 支持自定义删除器。
缺点
- 增加了内存开销:需要维护引用计数。
- 在多线程环境中使用时,可能导致性能问题(引用计数是线程安全的)。
7. 实践建议
- 优先使用
std::make_shared
。 - 慎用裸指针:
shared_ptr.get()
只在特殊情况下使用。 - 避免循环引用:合理使用
std::weak_ptr
。 - 适合共享所有权的场景,否则考虑使用
std::unique_ptr
。