必须同时删除拷贝构造函数和拷贝赋值运算符,否则默认 operator= 仍存在,导致传值、容器插入等场景意外触发拷贝;正确做法是二者成对声明为 delete,且推荐置于 public 区域。

为什么直接 delete 拷贝构造函数还不够
只声明 MyClass(const MyClass&) = delete; 无法阻止拷贝赋值,因为编译器仍会生成默认的 operator=。必须同时禁用拷贝构造和拷贝赋值,否则对象在传值、容器插入、隐式转换等场景下仍可能意外触发拷贝行为。
正确写法:两个 delete 必须成对出现
标准做法是显式删除拷贝构造函数和拷贝赋值运算符,且通常放在 private 区域(C++11 前)或 public 区域(C++11 起推荐),后者更清晰、错误提示更直接。
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
// 移动语义可选,但若保留需显式定义
NonCopyable(NonCopyable&&) noexcept = default;
NonCopyable& operator=(NonCopyable&&) noexcept = default;
};
- 不写
= default给移动函数,编译器不会自动生成移动操作(因为存在用户声明的拷贝操作) - 若类含指针或资源句柄,禁用拷贝后务必确认移动语义是否合理,否则可能引发资源泄漏或 double-free
- 继承时基类需为
protected析构或确保派生类也禁用拷贝,否则子类可能绕过限制
常见误用:只删构造函数却忘了 operator=
典型错误是只写 MyClass(const MyClass&) = delete;,然后尝试 a = b; —— 这会编译通过,因为默认赋值运算符仍存在。错误信息往往指向“use of deleted function”但发生在赋值处,容易让人误判问题位置。
- VS 和 GCC 都会在赋值时报错,但提示行号指向
operator=调用点,而非类定义处 - Clang 提示更明确:“copy assignment operator is implicitly deleted because ‘NonCopyable’ has a user-declared copy constructor”
- 使用
static_assert(std::is_copy_constructible_v可在编译期验证是否真正禁用, "")
比手动 delete 更省事的方式:继承 std::noncopyable
Boost 提供了 boost::noncopyable,C++ 标准库虽无等价物,但可轻松手写一个空基类,让所有需要不可拷贝的类统一继承:
立即学习“C++免费学习笔记(深入)”;
struct noncopyable {
protected:
noncopyable() = default;
~noncopyable() = default;
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
};
继承它即可:class Logger : private noncopyable { ... };。注意用 private 继承,避免接口污染;若需移动语义,仍需在派生类中显式声明移动函数。
手动 delete 是最直接可控的方式,而继承基类适合多处复用;但无论哪种,漏掉拷贝赋值运算符都是高频疏忽点。










