std::unique_ptr作为类成员时必须显式定义移动操作,否则类可能既不可拷贝也不可移动;应优先用std::make_unique初始化,访问前需判空,多态场景下基类析构函数必须为virtual。

std::unique_ptr作为类成员时必须显式定义移动操作
如果不手动声明移动构造函数和移动赋值运算符,编译器可能隐式删除它们,导致类对象无法移动——尤其当类中还有其他非移动友好的成员(比如含 const 成员、引用成员,或用户自定义了析构函数但没写移动函数)时。std::unique_ptr 本身不可复制,所以一旦类默认生成的拷贝操作被禁用,又没提供移动操作,该类就变成“既不能拷贝也不能移动”的类型,很多容器(如 std::vector)插入时直接报错。
实操建议:
- 只要类含
std::unique_ptr成员,且你希望类可移动,就显式声明MyClass(MyClass&&) = default;和MyClass& operator=(MyClass&&) = default; - 如果类已定义了析构函数(哪怕只是空的
~MyClass() {}),编译器就不会自动生成移动操作,此时= default必须显式写出 - 不要试图为含
std::unique_ptr的类定义拷贝构造或拷贝赋值——除非你明确实现深拷贝逻辑,否则编译会失败
初始化方式选 std::make_unique 而非 new
直接用 new 初始化 std::unique_ptr 成员容易引发异常安全问题:如果后续成员初始化抛异常,new 分配的内存不会自动释放;而 std::make_unique 是原子操作,能保证异常安全。
常见错误现象:在构造函数初始化列表里写 m_ptr(new int(42)),看似可行,但若同一初始化列表中靠后的成员构造抛异常,int* 就泄露了。
正确写法:
class Holder {
std::unique_ptr<int> m_ptr;
public:
Holder() : m_ptr(std::make_unique<int>(42)) {} // ✅ 推荐
// Holder() : m_ptr(new int(42)) {} // ❌ 风险高
};</int></int>
访问成员时注意空指针检查
std::unique_ptr 可为空,解引用前不检查会导致段错误。尤其在类生命周期中存在重置(reset())、条件构造或资源延迟加载时,空状态很常见。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
使用场景举例:类封装一个可选的缓存对象,只在首次访问时创建。
实操建议:
- 用
if (ptr)或if (ptr != nullptr)判断有效性,不要依赖get() != nullptr手动比较(冗余) - 避免无条件调用
*ptr或ptr->method();若逻辑上必须非空,考虑用assert(ptr)辅助调试,但生产环境仍需防御性判断 -
operator bool()是最简洁的判空方式:if (m_cache) { use(*m_cache); }
继承体系中 std::unique_ptr 成员的析构行为
当基类含 std::unique_ptr<base> 成员,并指向派生类对象时,若 Base 的析构函数不是 virtual,则通过基类指针 delete 会未定义行为——但 std::unique_ptr 默认用的是 delete 表达式,不是 delete[],也不自动感知多态析构需求。
关键点:唯一能保证正确析构派生对象的方式,是让 Base 拥有虚析构函数。
参数差异:
- 若
std::unique_ptr<derived></derived>直接持有派生对象,无需虚析构 —— 类型明确,静态析构即可 - 若
std::unique_ptr<base>持有new Derived,则Base::~Base()必须是virtual,否则派生部分不析构 - 不能靠自定义删除器绕过虚析构要求:即使写了
std::unique_ptr<base void>,若删除器内部仍用delete ptr,没有虚析构照样 UB
std::unique_ptr 当成员用,真正麻烦的从来不是怎么写,而是它悄悄改变了整个类的值语义边界——移动、异常、空状态、多态销毁,每个环节都卡在“看起来能编译,运行才出事”的位置。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










