系統程式設計師的 C++

特殊成員函式(the special member functions)

每個 C++ 類別都需要回答幾個基本問題:怎麼建立一個、怎麼複製、怎麼移動、怎麼清理?這些答案你可以自己寫,但若你不寫,編譯器會悄悄替你寫好。這些由編譯器管理的操作就是特殊成員函式,理解有哪幾個(以及它們何時出現或消失)對於把資源管理做對至關重要。

總共有六個:預設建構子(無引數建立)、解構子(清理)、複製建構子與複製賦值運算子(複製一個既有物件),以及移動建構子與移動賦值運算子(偷走一個即將消亡物件的內臟)。編譯器把每一個都產生為逐成員的預設版本——例如預設複製建構子會依序複製每個成員。微妙又常令人痛苦的規則是:宣告其中某些會抑制其他的自動產生:尤其,只要你宣告了解構子(或任何複製/移動操作),移動操作就不會被產生,編譯器可能悄悄退回去使用複製。

這六個正是 RAII 賴以掛載的鉤子。建構子取得資源,解構子釋放資源,而複製相對於移動決定了複製一個物件時,是該對持有的資源做深複製,還是轉移它的所有權。整套「零法則/五法則」的指引都是為了讓你連貫地處理這組函式。誠實的陷阱再次提醒:編譯器產生的複製是逐成員的(淺)複製——對一個全是 int 的 struct 完美無缺,對持有裸擁有指標的類別卻是災難,因為兩份副本都會試圖釋放同一塊記憶體。

struct P { P(); ~P(); P(const P&); P& operator=(const P&); P(P&&); P& operator=(P&&); }; // 六個特殊成員,依其簽章

預設建構子、解構子、複製建構子與複製賦值、移動建構子與移動賦值——你沒寫的編譯器會補上,但受抑制規則約束。

移動簽章裡的雙 & 號(&&)是右值參考,不是「參考的參考」。它是語言用來說「這個引數是暫存物件,或是你已同意被掏空的物件」的方式,正因如此廉價的移動才安全。

又称
the big five六大特殊成員函式