複製構造函數和賦值構造函數

        複製構造函數看書看的迷糊,不就是用一個對象來初始化另一個同類型的對象嘛。後來看了下面轉載的文章,覺得就一下理解了很多。

        轉載: http://www.cnblogs.com/xyl-share-happy/archive/2012/08/05/2623715.html

        C++自動提供的成員函數有:默認構造函數,複製構造函數,默認析構函數,賦值操作符,地址操作符即this指針,這五種函數如果用戶沒有定義,則系統會自動創建一個。

     複製構造函數:用一個對象複製一個新的對象時被調用,聲明爲:類名(類名&對象名);

     把參數傳遞給函數有三種方法,一種是值傳遞,一種是傳地址,還有一種是傳引用。前者與後兩者不同的地方在於:當使用值傳遞的時候,會在函數裏面生成傳遞參數的一個副本,這個副本的內容是按位從原始參數那裏複製過來的,兩者的內容是相同的。當原始參數是一個類的對象時,它也會產生一個對象的副本,不過在這裏要注意。一般對象產生時都會觸發構造函數的執行,但是在產生對象的副本時卻不會這樣,這時執行的是對象的複製構造函數。爲什麼會這樣?嗯,一般的構造函數都是會完成一些成員屬性初始化的工作,在對象傳遞給某一函數之前,對象的一些屬性可能已經被改變了,如果在產生對象副本的時候再執行對象的構造函數,那麼這個對象的屬性又再恢復到原始狀態,這並不是我們想要的。所以在產生對象副本的時候,構造函數不會被執行,被執行的是一個默認的構造函數(複製構造函數)。當函數執行完畢要返回的時候,對象副本會執行析構函數,如果你的析構函數是空的話,就不會發生什麼問題,但一般的析構函數都是要完成一些清理工作,如釋放指針所指向的內存空間。這時候問題就可能要出現了。假如你在構造函數裏面爲一個指針變量分配了內存,在析構函數裏面釋放分配給這個指針所指向的內存空間,那麼在把對象傳遞給函數至函數結束返回這一過程會發生什麼事情呢?首先有一個對象的副本產生了,這個副本也有一個指針,它和原始對象的指針是指向同塊內存空間的。函數返回時,對象的析構函數被執行了,即釋放了對象副本里面指針所指向的內存空間,但是這個內存空間對原始對象還是有用的啊,就程序本身而言,這是一個嚴重的錯誤。然而錯誤還沒結束,當原始對象也被銷燬的時候,析構函數再次執行,對同一塊系統動態分配的內存空間釋放兩次是一個未知的操作,將會產生嚴重的錯誤。

上面說的就是我們會遇到的問題。解決問題的方法是什麼呢?首先我們想到的是不要以傳值的方式來傳遞參數,我們可以用傳地址或傳引用。沒錯,這樣的確可以避免上面的情況,而且在允許的情況下,傳地址或傳引用是最好的方法,但這並不適合所有的情況,有時我們不希望在函數裏面的一些操作會影響到函數外部的變量。那要怎麼辦呢?可以利用複製構造函數來解決這一問題。複製構造函數就是在產生對象副本的時候執行的,我們可以定義自己的複製構造函數。在複製構造函數裏面我們申請一個新的內存空間來保存構造函數裏面的那個指針所指向的內容。這樣在執行對象副本的析構函數時,釋放的就是複製構造函數裏面所申請的那個內存空間。

除了將對象傳遞給函數時會存在以上問題,還有一種情況也會存在以上問題,就是當函數返回對象時,會產生一個臨時對象,這個臨時對象和對象的副本性質差不多。
 
拷貝構造函數,經常被稱作X(X&),是一種特殊的構造函數,他由編譯器調用來完成一些基於同一類的其他對象的構件及初始化。它的唯一的一個參數(對象的引用)是不可變的(因爲是const型的)。這個函數經常用在函數調用期間於用戶定義類型的值傳遞及返回。拷貝構造函數要調用基類的拷貝構造函數和成員函數。如果可以的話,它將用常量方式調用,另外,也可以用非常量方式調用。 
在C++中,下面三種對象需要拷貝的情況。因此,拷貝構造函數將會被調用。
1). 一個對象以值傳遞的方式傳入函數體 
2). 一個對象以值傳遞的方式從函數返回 
3). 一個對象需要通過另外一個對象進行初始化 
以上的情況需要拷貝構造函數的調用。如果在前兩種情況不使用拷貝構造函數的時候,就會導致一個指針指向已經被刪除的內存空間。對於第三種情況來說,初始化和賦值的不同含義是構造函數調用的原因。事實上,拷貝構造函數是由普通構造函數和賦值操作賦共同實現的。描述拷貝構造函數和賦值運算符的異同的參考資料有很多。
 
拷貝構造函數不可以改變它所引用的對象,其原因如下:當一個對象以傳遞值的方式傳一個函數的時候,拷貝構造函數自動的被調用來生成函數中的對象。如果一個對象是被傳入自己的拷貝構造函數,它的拷貝構造函數將會被調用來拷貝這個對象,這樣複製纔可以傳入它自己的拷貝構造函數,這會導致無限循環。 
除了當對象傳入函數的時候被隱式調用以外,拷貝構造函數在對象被函數返回的時候也同樣的被調用。換句話說,你從函數返回得到的只是對象的一份拷貝。但是同樣的,拷貝構造函數被正確的調用了,你不必擔心。 
如果在類中沒有顯式的聲明一個拷貝構造函數,那麼,編譯器會私下裏爲你制定一個函數來進行對象之間的位拷貝(bitwise copy)。這個隱含的拷貝構造函數簡單的關聯了所有的類成員。顯式聲明的拷貝構造函數關聯的只是被實例化的類成員的缺省構造函數除非另外一個構造函數在類初始化或者在構造列表的時候被調用。 
 
如果一個類中有指針成員,使用缺省的複製構造函數初始化對象就會出現問題。爲了說明存在的問題,我們假定對象A與對象B是相同的類,有一個指針成員,指向對象C。當用對象B初始化對象A時,缺省的複製構造函數將B中每一個成員的值複製到A的對應的成員當中,但並沒有複製對象C。也就是說,對象A和對象B中的指針成員均指向對象C,實際上,我們希望對象C也被複制,得到C的對象副本D。否則,當對象A和B銷燬時,會對對象C的內存區重複釋放,而導致錯誤。爲了使對象C也被複制,就必須顯式定義複製構造函數。

發佈了23 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章