上一篇探討合成預設構造函數,本篇繼續深入複製構造函數的運作細節。
複製構造函數形式為 X(X&),參數為用戶自定義類型。當需要以一個對象內容初始化另一個同類對象時,編譯器會自動合成複製構造函數。主要三種情況:
ClassA a; ClassA b = a;同預設構造函數,複製構造函數也分為 trivial(平凡)與 nontrivial(非平凡)兩類。若類未聲明複製構造函數,編譯器會隱含聲明一個,視情況合成。
class Word {
public:
Word(const char*);
~Word() { delete [] str; }
private:
int cnt;
char *str;
};
// 使用情境
Word noun("lsmodel");
void foo() {
Word verb = noun; // 災難發生
}
因 verb 與 noun 指向同一字串,verb 析構時釋放記憶體,導致 noun 成懸空指標。
class String {
public:
String(const char*);
String(const String&); // 複製構造函數
~String();
};
class Word {
public:
Word(const String&);
~Word();
private:
int cnt;
String str; // String 類對象
};
此時編譯器為 Word 合成複製構造函數,調用 str 的複製構造函數:
inline Word::Word(const Word& wd) {
str.String::String(wd.str);
cnt = wd.cnt;
}
前兩種由程式設計師控制,後兩種由編譯器為了正確初始化 vptr 或虛擬基類指標/偏移量而合成複製構造函數,執行 memberwise 初始化與記憶體相關工作。
當對象以另一對象為初值時,編譯器會視情況合成複製構造函數,確保正確初始化。理解這四種非位元拷貝情況,有助於避免淺拷貝導致的資源管理錯誤。