把參數傳遞給函數有三種方法,一種是傳值,一種是傳地址,一種是傳引用。傳值與其他兩種方式不同的地方在於 當使用
傳值方式的時候,會在函數里面生成傳遞參數的一個副本,這個副本的內容是按位從原始參數那里復制過來的,兩者的內容是相同的。
當原始參數是一個類的對象時,它也會產生一個對象的副本,此時需要注意:一般對象在創建時都會調用構造函數來進行初始化,但是
在產生對象的副本時如果再執行對象的構造函數,那么這個對象的屬性又再恢復到原始狀態,這就不是我們希望的了。所以在產生對象
副本的時候,構造函數不會被執行,被執行的是一個默認的默認的拷貝構造函數。
問題原因:
當函數執行完畢要返回的時候對象副本會執行析構函數,
如果你的析構函數是空的話,也不會發生什么問題,但一般的析構函數都是要完成一些清理工作,如釋放指針所指向的內存空間,這時候
可能就會出問題。 譬如:我們在構造函數中為一個指針變量分配了內存,在析構函數中釋放給這個指針所指向的內存空間,在把對象傳遞
給函數至函數結束返回 的這個過程中 首先有一個對象的副本產生了。這個副本也有一個指針,它和原始對象的指針是指向同塊內存空間的,
函數返回時,副本對象的析構函數執行了,釋放了副本對象中指針指向的內存空間,但是這個內存空間對於原始對象而言還是有效地,
這是第一個問題,后面當原始對象也被銷毀的時候,原始對象的析構函數執行,還會對那塊已經釋放掉的內存空間再次釋放,產生嚴重
錯誤,這是第二個問題。
解決方法:
既然傳值有這樣的問題,那是否可以使用傳地址或者傳引用的方式解決這種問題呢?
事實上傳地址和傳引用確實可以解決這種問題,但是這並不適用所有的情況,有時我們不希望在函數里面的一些操作會影響到函數外部的變量。
為了解決這種問題,此時就需要用到拷貝構造函數,拷貝構造函數就是在產生副本對象的時候執行的,在拷貝構造函數里面我們申請一個新的內存空間,
這樣在副本對象執行析構函數時其釋放的就是新的內存空間,從而解決這個問題。
適用范圍:
1. 一個對象以值傳遞的方式傳入函數體
2. 一個對象以值傳遞的方式從函數返回
3. 一個對象需要通過另外一個對象進行初始化
拷貝構造函數不可以改變它所引用的對象,如果可以改變,那么將導致無限循環,如果類中沒有顯示的聲明一個拷貝構造函數,
那么編譯器會為你隱式定義一個位拷貝的默認拷貝構造函數
如果不准備使用按值傳遞對象,那么其實是不需要拷貝構造函數,但是我們如果不寫拷貝構造函數,編譯器又可能為我們創建一個默認的。
那么如何保證一個對象將永遠不會被通過按值傳遞方式傳遞呢?
聲明一個私有的拷貝構造函數,甚至不必去定義它,除非成員函數或友元函數需要執行按值傳遞方式的傳遞。否則,如果用戶試圖用按值傳遞方式傳遞
或返回對象,編譯器將會報錯。這是因為拷貝構造函數是私有的。因為已經顯示地聲明我們接管了這項工作,所以編譯器不再創建默認的拷貝構造函數
