經典 C++ 面試題:`operator=` 為什麼要回傳 `*this` 的參考?本文透過對比實驗,從物件模型、建構/解構開銷、鏈式賦值語意三個維度完整解析。
class A {
public:
A(int n) : m_nTest(n) { cout << "建構(值) " << n << endl; }
A(const A& a) : m_nTest(a.m_nTest) { cout << "複製建構" << endl; }
A() { cout << "建構(預設)" << endl; }
~A() { cout << "解構" << endl; }
// 版本一:回傳物件 (by value)
A operator=(const A& a) {
if (this == &a) return *this;
m_nTest = a.m_nTest;
cout << "指派" << endl;
return *this; // 觸發複製建構
}
// 版本二:回傳參考
// A& operator=(const A& a) { ... return *this; }
void print() { cout << m_nTest << endl; }
private:
int m_nTest;
};
int main() {
A a(100), b(99), c(98);
a = b = c; // 右結合:a.operator=(b.operator=(c))
a.print(); b.print(); c.print();
}
建構(值) 100
建構(值) 99
建構(值) 98
指派 // b = c
複製建構 // 產生暫時物件接收 b.operator= 回傳值
指派 // a = 暫時物件
複製建構 // 產生第二個暫時物件
解構 // 解構第一個暫時物件 (a= 的結果)
解構 // 解構第二個暫時物件 (b= 的結果)
99
99
98
解構 x3 // a,b,c 解構
額外開銷:2 次複製建構 + 2 次解構 = 2 個暫時物件完整生命週期。
建構(值) 100
建構(值) 99
建構(值) 98
指派 // b = c
指派 // a = b (直接操作 b 物件)
98
98
98
解構 x3
零暫時物件:參考只是別名,不觸發建構/解構。
`A& operator=` 回傳 `A&`,參考綁定至原物件,**無新物件建立**。類似:
void func(A& ref) { ref... } // 呼叫 func(a) 不產生暫時物件
A& ref = a; // 別名綁定,零開銷
(a=b)=c vs a=b=c:前者強制左結合,`a=b` 回傳參考直接賦值 `c`,無暫時物件;後者右結合同上分析。const A& operator=:可行但破壞鏈式賦值 (`(a=b)=c` 編譯錯誤,`const` 物件不可指派)。標準庫容器 `operator=` 回傳非 const 參考。return a vs return *this:語意錯誤!回傳右運算元參考會導致 `a=b=c` 讓 `a` 變成 `c` 的別名,後續修改 `a` 影響 `c`。A a; A b = a; (複製建構) vs A a,b; a=b; (指派):前者單次複製建構;後者預設建構 + 指派。資源管理類別 (RAII) 差異巨大。class MyClass {
public:
// 標準規範形式
MyClass& operator=(const MyClass& other) {
if (this != &other) {
// 1. 釋放舊資源
// 2. 深層複製新資源
// 3. 複製資料成員
}
return *this; // 參考回傳:支援鏈式、零暫時物件、符合標準慣例
}
// C++11 移動指派
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) {
// 移動資源所有權
}
return *this;
}
};