class MyClass {
public:
MyClass(int& ref, const int c_val)
: myRef(ref), myConstVal(c_val) {}
// 明确删除拷贝操作
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
private:
int& myRef; // 引用成员
const int myConstVal; // const成员
};
注意资源管理陷阱
1. 赋值运算符的调用条件
赋值运算符
operator=
的语义是:将右侧对象的内容复制到左侧已存在的对象中。因此:
- 左侧对象必须已经存在(已构造完成)。
- 如果左侧对象尚未构造,C++ 会调用 拷贝构造函数 来初始化左侧对象。
2. 示例对比
场景 1:调用赋值运算符
Derived md; // md 已经存在 md = bd; // ✅ 调用赋值运算符 Derived::operator=
- 解释:
md
是一个已经构造的对象,此时md = bd;
会调用Derived::operator=
。场景 2:调用拷贝构造函数
Derived md = bd; // ❌ 调用拷贝构造函数 Derived::Derived(const Derived&)
- 解释:
md
是一个新对象,尚未构造,因此 C++ 会调用 拷贝构造函数,而非赋值运算符。
3. 为什么
Derived md = bd;
调用的是拷贝构造函数?C++ 的语法规则规定:
T a = b;
等价于T a(b);
(即调用拷贝构造函数),而不是a = b;
。- 因此,
Derived md = bd;
实际上是初始化语句,触发的是 拷贝构造函数。
4. 如何显式触发赋值运算符?
要调用赋值运算符,必须确保左侧对象已经存在。例如:
Derived md; // md 已构造 md = bd; // ✅ 调用赋值运算符
5. 总结:关键区别
操作 调用函数 左侧对象是否存在? Derived md = bd;
拷贝构造函数 ❌ 不存在(初始化) Derived md; md = bd;
赋值运算符 ✅ 存在(已构造)
6. 参考知识库
知识库 [1] 的说明:
赋值运算符不会创建新对象,而是操作左侧已有对象。 左侧对象必须已经存在,赋值运算符不会重新创建它。
知识库 [10] 的说明:
赋值操作和初始化操作都会导致对象被复制。赋值是将一个对象的值分配给另一个对象,而初始化是在声明新对象时进行的。
7. 常见陷阱
- 误认为
a = b;
一定调用赋值运算符:如果a
是新对象,a = b;
会调用赋值运算符;但如果a
是初始化语句的一部分(如T a = b;
),则调用拷贝构造函数。- 调试建议:在
operator=
中添加日志(如std::cout
),观察何时被调用。
通过理解这一规则,你可以避免混淆拷贝构造和赋值运算符的调用场景,编写更健壮的 C++ 代码。