這是 C++ 面試必考、工程必懂的核心知識點。本文通過 String 類實戰演示兩者區別,並深入分析臨時對象生命週期陷阱。
| 特性 | 拷貝構造函數 | 賦值運算符 |
|---|---|---|
| 調用時機 | 用現有對象初始化新對象 | 將現有對象賦值給已存在對象 |
| 是否創建新對象 | 是 | 否 |
| 需檢查自賦值 | 否(新對象不可能等於源) | 是(if (this == &rhs) return *this;) |
| 需釋放舊資源 | 否(新對象無舊資源) | 是(先 delete 再 new) |
| 返回類型 | 無(構造函數無返回) | Type&(支持鏈式賦值) |
class String {
public:
String(const char* str = nullptr); // 普通構造
String(const String& other); // 拷貝構造
~String(); // 析構
String& operator=(const String& other); // 賦值運算符
private:
char* m_data;
};
// 拷貝構造:直接構建新對象
String::String(const String& other) {
int len = strlen(other.m_data);
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;
}
void test() {
B b1;
B b2 = b1; // 拷貝構造(初始化)
B b3;
b3 = b1; // 賦值運算符(已存在對象)
}
輸出:
default constructor copyed by parameter 0 ← b2 拷貝構造 default constructor = by parameter 0 ← b3 賦值運算符
B play(B b) { return b; } // 參數按值傳遞,返回按值
void test() {
play(1); // ①
B t1 = play(2); // ②
B t2;
t2 = play(3); // ③
}
關鍵規則:
play(1) 中 1 隱式轉換構造 B,再拷貝構造參數 b)完整輸出流程分析見原文,核心在於區分「被調用者結束」vs「調用者結束」銷毀臨時對象的時機差異。
std::string, std::vector 等標準容器,避免手動管理裸指針delete 禁止拷貝(如單例、非拷貝類),或 default 交由編譯器生成