C++ 編譯器在類未顯式聲明時會隱式生成六個成員函數:預設構造、析構、拷貝構造、拷貝賦值、取址運算符(const/非 const)。本文重點剖析前四者的觸發時機、預設行為、淺拷貝陷阱與深拷貝正確實現模式。
this 指針(對象尚未構造完成)。// 低效:默認構造 + 賦值(兩步)
Date(int y, int m, int d) { year = y; month = m; day = d; }
// 高效:初始化列表(一步到位)
Date(int y, int m, int d) : year(y), month(m), day(d) {}
const 成員、引用成員、無預設構造函數的類類型成員。~ClassName(),無參、無返回值、有 this、不可重載、一個類只能有一個。delete 持有的指針、關閉句柄等)。ClassName(const ClassName&); // 必須為 const 引用,否則按值傳遞導致無限遞歸
T t2(t1); / T t2 = t1;逐位複製所有非靜態成員。對於裸指針成員,導致兩個對象指向同一塊堆內存 → 雙重 delete 崩潰。
class Person {
char* name;
public:
Person(const char* n) { name = strdup(n); }
~Person() { free(name); }
// 無拷貝構造 → 淺拷貝
};
Person p1("Alice");
Person p2 = p1; // p2.name == p1.name
// p1、p2 析構時同一塊記憶體 free 兩次 → 崩潰
Person(const Person& other) {
name = strdup(other.name); // 分配新記憶體並複製內容
}
t2 = t1;)。Person& operator=(const Person& rhs) {
if (this == &rhs) return *this; // 1. 自賦值檢查
free(name); // 2. 釋放舊資源
name = strdup(rhs.name); // 3. 分配新資源 + 4. 複製內容
return *this; // 5. 支援鏈式賦值
}
Person& operator=(Person rhs) { // 參數按值傳遞 → 自動調用拷貝構造
swap(*this, rhs); // 交換內部指針
return *this; // rhs 析構時釋放舊資源
}
friend void swap(Person& a, Person& b) noexcept {
std::swap(a.name, b.name);
}
| 函數 | 觸發時機 | 預設行為 | 資源管理類必須自定義? |
|---|---|---|---|
| 預設構造 | 無參創建對象 | 不初始化成員 | 是(初始化資源句柄) |
| 析構 | 對象銷毀 | 不釋放資源 | 是(釋放資源) |
| 拷貝構造 | 初始化新對象 | 淺拷貝 | 是(深拷貝) |
| 賦值運算符 | 已有對象賦值 | 淺拷貝 | 是(深拷貝 + 自賦值檢查) |
std::unique_ptr/shared_ptr/string/vector 等 RAII 類型替代裸指針,讓編譯器生成的預設版本自動正確(Rule of Zero)。