歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C++復制控制:拷貝構造函數

一、拷貝構造函數是一種特殊構造函數,具有單個形參,該形參(常用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個章節中:

  1. Linux-C成長之路(一):Linux下C編程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
  2. Linux-C成長之路(二):基本數據類型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
  3. Linux-C成長之路(三):基本IO函數操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
  4. Linux-C成長之路(四):運算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
  5. Linux-C成長之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
  6. Linux-C成長之路(六):函數要義 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
  7. Linux-C成長之路(七):數組與指針 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
  8. Linux-C成長之路(八):存儲類,動態內存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
  9. Linux-C成長之路(九):復合數據類型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
  10. Linux-C成長之路(十):其他高級議題

Copyright © Linux教程網 All Rights Reserved