1、拷貝構造函數是一種特殊構造函數,具備單個形參,該形參(經常使用const修飾)是對該類類型的引用。與默認構造函數同樣 ,拷貝構造函數可由編譯器隱式調用。拷貝構造函數應用的場合爲:ios
(1)根據另外一個同類型的對象顯式或隱式初始化一個對象。數組
(2)複製一個對象將它做爲實參傳給一個函數。函數
(3)從函數返回時複製一個對象。spa
(4)初始化順序容器中的元素。指針
(5)根據元素初始化式列表初始化數組元素。code
下面分別對以上5點進行說明。對象
一、對象的定義式。blog
C++支持兩種初始化形式:直接初始化和複製初始化。複製初始化使用「=」符號,而直接初始化將初始化式放在圓括號中。對於類類型對象,初始化的複製形式和直接形式有所不一樣。ci
直接初始化直接調用與實參匹配的構造函數。複製初始化首先使用指定構造函數建立一個臨時對象,而後用拷貝構造函數將那個臨時對象複製到正在建立的對象。資源
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");
二、形參與返回值。
當形參爲非引用類型時,將複製實參的值,以非引用類型做返回值時,將返回return語句中值的副本。於是,當形參或返回值爲類類型時,由拷貝構造函數進行復制。
string make_plural(size_t, const string &, const string &);
這個函數隱式使用string拷貝構造函數返回值的副本,形參是const引用,不會複製。
三、初始化容器元素。
vector<string> vec(5);
使用了默認構造函數和拷貝構造函數。編譯器首先使用string默認構造函數建立一個臨時對象,而後使用拷貝構造函數將臨時值複製到vec的每一個元素。
四、構造函數與數組元素。
當用花括號初始化列表來顯式初始化類類型的數組,則使用複製初始化來初始化每一個元素。根據指定值建立適當類型元素,而後用拷貝構造函數將該值複製到相應元素。
Sales_item primer_eds[] = { string("0-201-16487-6"), string("0-201-54848-8"), string("0-201-82470-1"), Sales_item() };
當定義一個新對象並用一個同類型的對象對它初始化時,將顯式使用拷貝構造函數。當將該類型的對象傳遞給函數或從函數返回該類型的對象時,將隱式使用拷貝構造函數。
2、合成的拷貝構造函數。
若是沒有定義拷貝構造函數,編譯器就會爲咱們合成一個。與合成的默認構造函數不一樣,即便咱們定義了其餘構造函數,也會合成拷貝構造函數。合成拷貝構造函數的行爲是,執行逐個成員初始化,將新對象初始化爲原對象的副本。
說明:
(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 { }
3、定義本身的拷貝構造函數。
拷貝構造函數就是接受單個類類型引用形參(一般用const修飾)的構造函數。由於用於向函數傳遞對象和從函數返回對象,該構造函數通常不該設置爲explicit,拷貝構造函數應將實參的成員複製到正在構造的對象。它與類同名,沒有返回值,能夠(並且應該)使用構造函數初始化列表初始化新建立對象的成員,能夠在函數體中作任何其餘必要工做。
說明:
(1)合成拷貝構造函數只完成必要的工做。只包含類類型成員或內置類型(但不是指針類型)成員的類,無須顯式地定義拷貝構造函數,也能夠複製。
(2)有些類必須定義複製構造函數對複製對象時發生的事情加以控制。例如:
1)類有一個數據成員是指針,或者有成員表示在構造函數中分配的其餘資源
2)類在建立新對象時必須作一些特定工做。
4、禁止複製。
有些類須要徹底禁止複製,例如:iostream。如何禁止複製呢?省略拷貝構造函數這種作法不行,由於編譯器將會幫咱們合成一個。爲了防止複製,類必須顯式聲明其拷貝構造函數爲private。
說明:
(1)若是拷貝構造函數是私有的,將不容許用戶複製該類型對象。
(2)類的友元和成員仍能夠進行復制,若是也想禁止它們,可聲明一個私有的拷貝構造函數但不對其定義。聲明而不定義是合法的,但使用未定義的成員將致使連接失敗。
(3)經過聲明不定義私有拷貝構造函數,可禁止任何複製類類型對象的嘗試:用戶嘗試複製致使編譯錯誤,成員函數和友元複製致使連接錯誤。
(4)若是定義了拷貝構造函數,也必須定義默認構造函數。不容許複製的類對象只能做爲引用傳遞給函數或從函數返回,它們也不能做爲容器元素,嚴重侷限類的使用。