shared_ptr管理动态数组必须用自定义删除器,否则delete与delete[]不匹配导致未定义行为;c++17支持shared_ptr自动调用delete[],但需显式声明类型并配合适配删除器。

shared_ptr管理动态数组必须用自定义删除器
默认 shared_ptr 构造函数调用 delete,而 new[] 分配的内存必须用 delete[] 释放,否则行为未定义——常见表现是析构时崩溃、内存泄漏或静默损坏。C++17 虽引入了 std::make_shared<t>(n)</t>,但它只支持默认构造元素,不适用于需要带参构造或非 trivial 类型的场景,此时仍得手写删除器。
怎么写一个安全的数组删除器
删除器必须是可调用对象(函数指针、lambda 或仿函数),且接受单个 T* 参数。关键点在于:它不能捕获外部变量(避免生命周期依赖),类型要能被 shared_ptr 正确推导或显式指定。
- 推荐用 lambda:简洁、无状态、编译期内联
- 不要用普通函数指针去传
delete[]——delete[]不是函数,不能取地址 - 若用仿函数类,需确保其析构函数不抛异常,且满足
CopyConstructible
正确示例:
auto deleter = [](int* p) { delete[] p; };
std::shared_ptr<int> arr(new int[10], deleter);
</int>
shared_ptr 的类型声明和访问细节
从 C++17 开始,shared_ptr<t></t> 是合法特化,它会自动选用 delete[],但前提是构造时没传自定义删除器;一旦传了,就完全由你负责。这个类型影响解引用行为:
-
shared_ptr<int></int>支持ptr[i]下标访问(重载了operator[]) -
shared_ptr<int></int>(即使指向数组)不支持[],只能用get()[i] - 类型不匹配会导致编译失败,比如把
shared_ptr<int></int>赋给shared_ptr<int></int>
所以更稳妥的写法是显式声明数组类型:
std::shared_ptr<int> arr(new int[5], [](int* p) { delete[] p; });
arr[0] = 42; // 合法
</int>
容易忽略的坑:异常安全与所有权转移
构造 shared_ptr 时,如果 new[] 成功但删除器构造失败(比如 lambda 捕获了已销毁对象),会导致内存泄漏。因此删除器必须是无状态的。
- 禁止在删除器里抛异常——
shared_ptr析构期间若抛异常,程序直接终止 - 用
std::move转移数组shared_ptr时,删除器也会被移动(若为可移动类型),但 lambda 默认不可移动,所以用值捕获或不捕获即可 - 不要混用
shared_ptr<t></t>和shared_ptr<t></t>指向同一块内存,类型系统不会帮你检查
最易错的是忘记类型后缀:写成 shared_ptr<int></int> 却管理数组,编译不报错,运行时才出事。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











