按值传递 std::unique_ptr 会触发移动,原指针变空;应根据是否接管资源选择传值(接管)或 const 引用/裸指针(仅访问)。

直接传 std::unique_ptr 值会触发移动,原指针变空
这是最常踩的坑:把 std::unique_ptr 按值传递进函数,比如 void foo(std::unique_ptr<int> p)</int>,调用时 foo(std::move(ptr)) 确实能编译,但 ptr 会立刻变成 nullptr。如果你后续还打算用它,就会出错。
原因很简单:std::unique_ptr 的拷贝构造函数被显式删除了,按值传参只能走移动构造——所有权强制转移。
- 适用场景:函数确实要接管资源(比如工厂函数、销毁逻辑)
- 不适用场景:只是想读/修改对象内容,不打算释放内存
- 检查方式:调用后打印
if (ptr) { ... },很可能为false
想只读访问?用 const std::unique_ptr<t>&</t> 或裸指针
如果函数只是需要访问 *ptr 或调用 ptr->method(),别动所有权。传 const std::unique_ptr<t>&</t> 最安全直观;更轻量的做法是传 T* 或 T&(前提是确定指针非空)。
示例对比:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
void inspect(const std::unique_ptr<int>& p) { std::cout <ul>
<li>
<code>const std::unique_ptr<t>&</t></code> 能体现“我依赖的是 unique_ptr 管理的对象”,语义清晰</li>
<li>
<code>T*</code> 性能零开销,且兼容 <code>std::shared_ptr</code>、原始 <code>new</code> 结果等</li>
<li>避免传 <code>std::unique_ptr<t>&</t></code>(非常量左值引用)——容易误写成移动或重置</li>
</ul>
<h3>函数要修改所指对象但不改变所有权?传 <code>T&</code> 最干净</h3>
<p>比如一个函数要给智能指针指向的对象赋值、调用成员函数、或做其他不涉及内存管理的操作,<code>T&</code> 是最直接的选择。它不暴露内存管理细节,调用方也完全不用考虑移动语义。</p>
<p>示例:</p>
<pre class="brush:php;toolbar:false;">void reset_value(int& x) { x = 42; }
// 调用:
auto ptr = std::make_unique<int>(10);
reset_value(*ptr); // ptr 仍有效,*ptr 现在是 42
</int>
- 比
const std::unique_ptr<t>&</t>更轻量,无模板实例化开销 - 要求调用方确保
ptr不为空,否则解引用未定义行为 - 不能用于可能为空的场景;若需处理空情况,改用
std::optional<t></t>(C++23)或加空检查
返回 std::unique_ptr 时,别用引用或指针试图“延长生命周期”
有人会想:“我把 unique_ptr 存在局部变量里,然后返回它的引用或 .get()”,这绝对不行。局部 std::unique_ptr 析构时会自动 delete 所指对象,返回的裸指针立刻悬空。
- 正确做法:函数直接返回
std::unique_ptr<t></t>(现代 C++ 编译器基本都 RVO/NRVO,无额外开销) - 错误写法:
std::unique_ptr<int>& bad() { auto p = std::make_unique<int>(1); return p; }</int></int>—— 返回局部变量引用,未定义行为 - 错误写法:
int* bad2() { auto p = std::make_unique<int>(1); return p.get(); }</int>——p析构,内存被释放
所有权转移必须显式、单向、不可逆;任何绕过 std::unique_ptr 生命周期管理的尝试,都会在某个时刻咬你一口。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










