——複製構造函數用於將一個對象的值複製到新創建的對象中,用於初始化過程中(包括按值傳遞參數),而不是常規的賦值過程中 原型: Class_name(const Class_name &) 何時調用: 新建一個對象並將其初始化為同類現有對象時,複製構造函數都將被調用 StringBad ditto(m ...
——複製構造函數用於將一個對象的值複製到新創建的對象中,用於初始化過程中(包括按值傳遞參數),而不是常規的賦值過程中
原型:
Class_name(const Class_name &)
何時調用:
- 新建一個對象並將其初始化為同類現有對象時,複製構造函數都將被調用
StringBad ditto(motto); // calls StringBad(const StringBad &) StringBad metoo = motto; // calls StringBad(const StringBad &) StringBad also = StringBad(motto); // calls StringBad(const StringBad &) StringBad * pStringBad = new StringBad(motto) // calls StringBad(const StringBad &)
中間兩種聲明可能會使用複製構造函數直接創建metoo和also,也可能使用複製構造函數生成一個臨時對象,然後將臨時對象的內容賦給metoo和also,這取決於具體的實現
- 每當生成了對象副本時,編譯器都將使用複製構造函數。具體地說按值傳遞和返回對象時,都將調用複製構造函數(按值傳遞意味著創建原始變數的一個副本)。
- 由於按值傳遞對象時將調用複製構造函數,因此應該按引用傳遞對象。這樣可以節省調用構造函數的時間以及存儲新對象的空間
功能:
——預設的複製構造函數逐個複製非靜態成員(成員複製也稱為淺複製),複製的是成員的值
StringBad sailor = sports; //等效於 StringBad sailor; sailor.str = sports.str; sailor.len = sports.len;
只是由於私有成員是無法訪問的,因此後面的代碼不能通過編譯
靜態對象不受影響,因為它們屬於整個類,而不是各個對象
預設複製構造函數的問題:
一:
預設的複製構造函數不說明其行為(全盤複製),如果需要在構造函數中修改類靜態數據成員,這時就會發生錯誤
解決辦法:提供一個顯示覆制構造函數
StringBad::StringBad(const String &s) { num_strings++; // static int num_strings聲明在類中 ... // imported stuff to go here }
如果類中包含這樣的靜態數據成員,即其值將在新對象被創建時發生變化,則應該提供一個顯示覆制構造函數
二:
隱式複製構造函數的淺複製(複製指針)導致的記憶體問題
- 新對象可能修改指針指向的記憶體
- 新對象若走完生命周期將會調用析構函數釋放指針指向的記憶體,但當原對象也調用析構函數時,由於該段記憶體已被釋放,這將導致不確定的、可能有害的結果,另一個癥狀是試圖釋放記憶體兩次可能導致程式異常終止
解決辦法:定義一個顯示覆制構造函數,進行深度複製
StringBad::StringBad(const StringBad &st) { num_strings++; // handle static member update len = st.len; // same length str = new char [len + 1]; // allot space std::strcpy(str, st.str); // copy string to new location ... }
複製字元串並將副本的地址賦給str成員
如果類中包含了使用new初始化的指針成員,應當定義一個複製構造函數,以複製指向的數據,而不是指針,這被稱為深度複製。複製的另一種形式(成員複製或淺複製)只是複製指針值。淺複製僅淺淺地複製指針信息,而不會深入“挖掘”以複製指針引用地結構