C++17中std::make_unique与直接构造std::unique_ptr的优劣对比
C++17中std::make_unique与直接构造std::unique_ptr的优劣对比
先明确一个核心前提:和std::make_shared与直接构造std::shared_ptr的差异不同,std::make_unique<T>()和std::unique_ptr<T>{new T}在内存分配次数上完全一致——都是只做一次内存分配,不存在性能上的本质差距。那既然C++17已经支持从初始化语句自动推导模板参数(比如可以直接写auto ptr = std::unique_ptr{new T};不用显式指定std::unique_ptr<T>),make_unique还有啥不可替代的价值?咱们来慢慢捋:
「std::make_unique」的优势
- 异常安全是最大亮点:举个实际场景,假设你有个函数
void func(std::unique_ptr<T>, int),如果直接写func(std::unique_ptr<T>{new T}, some_func_that_throws()),编译器可能会先执行new T,再调用some_func_that_throws()。要是这个函数抛出异常,那new出来的内存就没人接管了,直接造成内存泄漏。但用func(std::make_unique<T>(), some_func_that_throws())就不会有这问题——make_unique把内存分配和智能指针构造做成了原子操作,就算后面的函数抛异常,也不会有泄漏风险。 - 代码更简洁清爽:虽然C++17能推导模板参数,但
make_unique写起来还是更短!比如auto ptr = std::make_unique<MyClass>(arg1, arg2);对比auto ptr = std::unique_ptr<MyClass>{new MyClass(arg1, arg2)};,少敲不少重复代码,可读性也更好。 - 彻底规避裸指针暴露:用
make_unique的话,代码里完全看不到new关键字,符合现代C++“尽量避免裸指针和显式new/delete”的编码风格,也减少了不小心把裸指针传给其他函数的风险。 - 数组构造更直观:创建数组类型的
unique_ptr时,std::make_unique<T[]>(10)的写法比std::unique_ptr<T[]>{new T[10]}更符合函数式构造的逻辑,不容易搞错数组模板参数的写法。
「直接构造std::unique_ptr」的优势
- 支持自定义删除器:
make_unique不支持传递自定义删除器参数,如果你的场景需要给unique_ptr指定特殊的删除逻辑(比如释放特定资源、调用自定义销毁函数),那只能用直接构造的方式,比如std::unique_ptr<T, MyDeleter> ptr{new T, MyDeleter{}};。 - 定制内存分配细节:要是你需要用重载的
operator new、或者从特定内存池分配内存,这时候必须自己调用自定义分配逻辑拿到裸指针,再交给unique_ptr管理——make_unique无法满足这种个性化的分配需求。
总结
除非你需要自定义删除器或者特殊的内存分配逻辑,否则在C++17及以后,优先选择std::make_unique——它带来的异常安全、代码简洁性和风格一致性都是实打实的好处,绝不仅仅是旧版本里的模板参数推导功能而已。
内容来源于stack exchange
相关产品推荐
相关产品推荐

