std::make_unique是创建std::unique_ptr最安全推荐的方式,避免裸指针泄漏与异常不安全问题,但不支持数组(需用std::make_unique<T[]>)、自定义删除器及初始化列表(C++17起才支持)。

直接说结论:std::make_unique 是创建 std::unique_ptr 最安全、最推荐的方式,它能避免裸指针泄漏、异常安全问题,且写法简洁;但不能用于数组类型(除非用 std::make_unique<t></t>),也不能用于自定义删除器的场景。
为什么不用 new + unique_ptr 构造?
手动写 std::unique_ptr<int>(new int(42))</int> 看似可行,但一旦构造过程中抛异常(比如 new 成功,但 unique_ptr 构造器内部调用 delete 前又失败),就可能造成内存泄漏。
而 std::make_unique 是原子操作:要么全部成功,要么不分配——没有中间态。编译器也更容易优化它。
常见错误现象:
- 代码里混用 new 和 unique_ptr,结果在函数多参数调用中因求值顺序未定义导致泄漏(如 f(unique_ptr<int>(new int), g())</int> 中 g() 抛异常,new int 就漏了)
- 误以为“反正有智能指针兜底”,忽略了构造过程本身不安全
实操建议:
- 所有单对象 unique_ptr 创建,优先写 std::make_unique<t>(args...)</t>
- 不要自己 new 后传给 unique_ptr 构造函数
- 注意 C++14 才完全支持可变参数和完美转发,C++11 只支持无参或单参(部分编译器扩展支持,但不标准)
std::make_unique 怎么用?
它专为动态数组设计,返回 std::unique_ptr<t></t>,不是 std::unique_ptr<t></t>。这点容易混淆,且不能混用。
立即学习“C++免费学习笔记(深入)”;
使用场景:
- 需要堆上分配固定大小数组(比如缓冲区、图像像素行)
- 不想用 std::vector(例如接口要求裸数组指针,或需兼容 C API)
常见错误现象:
- 写成 auto p = std::make_unique<int>(5);</int> ✅ 正确
- 写成 auto p = std::make_unique<int>(5);</int> ❌ 这是创建一个值为 5 的 int,不是数组
- 对 std::make_unique<t></t> 返回的对象调用 *p 或 p->foo() —— 编译失败,因为 T[] 没有成员函数
实操建议:
- 数组长度必须是编译期常量或运行时值,但不能是表达式(如 make_unique<int>(n + 1)</int> 合法)
- 访问元素用 p[i],不能用 -> 或 *
- 它自动调用 delete[],不用手动干预
哪些情况不能用 std::make_unique?
它不支持自定义删除器(deleter)。如果你需要非标准释放逻辑(比如关闭文件描述符、调用 CoTaskMemFree、或释放 GPU 显存),std::make_unique 无能为力。
性能 / 兼容性影响:
- std::make_unique 在所有主流编译器(GCC 4.9+、Clang 3.4+、MSVC 2013 Update 2+)都已支持
- 它生成的代码和手写 new + unique_ptr 几乎一样快,但更安全
实操建议:
- 需要自定义删除器时,必须显式构造:std::unique_ptr<int mydeleter>(new int(42), MyDeleter{})</int>
- 不要试图绕过:比如先 make_unique 再 release() + reset(..., deleter) —— 这样反而破坏异常安全性
- 如果删除器类型复杂,考虑封装成工厂函数,而不是硬塞进 make_unique
最容易被忽略的一点:std::make_unique 不支持初始化列表语法(如 std::make_unique<:vector>>({1,2,3})</:vector> 在 C++14 是非法的,C++17 起才允许)。遇到这类需求,得退回到 std::unique_ptr 构造函数,或者改用 std::make_shared(如果语义允许共享)。











