effect C++ 複製物件時勿忘其每一個成分
阿新 • • 發佈:2019-01-02
copy 函式
設計良好之面向物件系統會將物件的內部封裝起來,只留兩個函式負責物件拷貝(複製),copy建構函式和co培養assignment 操作符。
如果你宣告自己的copying函式,意思是告訴編譯器你並不喜歡預設實現中的某些行為。編譯器會以一種奇怪的方式回敬:當你的實現程式碼幾乎必然出錯卻不告訴你。
void logCall(const std::string& funcName); class Customer{ public : ... Customer(const Customer&rhs); Customer& operator=(const Customer &rhs); ... private: std::string name; }; Customer::Customer(const Customer &rhs):name (rhs.name) { logCall("Customer copy constructor"); } Customer &Customer::operator=(const Customer&rhs) { logCall("Customer copy assignment operator"); name = rhs.name; return *this; }
如果另一個成員變數加入戰局這時既有的copying函式執行的是區域性拷貝:複製了name,沒有新新增複製的lastTransaction。編譯器不會發生怨言。class Date{...}; class Customer{ public: ... //同前 private: std::string name; Date lastTransaction; }
如果你為class新增一個變數,你必須同時修改copy函式(還有class內所有建構函式以及任何非標準形式的operator=)
一旦發生繼承
class PriorityCustomer:public Customer{ public: ... PriorityCustomer(const PriorityCustomer &rhs); PriorityCustomer& operator=(const PriorityCustomer &rhs); ... private: int priority; }; PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):priority(rhs.priority) { logCall("PriorityCustomer copy constructor"); } PriorityCustomer&PriorityCustomer::operator=(const PriorityCustomer &rhs) { logCall("PriorityCustomer copy assignment operator"); priority = rhs.priority; return *this; }
PriorityCustomer的copying函式複製了PriorityCustomer內宣告的成員變數,但每個PriorityCustomer還內涵它所繼承的Customer成員變數復件(副本),而那些成員變數並未複製。PriorityCustomer的copy建構函式並沒有指定的實參傳給base class建構函式(也就是說它在它的成員初值列中沒有提到Customer),因此PriorityCustomer物件Customer成分會被不帶實參之Customer建構函式(即default建構函式)初始化。default建構函式將針對name和lastTransaction執行預設的初始化動作。
任何時候需要“為derived class 撰寫copying函式”的重責大任,必須複製其base class 成分。應該用derived class 的copying函式呼叫相應的base class函式。
當編寫一個copying函式,請確保 (1)複製所有local成員變數 (2)呼叫所有base class 內的適當的copying函式PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs):Customer(rhs),priority(rhs.priority) { logCall("PriorityCustomer copy constructor"); } PriorityCustomer&PriorityCustomer::operator=(const PriorityCustomer &rhs) { logCall("PriorityCustomer copy assignment operator"); Customer::operator=(rhs); priority = rhs.priority; return *this; }
如果copy函式和copy assignment操作符有相近的程式碼,消除重複程式碼的做法是,建立一個新的成員函式給兩者呼叫。這樣的函式往往是private而且常被命名為init.
Copying函式應該確保複製“物件內所有成員變數”及“所有base class”成分。
不要嘗試以某個copy函式實現另一個copy函式。應該將共同機能放進第三個函式中,並由兩個coping函式共同呼叫。