C++ 編譯器為類自動生成的預設賦值運算符執行成員逐一拷貝(member-wise copy)。對於無動態資源的類(如僅含基本型別成員),這完全足夠:
class Demo {
int m_a, m_b;
public:
Demo(int a, int b) : m_a(a), m_b(b) {}
void display() { cout << m_a << ", " << m_b << endl; }
};
int main() {
Demo obj1(10, 20);
Demo obj2 = obj1; // 預設賦值,正常工作
obj1.display(); // 10, 20
obj2.display(); // 10, 20
}
當類包含指針成員並管理動態記憶體時,預設賦值會導致淺拷貝——多個物件共享同一塊記憶體,造成雙重釋放、資料競爭等嚴重錯誤:
class Book {
double m_price;
int* m_bookmark; // 動態陣列
int m_num;
public:
Book(double price, int* bookmark, int num) : m_price(price), m_num(num) {
m_bookmark = new int[num];
for (int i = 0; i < num; ++i) m_bookmark[i] = bookmark[i];
}
void setBookmark(int page, int index) { m_bookmark[index] = page; }
void display() { /* ... */ }
};
int main() {
int marks[] = {1, 49, 56, 290};
Book cpp(68.5, marks, 4);
Book java = cpp; // 淺拷貝!java.m_bookmark 指向同一陣列
java.setBookmark(100, 2); // 修改影響 cpp!
cpp.display(); // 書籤被意外修改
}
核心原則:分配新記憶體、拷貝資料、釋放舊資源、處理自我賦值。
Book& Book::operator=(const Book& b) {
if (this != &b) { // 1. 自我賦值檢查
m_price = b.m_price;
m_num = b.m_num;
// 2. 釋放舊資源
delete[] m_bookmark;
// 3. 分配新記憶體並深拷貝
m_bookmark = new int[b.m_num];
for (int i = 0; i < b.m_num; ++i)
m_bookmark[i] = b.m_bookmark[i];
}
return *this; // 4. 返回引用支援鏈式賦值
}
| 要素 | 建議 | 理由 |
|---|---|---|
| 參數型別 | const Book& | 避免拷貝開銷,防止修改源物件 |
| 返回型別 | Book& | 支援 a = b = c 鏈式賦值 |
| 自我賦值檢查 | if (this != &b) | 防止釋放後再分配導致資料丟失 |
| 資源管理順序 | 先釋放舊,再分配新 | 異常安全:分配失敗時舊資源已釋放,物件處於有效空狀態 |