一、拷貝構造函數是一種特殊構造函數,具有單個形參,該形參(常用const修飾)是對該類類型的引用。與默認構造函數一樣 ,拷貝構造函數可由編譯器隱式調用。拷貝構造函數應用的場合為:
(1)根據另一個同類型的對象顯式或隱式初始化一個對象。
(2)復制一個對象將它作為實參傳給一個函數。
(3)從函數返回時復制一個對象。
(4)初始化順序容器中的元素。
(5)根據元素初始化式列表初始化數組元素。
下面分別對以上5點進行說明。
1、對象的定義式。
C++支持兩種初始化形式:直接初始化和復制初始化。復制初始化使用“=”符號,而直接初始化將初始化式放在圓括號中。對於類類型對象,初始化的復制形式和直接形式有所不同。
直接初始化直接調用與實參匹配的構造函數。復制初始化首先使用指定構造函數創建一個臨時對象,然後用拷貝構造函數將那個臨時對象復制到正在創建的對象。
string null_book = "9-999-99999-9"; // copy-initialization
string dots(10, '.'); // direct-initialization
string empty_copy = string(); // copy-initialization
string empty_direct; // direct-initialization
說明:
(1)對於類類型對象,只有指定單個實參或顯式創建一個臨時對象用於復制時,才使用復制初始化。
(2)支持初始化的復制形式主要是為了與C的用法兼容。當情況許可時,可以允許編譯器跳過復制構造函數直接創建對象,但編譯器沒有義務這樣做。
(3)對於不支持復制的類型,或者使用explicit構造函數,不能進行復制初始化。例如:由於不能復制IO類型的對象,所以不能對那些類型的對象使用復制初始化。
ifstream file1("filename"); // ok: direct initialization
ifstream file2 = "filename"; // error: copy constructor is private
// This initialization is okay only if the Sales_item(const string&) constructor is not explicit
Sales_item item = string("9-999-99999-9");
2、形參與返回值。
當形參為非引用類型時,將復制實參的值,以非引用類型作返回值時,將返回return語句中值的副本。因而,當形參或返回值為類類型時,由拷貝構造函數進行復制。
string make_plural(size_t, const string &, const string &);
這個函數隱式使用string拷貝構造函數返回值的副本,形參是const引用,不會復制。
3、初始化容器元素。
vector<string> vec(5);
使用了默認構造函數和拷貝構造函數。編譯器首先使用string默認構造函數創建一個臨時對象,然後使用拷貝構造函數將臨時值復制到vec的每個元素。
4、構造函數與數組元素。
當用花括號初始化列表來顯式初始化類類型的數組,則使用復制初始化來初始化每個元素。根據指定值創建適當類型元素,然後用拷貝構造函數將該值復制到相應元素。
Sales_item primer_eds[] = { string("0-201-16487-6"),
string("0-201-54848-8"),
string("0-201-82470-1"),
Sales_item()
};
當定義一個新對象並用一個同類型的對象對它初始化時,將顯式使用拷貝構造函數。當將該類型的對象傳遞給函數或從函數返回該類型的對象時,將隱式使用拷貝構造函數。
二、合成的拷貝構造函數。
如果沒有定義拷貝構造函數,編譯器就會為我們合成一個。與合成的默認構造函數不同,即使我們定義了其他構造函數,也會合成拷貝構造函數。合成拷貝構造函數的行為是,執行逐個成員初始化,將新對象初始化為原對象的副本。
說明:
(1)編譯器將現在對象的每個非static成員,依次復制到正創建的對象。每個成員的類型決定了復制該成員的含義。
(2)合成拷貝構造函數直接復制內置類型成員的值,類類型成員使用該類的拷貝構造函數進行復制。
(3)數組成員的復制是個例外。雖然一般不能復制數組,但如果一個類具有數組成員,則合成復制構造函數將復制數組的每一個元素。
Sales_item::Sales_item(const Sales_item &orig):
isbn(orig.isbn), //使用string拷貝構造函數
units_sold(orig.units_sold), //直接復制orig.units_sold
revenue(orig.revenue) //直接復制orig.revenue
{ }
三、定義自己的拷貝構造函數。
拷貝構造函數就是接受單個類類型引用形參(通常用const修飾)的構造函數。因為用於向函數傳遞對象和從函數返回對象,該構造函數一般不應設置為explicit,拷貝構造函數應將實參的成員復制到正在構造的對象。它與類同名,沒有返回值,可以(而且應該)使用構造函數初始化列表初始化新創建對象的成員,可以在函數體中做任何其他必要工作。
說明:
(1)合成拷貝構造函數只完成必要的工作。只包含類類型成員或內置類型(但不是指針類型)成員的類,無須顯式地定義拷貝構造函數,也可以復制。
(2)有些類必須定義復制構造函數對復制對象時發生的事情加以控制。例如:
1)類有一個數據成員是指針,或者有成員表示在構造函數中分配的其他資源
2)類在創建新對象時必須做一些特定工作。
四、禁止復制。
有些類需要完全禁止復制,例如:iostream。如何禁止復制呢?省略拷貝構造函數這種做法不行,因為編譯器將會幫我們合成一個。為了防止復制,類必須顯式聲明其拷貝構造函數為private。
說明:
(1)如果拷貝構造函數是私有的,將不允許用戶復制該類型對象。
(2)類的友元和成員仍可以進行復制,如果也想禁止它們,可聲明一個私有的拷貝構造函數但不對其定義。聲明而不定義是合法的,但使用未定義的成員將導致鏈接失敗。
(3)通過聲明不定義私有拷貝構造函數,可禁止任何復制類類型對象的嘗試:用戶嘗試復制導致編譯錯誤,成員函數和友元復制導致鏈接錯誤。
(4)如果定義了拷貝構造函數,也必須定義默認構造函數。不允許復制的類對象只能作為引用傳遞給函數或從函數返回,它們也不能作為容器元素,嚴重局限類的使用。
------------------------------分割線------------------------------
C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm
讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm
讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm
將C語言梳理一下,分布在以下10個章節中: