智能指针 shared_ptr 详解

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 是推荐的方式,具有以下优点:

  1. 提高性能:分配内存时只需要一次操作(对象和控制块在一起)。
  2. 安全性:避免了显式使用裸指针可能带来的问题。
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. 实践建议

  1. 优先使用 std::make_shared
  2. 慎用裸指针shared_ptr.get() 只在特殊情况下使用。
  3. 避免循环引用:合理使用 std::weak_ptr
  4. 适合共享所有权的场景,否则考虑使用 std::unique_ptr