std::shared_ptr
是 C++11 引入的一种智能指针,它提供了自动化的内存管理功能,以防止内存泄漏。它通过引用计数来管理动态分配的对象,当最后一个 std::shared_ptr
被销毁时,该对象的内存会自动释放。
下面我将分别解释 std::shared_ptr
的优点、缺点、特性和建议搭配的使用场景,并附上相关的代码示例。
1. 优点
- 自动内存管理:通过引用计数确保指针安全,避免内存泄漏。
- 强所有权共享:多个
shared_ptr
可以共享同一个对象,当最后一个shared_ptr
被销毁时,资源才会被释放。 - 线程安全:引用计数的修改是原子的,适合多线程环境。
代码示例:
#include <iostream>
#include <memory>
void demonstrate_shared_ptr() {
std::shared_ptr<int> sp1(new int(10)); // 创建一个 shared_ptr
std::shared_ptr<int> sp2 = sp1; // 复制 shared_ptr,引用计数+1
std::cout << "sp1 use count: " << sp1.use_count() << std::endl; // 输出:2
sp2.reset(); // 重置 sp2,引用计数-1
std::cout << "sp1 use count: " << sp1.use_count() << std::endl; // 输出:1
std::cout << "sp2 is null: " << std::boolalpha << (sp2 == nullptr) << std::endl; // 输出:true
}
int main() {
demonstrate_shared_ptr();
return 0;
}
2. 缺点
- 性能开销:引用计数的更新涉及到原子操作,带来一些性能开销,特别是在多线程环境中。
- 资源循环引用:如果两个或多个
shared_ptr
对象循环引用对方,会导致内存泄漏,因为引用计数永远不会归零。 - 额外内存开销:管理引用计数需要额外的内存。
代码示例:循环引用(避免方法见最后的场景)
#include <iostream>
#include <memory>
struct Node {
std::shared_ptr<Node> next;
~Node() {
std::cout << "Node destroyed" << std::endl;
}
};
void demonstrate_circular_reference() {
std::shared_ptr<Node> node1 = std::make_shared<Node>();
std::shared_ptr<Node> node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1;
// 这个例子中,Node 的析构函数不会被调用,导致内存泄漏
}
int main() {
demonstrate_circular_reference();
return 0;
}
3. 特性
- 引用计数:多个
shared_ptr
可以共享同一对象,引用计数会自动维护,引用计数为零时对象被删除。 - 线程安全:引用计数的增加和减少是线程安全的。
- 自定义删除器:可以指定自定义资源释放逻辑。
代码示例:
#include <iostream>
#include <memory>
struct Resource {
~Resource() {
std::cout << "Resource destroyed" << std::endl;
}
};
void custom_deleter(Resource* res) {
std::cout << "Custom delete for Resource" << std::endl;
delete res;
}
void demonstrate_custom_deleter() {
std::shared_ptr<Resource> sp(new Resource(), custom_deleter);
// sp goes out of scope here, invoking custom_deleter
}
int main() {
demonstrate_custom_deleter();
return 0;
}
4. 建议搭配使用场景
- 适合需要共享所有权的对象:如缓存、资源管理器等。
- 避免循环引用(搭配
std::weak_ptr
使用)。
代码示例:利用 std::weak_ptr
避免循环引用
#include <iostream>
#include <memory>
struct Node {
std::weak_ptr<Node> next; // 使用 weak_ptr 避免循环引用
~Node() {
std::cout << "Node destroyed" << std::endl;
}
};
void demonstrate_weak_ptr() {
std::shared_ptr<Node> node1 = std::make_shared<Node>();
std::shared_ptr<Node> node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // weak_ptr 不增加引用计数
// Node 的析构函数将被调用,因为没有循环引用
}
int main() {
demonstrate_weak_ptr();
return 0;
}
总结
- 优点:
shared_ptr
提供自动内存管理和线程安全的强所有权共享。 - 缺点:存在性能开销,可能导致循环引用问题,需注意防范。
- 特性:包括引用计数、线程安全和支持自定义删除器。
- 建议使用场景:适合需要共享所有权的场景,并且应在可能的循环引用情况下使用
std::weak_ptr
。