成員初始化順序**唯由宣告順序決定**,因類別宣告唯一但建構函數可多個 (初始化列表順序各異),若按列表順序建構將導致解構無法確定唯一逆序。
// 普通建構函數
String::String(const char* str) {
if (str == nullptr) {
m_data = new char[1];
*m_data = '\0';
} else {
int len = strlen(str);
m_data = new char[len + 1];
strcpy(m_data, str);
}
}
// 解構函數
String::~String() {
delete [] m_data; // 內建型別指標 delete/delete[] 均可,習慣用 []
}
String a("hello"), b("world");
// 預設賦值 b = a 相當於:
b.m_data = a.m_data; // 三大禍害:
// 1. b 原記憶體洩漏
// 2. 雙指標指向同一塊區域,雙邊修改互擾
// 3. 解構時雙重 delete → 崩潰
String a("hello"), b("world");
String c = a; // 拷貝建構 (物件創建時)
c = b; // 賦值運算子 (物件已存在)
// 建議寫成 String c(a); 以區分語意
// 拷貝建構函數
String::String(const String& other) {
int len = strlen(other.m_data); // 參考不可為 null,無需檢查
m_data = new char[len + 1];
strcpy(m_data, other.m_data);
}
// 賦值運算子 (同上四步)
String& String::operator=(const String& other) {
if (this == &other) return *this;
delete [] m_data;
int len = strlen(other.m_data);
m_data = new char[len + 1];
strcpy(m_data, other.m_data);
return *this;
}
差異點:拷貝建構入口無需檢查 nullptr,因 const String& 綁定既有物件,**參考絕不為 null**;普通建構接受指標須防 null。