文章目录优先使用 std::make_unique 和 std::make_shared而非直接使用 new背景知识使用 make 函数的优势不适合使用 make 函数的情况无法使用 make 时的异常安全方案优先使用 std::make_unique 和 std::make_shared而非直接使用 new核心要点要点1优先使用 make 函数消除代码重复、提高异常安全、对 shared_ptr 生成更小更快的代码2不能使用 make 的场景需要自定义删除器、需要使用花括号初始化3shared_ptr 额外限制类有自定义operator new/delete大对象且weak_ptr存活时间长于shared_ptr4如果必须用new在独立语句中创建智能指针并用std::move传递给函数背景知识std::make_shared在C11引入std::make_unique直到C14才成为标准templatetypenameT,typename...Tsstd::unique_ptrTmake_unique(Ts...params){returnstd::unique_ptrT(newT(std::forwardTs(params)...));}⚠️**注意不要将自制版本放入std命名空间以免升级到 C14 后与标准库版本冲突。**:::info三个make 函数std::make_uniqueC14std::make_sharedC11std::allocate_shared— 与make_shared类似尽量用make_shared但第一个参数是 allocator 对象。:::使用 make 函数的优势1️⃣避免代码重复autoupw1(std::make_uniqueWidget());// Widget 出现 1 次std::unique_ptrWidgetupw2(newWidget);// Widget 出现 2 次 ❌autospw1(std::make_sharedWidget());// Widget 出现 1 次std::shared_ptrWidgetspw2(newWidget);// Widget 出现 2 次 ❌2️⃣异常安全processWidget(std::shared_ptrWidget(newWidget),// 潜在资源泄漏computePriority());:::color4编译器的执行顺序可能是执行new Widget执行computePriority()执行shared_ptr构造函数如果第 2 步抛出异常第 1 步 new 出来的 Widget 就永远不会被 shared_ptr 管理造成内存泄漏:::processWidget(std::make_sharedWidget(),// 没有资源泄漏computePriority());3️⃣** 效率提升仅限std::make_shared/std::allocate_shared**:::color5直接使用new需要两次内存分配一次为对象new Widget一次为控制块shared_ptr构造函数内部分配::::::color1使用std::make_shared只需一次内存分配分配一整块内存同时容纳对象和控制块:::优势说明✅ 代码体积更小只含一次内存分配调用✅ 执行速度更快内存只分配一次✅ 内存占用更少减少控制块中某些簿记信息不适合使用 make 函数的情况1️⃣需要指定自定义删除器❌autowidgetDeleter[](Widget*pw){/* ... */};// 只能用 newstd::unique_ptrWidget,decltype(widgetDeleter)upw(newWidget,widgetDeleter);std::shared_ptrWidgetspw(newWidget,widgetDeleter);2️⃣需要使用花括号初始化❌autoupvstd::make_uniquestd::vectorint(10,20);// 结果10 个元素每个值为 20而非两个元素 {10, 20}autoinitList{10,20};// 先创建 std::initializer_listautospvstd::make_sharedstd::vectorint(initList);3️⃣类有自定义 operator new/delete❌仅 shared_ptr自定义的operator new/delete通常只处理精确sizeof(类型)大小的内存。但std::make_shared需要分配对象大小 控制块大小**与类自身的自定义内存管理不兼容。4️⃣大对象 weak_ptr 生命周期长❌仅 shared_ptr使用make_shared时对象和控制块在同一块内存中只有当最后一个shared_ptr和最后一个weak_ptr都销毁后整个内存块才会释放make_shared 最后一个 shared_ptr 销毁 → 对象析构但内存未释放 最后一个 weak_ptr 销毁 → 整块内存才释放 直接 new 最后一个 shared_ptr 销毁 → 对象析构 对象内存释放 ✅ 最后一个 weak_ptr 销毁 → 只有控制块内存释放classReallyBigType{/* 很大的对象 */};// ❌ 大对象内存延迟释放autopBigObjstd::make_sharedReallyBigType();// ... 即使 shared_ptr 全部销毁只要 weak_ptr 还在内存就不释放// ✅ 大对象内存及时释放std::shared_ptrReallyBigTypepBigObj(newReallyBigType);// 最后一个 shared_ptr 销毁 → 内存立即释放无法使用 make 时的异常安全方案如果必须直接使用new例如需要自定义删除器应将new 的结果在一条独立语句中立即传给智能指针构造函数// ❌ 非异常安全processWidget(std::shared_ptrWidget(newWidget,cusDel),computePriority());// ✅ 异常安全std::shared_ptrWidgetspw(newWidget,cusDel);processWidget(std::move(spw),computePriority());// move 避免引用计数原子递增std::move优化原理传右值时用移动构造无引用计数操作传左值时用拷贝构造需原子递增引用计数。
智能指针——优先使用 std::make_unique 和 std::make_shared,而非直接使用 new
发布时间:2026/6/10 1:49:53
文章目录优先使用 std::make_unique 和 std::make_shared而非直接使用 new背景知识使用 make 函数的优势不适合使用 make 函数的情况无法使用 make 时的异常安全方案优先使用 std::make_unique 和 std::make_shared而非直接使用 new核心要点要点1优先使用 make 函数消除代码重复、提高异常安全、对 shared_ptr 生成更小更快的代码2不能使用 make 的场景需要自定义删除器、需要使用花括号初始化3shared_ptr 额外限制类有自定义operator new/delete大对象且weak_ptr存活时间长于shared_ptr4如果必须用new在独立语句中创建智能指针并用std::move传递给函数背景知识std::make_shared在C11引入std::make_unique直到C14才成为标准templatetypenameT,typename...Tsstd::unique_ptrTmake_unique(Ts...params){returnstd::unique_ptrT(newT(std::forwardTs(params)...));}⚠️**注意不要将自制版本放入std命名空间以免升级到 C14 后与标准库版本冲突。**:::info三个make 函数std::make_uniqueC14std::make_sharedC11std::allocate_shared— 与make_shared类似尽量用make_shared但第一个参数是 allocator 对象。:::使用 make 函数的优势1️⃣避免代码重复autoupw1(std::make_uniqueWidget());// Widget 出现 1 次std::unique_ptrWidgetupw2(newWidget);// Widget 出现 2 次 ❌autospw1(std::make_sharedWidget());// Widget 出现 1 次std::shared_ptrWidgetspw2(newWidget);// Widget 出现 2 次 ❌2️⃣异常安全processWidget(std::shared_ptrWidget(newWidget),// 潜在资源泄漏computePriority());:::color4编译器的执行顺序可能是执行new Widget执行computePriority()执行shared_ptr构造函数如果第 2 步抛出异常第 1 步 new 出来的 Widget 就永远不会被 shared_ptr 管理造成内存泄漏:::processWidget(std::make_sharedWidget(),// 没有资源泄漏computePriority());3️⃣** 效率提升仅限std::make_shared/std::allocate_shared**:::color5直接使用new需要两次内存分配一次为对象new Widget一次为控制块shared_ptr构造函数内部分配::::::color1使用std::make_shared只需一次内存分配分配一整块内存同时容纳对象和控制块:::优势说明✅ 代码体积更小只含一次内存分配调用✅ 执行速度更快内存只分配一次✅ 内存占用更少减少控制块中某些簿记信息不适合使用 make 函数的情况1️⃣需要指定自定义删除器❌autowidgetDeleter[](Widget*pw){/* ... */};// 只能用 newstd::unique_ptrWidget,decltype(widgetDeleter)upw(newWidget,widgetDeleter);std::shared_ptrWidgetspw(newWidget,widgetDeleter);2️⃣需要使用花括号初始化❌autoupvstd::make_uniquestd::vectorint(10,20);// 结果10 个元素每个值为 20而非两个元素 {10, 20}autoinitList{10,20};// 先创建 std::initializer_listautospvstd::make_sharedstd::vectorint(initList);3️⃣类有自定义 operator new/delete❌仅 shared_ptr自定义的operator new/delete通常只处理精确sizeof(类型)大小的内存。但std::make_shared需要分配对象大小 控制块大小**与类自身的自定义内存管理不兼容。4️⃣大对象 weak_ptr 生命周期长❌仅 shared_ptr使用make_shared时对象和控制块在同一块内存中只有当最后一个shared_ptr和最后一个weak_ptr都销毁后整个内存块才会释放make_shared 最后一个 shared_ptr 销毁 → 对象析构但内存未释放 最后一个 weak_ptr 销毁 → 整块内存才释放 直接 new 最后一个 shared_ptr 销毁 → 对象析构 对象内存释放 ✅ 最后一个 weak_ptr 销毁 → 只有控制块内存释放classReallyBigType{/* 很大的对象 */};// ❌ 大对象内存延迟释放autopBigObjstd::make_sharedReallyBigType();// ... 即使 shared_ptr 全部销毁只要 weak_ptr 还在内存就不释放// ✅ 大对象内存及时释放std::shared_ptrReallyBigTypepBigObj(newReallyBigType);// 最后一个 shared_ptr 销毁 → 内存立即释放无法使用 make 时的异常安全方案如果必须直接使用new例如需要自定义删除器应将new 的结果在一条独立语句中立即传给智能指针构造函数// ❌ 非异常安全processWidget(std::shared_ptrWidget(newWidget,cusDel),computePriority());// ✅ 异常安全std::shared_ptrWidgetspw(newWidget,cusDel);processWidget(std::move(spw),computePriority());// move 避免引用计数原子递增std::move优化原理传右值时用移动构造无引用计数操作传左值时用拷贝构造需原子递增引用计数。