应显式使用=delete禁用拷贝和移动操作,因不写会导致编译器自动生成不安全版本;C++11起推荐public区直接声明=delete,无函数体且不可private;C++98需private声明但不定义,存在友元误用及链接期错误等隐患。

直接禁止:把 拷贝构造函数 和 operator= 声明为 = delete,C++11 起这是最干净、最可靠的做法。
为什么不能只靠“不写”?
不写拷贝构造和赋值运算符,编译器会自动生成默认版本——只要类里没手动定义,且所有成员都可拷贝,它就允许你拷贝。这不是“禁用”,是“默认开启”。更危险的是,有人后期加了成员(比如一个 std::mutex),却忘了补 = delete,结果编译器悄悄生成了不安全的拷贝函数,运行时报错或死锁。
所以必须显式干预:
-
= delete是编译期硬约束,任何拷贝/赋值尝试都会在编译时报错,错误信息明确指向那两个函数 - 不依赖访问控制(比如私有化),避免派生类意外绕过或友元滥用
- 连移动操作也建议一并
= delete,否则编译器可能隐式生成移动函数,导致你以为禁了拷贝,实际还能被移动(而移动语义对资源类往往同样危险)
C++11 及以后的标准写法
在 public 区域直接声明并 = delete,不需要 private 或任何额外修饰:
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
// 移动也禁掉,防止误用
NonCopyable(NonCopyable&&) = delete;
NonCopyable& operator=(NonCopyable&&) = delete;
};
注意:= delete 的函数不能有函数体,也不能是 private;放在 public 里反而更安全——它告诉编译器“这个函数存在,但谁都不许调”,而不是“这个函数存在,只限内部调”,后者容易被友元或成员函数误用。
C++98 兼容写法(仅限必须支持老标准时)
把拷贝构造和赋值运算符声明为 private 且**只声明不定义**:
class NonCopyable {
public:
NonCopyable() {}
private:
NonCopyable(const NonCopyable&); // 仅声明,无定义
NonCopyable& operator=(const NonCopyable&); // 仅声明,无定义
};
这种写法的问题很实在:
- 如果类有友元函数或友元类,它们仍能调用这些 private 函数
- 类内成员函数也能意外调用(比如某个成员函数写了
*this = other;) - 链接期才报错(因为未定义),错误信息模糊,不如
= delete的编译期提示直接
容易忽略的坑
很多人以为禁了拷贝就万事大吉,但实际踩坑点很具体:
- 继承体系中,基类
= delete不会自动传导到派生类——子类若没显式禁用,仍可能生成合法的拷贝函数 - 模板类里如果用了
= delete,要注意 SFINAE 场景下可能影响重载解析,必要时配合static_assert加一层检查 - 禁拷贝类被放进容器(如
std::vector)会失败,不是 bug,是设计使然;真要存,得用指针或智能指针包装 - 析构函数被
= delete会导致std::unique_ptr等无法管理该对象,慎用
真正关键的不是“怎么写语法”,而是每次加新成员前,先问一句:这个类还应该被拷贝吗?如果答案是否定的,= delete 就得同步更新——否则约束就从编译期断掉了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











