c++ Effective c++ 条款5

发布于:2025-09-01 ⋅ 阅读:(22) ⋅ 点赞:(0)
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++ 代码。


网站公告

今日签到

点亮在社区的每一天
去签到