std::make_unique不能直接传入裸指针,因其语义是“分配新对象并立即交由unique_ptr管理”,而非包装已有指针;传入裸指针会导致编译失败,正确做法是用std::unique_ptr构造函数显式接管,但需确保该指针由new分配且无其他所有者。

std::make_unique 为什么不能直接传入裸指针
它根本不是用来“包装”已有裸指针的——std::make_unique 的语义是「分配新对象并立即交由 std::unique_ptr 管理」。如果你试图用 std::make_unique<int>(p)</int>(其中 p 是 int*),编译会直接失败,因为构造参数会被当作要传递给 int 构造函数的实参,而不是接管指针所有权。
想把已有裸指针转为 std::unique_ptr,必须显式调用 std::unique_ptr 构造函数:std::unique_ptr<int>(p)</int>。但注意:这要求你**完全确定该指针是 new 分配且未被其他所有者持有**,否则极易 double-delete。
std::make_unique 的正确调用形式(C++14)
C++14 中 std::make_unique 支持三种基本用法,全部基于 new 表达式隐式构造:
-
std::make_unique<t>()</t>→ 调用T()(默认构造) -
std::make_unique<t>(a, b)</t>→ 调用T(a, b)(完美转发参数) -
std::make_unique<t>(n)</t>→ 分配n个默认初始化的T元素(数组版,返回std::unique_ptr<t></t>)
例如:auto p = std::make_unique<:string>("hello");</:string> 安全等价于 std::unique_ptr<:string>(new std::string("hello"))</:string>,但前者更简洁、异常安全(避免 new 成功而 unique_ptr 构造失败时的泄漏)。
立即学习“C++免费学习笔记(深入)”;
容易踩的坑:数组版 make_unique 和 delete[] 的隐含契约
用 std::make_unique<t>(n)</t> 创建的指针,其底层析构器自动绑定为 delete[];但如果你误用非数组版(比如 std::make_unique<t>(n)</t>),即使 T 是数组类型(如 int[5]),也会触发 delete 而非 delete[],导致未定义行为。
务必核对类型和调用形式是否匹配:
- ✅ 正确:
auto arr = std::make_unique<int>(10);</int>→ 类型是std::unique_ptr<int></int> - ❌ 危险:
auto bad = std::make_unique<int>();</int>→ C++14 不支持这种语法,编译失败;C++17 才允许,但仍不推荐 - ⚠️ 隐患:
auto p = std::unique_ptr<int>(new int[10]);</int>→ 手动构造虽可行,但失去make_unique的异常安全性保障
替代裸 new + unique_ptr 构造的真正优势在哪
核心不是“写得少”,而是「异常安全」和「避免重复书写类型」:
- 当构造函数抛异常时,
std::make_unique<foo>(x, y, z)</foo>能保证:要么完整构造成功并返回unique_ptr,要么x/y/z的构造副作用已回滚,且不会发生内存泄漏;而new Foo(x, y, z)若在y或z构造中抛异常,x可能已构造完毕却无法释放 - 类型推导省事:
std::make_unique<:vector>>(100)</:vector>比std::unique_ptr<:vector>>(new std::vector<:string>(100))</:string></:vector>更清晰、不易拼错 - 不支持自定义删除器:如果需要
FILE*或 Windows HANDLE 这类资源管理,std::make_unique无能为力,必须手写std::unique_ptr并传入自定义 deleter
所以,只要你在做 new 对象 → 交给 unique_ptr 管理这件事,且不需要自定义析构逻辑,std::make_unique 就是更安全、更干净的选择;但它从不接管已有指针,这点必须刻在脑子里。


















