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++ 代碼。