std::shared_ptr是C++11引入的智能指针,通过引用计数自动管理内存,防止泄漏;支持多实例共享对象所有权,引用计数为0时自动释放资源。使用make_shared创建更安全高效,避免裸指针直接构造。引用计数操作线程安全,但对象访问需额外同步。循环引用会导致内存泄漏,可用std::weak_ptr打破循环。支持自定义删除器处理文件、数组等特殊资源。建议优先使用make_shared,避免重复释放和异常风险,结合unique_ptr与weak_ptr构建安全资源管理体系。

在C++开发中,内存管理是核心问题之一。手动管理内存容易导致内存泄漏、重复释放或悬空指针等问题。为了解决这些问题,C++11引入了智能指针,其中 std::shared_ptr 是最常用的智能指针之一,它通过引用计数机制自动管理对象的生命周期,有效避免内存泄漏。
什么是 shared_ptr?
std::shared_ptr 是一个模板类,用于共享某个堆上对象的所有权。多个 shared_ptr 实例可以指向同一个对象,内部维护一个引用计数。每当有新的 shared_ptr 指向该对象时,引用计数加1;当某个 shared_ptr 被销毁或重新赋值时,引用计数减1。当引用计数变为0时,表示没有指针再使用该对象,系统会自动调用其析构函数并释放内存。
这种机制确保了资源在不再被任何所有者使用时才被释放,从而防止内存泄漏。
基本用法与创建方式
使用 shared_ptr 前需包含头文件:
立即学习“C++免费学习笔记(深入)”;
#include推荐使用 std::make_shared 创建 shared_ptr,这是最安全和高效的方式:
auto ptr1 = std::make_sharedauto ptr2 = ptr1; // 引用计数变为2
也可以用普通构造函数绑定已有的指针,但应尽量避免直接传入裸指针,以防异常导致泄漏:
int* raw = new int(10);std::shared_ptr
更安全的做法始终是优先使用 make_shared,它能保证原子性地分配对象和控制块,性能更好且异常安全。
引用计数与线程安全
shared_ptr 的引用计数操作是线程安全的:多个线程同时拷贝或销毁不同的 shared_ptr 实例(指向同一对象)不会导致数据竞争。但注意,这仅指控制块的引用计数本身安全,并不保护所指向对象的数据同步。
举例说明:
std::shared_ptr globalPtr = std::make_shared();// 线程1
auto p1 = globalPtr; // 安全:增加引用计数
// 线程2
auto p2 = globalPtr; // 安全:同样增加引用计数
虽然引用计数安全,但若多个线程通过 p1、p2 同时修改 *p1 或 *p2 所指内容,仍需额外同步机制(如互斥锁)保护对象本身。
循环引用问题与 weak_ptr 解决方案
使用 shared_ptr 最常见的陷阱是循环引用:两个对象互相持有对方的 shared_ptr,导致引用计数永远无法归零,造成内存泄漏。
例如:
struct Node {std::shared_ptr
std::shared_ptr
};
auto a = std::make_shared
auto b = std::make_shared
a->child = b;
b->parent = a; // 循环形成:a 和 b 的引用计数都为2
当 a 和 b 离开作用域时,它们的引用计数从2降到1,但由于彼此持有,无法继续下降到0,内存不会释放。
解决办法是使用 std::weak_ptr 打破循环。它不增加引用计数,仅观察对象是否存在:
struct Node {std::shared_ptr
std::weak_ptr
};
访问 weak_ptr 时需先检查对象是否还存在:
if (auto p = b->parent.lock()) {// p 是 shared_ptr,可安全使用
}
这样,当外部指针释放后,对象能被正确回收。
自定义删除器
有时需要释放非标准资源,比如文件句柄、C风格数组或调用特定清理函数。shared_ptr 支持传入自定义删除器:
void close_file(FILE* f) {if (f) fclose(f);
}
auto fp = std::shared_ptr
当 fp 被销毁时,会自动调用 close_file 函数关闭文件。删除器也可为 lambda 表达式,灵活适配各种场景。
对于动态数组,虽然 C++ 推荐使用 std::vector 或 std::array,但如果必须使用原始数组,可通过删除器指定 delete[]:
auto arr = std::shared_ptr不过更现代的写法仍是优先选择容器类型。
常见使用建议
- 优先使用 std::make_shared 创建对象,避免多次内存分配和异常风险。
- 不要将同一个裸指针多次交给不同的 shared_ptr,会导致重复释放。
- 避免在函数参数中直接调用 make_shared,以防异常导致临时对象未被保存。
- 在可能形成循环引用的场景中,使用 weak_ptr 打破强依赖。
- 理解引用计数的开销,在高性能或频繁复制场景中评估是否适合使用。
基本上就这些。合理使用 shared_ptr 可显著提升代码的安全性和可维护性,是现代 C++ 内存管理的重要工具。配合 unique_ptr 和 weak_ptr,能够构建清晰、低风险的资源管理体系。不复杂但容易忽略的是循环引用和删除器细节,多加留意即可避免大部分问题。










