1.什麼是拷貝構造函數:
CA(const CA& C)就是咱們自定義的拷貝構造函數。可見,拷貝構造函數是一種特殊的構造函數,函數的名稱必須和類名稱一致,它的惟一的一個參數是本類型的一個引用變量,該參數是const類型,不可變的。例如:類X的拷貝構造函數的形式爲X(X& x)。
當用一個已初始化過了的自定義類類型對象去初始化另外一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象須要拷貝時,拷貝構造函數將會被調用。如下狀況都會調用拷貝構造函數:
① 程序中須要新創建一個對象,並用另外一個同類的對象對它初始化,如前面介紹的那樣。
② 當函數的參數爲類的對象時。在調用函數時須要將實參對象完整地傳遞給形參,也就是須要創建一個實參的拷貝,這就是按實參複製一個形參,系統是經過調用複製構造函數來實現的,這樣能保證形參具備和實參徹底相同的值。
③ 函數的返回值是類的對象。在函數調用完畢將返回值帶回函數調用處時。此時須要將函數中的對象複製一個臨時對象並傳給該函數的調用處。如
Box f( ) //函數f的類型爲Box類類型
{Box box1(12,15,18);
return box1; //返回值是Box類的對象
}
int main( )
{Box box2; //定義Box類的對象box2
box2=f( ); //調用f函數,返回Box類的臨時對象,並將它賦值給box2
}
若是在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝,後面將進行說明。
自定義拷貝構造函數是一種良好的編程風格,它能夠阻止編譯器造成默認的拷貝構造函數,提升源碼效率。
淺拷貝和深拷貝
在某些情況下,類內成員變量須要動態開闢堆內存,若是實行位拷貝,也就是把對象裏的值徹底複製給另外一個對象,如A=B。這時,若是B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。
深拷貝和淺拷貝能夠簡單理解爲:若是一個類擁有資源,當這個類的對象發生複製過程的時候,資源從新分配,這個過程就是深拷貝,反之,沒有從新分配資源,就是淺拷貝。
2.C++拷貝構造函數的幾個細節
1) 如下函數哪一個是拷貝構造函數,爲何?
1.X::X( const X&);
2.X::X(X);
3.X::X(X&, int a=1);
4.X::X(X&, int a=1, b=2);
解答:1) 對於一個類X,若是一個構造函數的第一個參數是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且沒有其餘參數或其餘參數都有默認值,那麼這個函數是拷貝構造函數.
1.X::X( const X&); //是拷貝構造函數
2.X::X(X&, int =1); //是拷貝構造函數
2) 一個類中能夠存在多於一個的拷貝構造函數嗎?
解答:類中能夠存在超過一個拷貝構造函數,
1.class X {
2.public :
3. X( const X&);
4. X(X&); // OK
5.};
注意,若是一個類中只存在一個參數爲X&的拷貝構造函數,那麼就不能使用const X或volatile X的對象實行拷貝初始化.
1.class X {
2.public :
3. X();
4. X(X&);
5.};
6.
7.const X cx;
8.X x = cx; // error
若是一個類中沒有定義拷貝構造函數,那麼編譯器會自動產生一個默認的拷貝構造函數.
這個默認的參數可能爲X::X(const X&)或X::X(X&),由編譯器根據上下文決定選擇哪個.
默認拷貝構造函數的行爲以下:
默認的拷貝構造函數執行的順序與其餘用戶定義的構造函數相同,執行先父類後子類的構造.
拷貝構造函數對類中每個數據成員執行成員拷貝(memberwise Copy)的動做.
a)若是數據成員爲某一個類的實例,那麼調用此類的拷貝構造函數.
b)若是數據成員是一個數組,對數組的每個執行按位拷貝.
c)若是數據成員是一個數量,如int,double,那麼調用系統內建的賦值運算符對其進行賦值.
3) 拷貝構造函數不能由成員函數模版生成.
struct X {
template < typename T>
X( const T& ); // NOT copy ctor, T can't be X
template < typename T>
operator=( const T& ); // NOT copy ass't, T can't be X
};
緣由很簡單, 成員函數模版並不改變語言的規則,而語言的規則說,若是程序須要一個拷貝構造函數而你沒有聲明它,那麼編譯器會爲你自動生成一個.因此成員函數模版並不會阻止編譯器生成拷貝構造函數, 賦值運算符重載也遵循一樣的規則
3.拷貝構造函數與賦值函數的異同:
1) 拷貝構造,是一個的對象來初始化一片內存區域,這片內存區域就是你的新對象的內存區域賦值運算,對於一個已經被初始化的對象來進行operator=操做
class A;
A a;
A b=a; //拷貝構造函數調用
//或
A b(a); //拷貝構造函數調用
///////////////////////////////////
A a;
A b;
b =a; //賦值運算符調用
你只須要記住,在C++語言裏,
String s2(s1);
String s3 = s1;
只是語法形式的不一樣,意義是同樣的,都是定義加初始化,都調用拷貝構造函數。
2) 通常來講是在數據成員包含指針對象的時候,應付兩種不一樣的處理需求的 一種是複製指針對象,一種是引用指針對象 copy大多數狀況下是複製,=則是引用對象的
例子:
class A
{
int nLen;
char * pData;
}
顯然
A a, b;
a=b的時候,對於pData數據存在兩種需求
第一種copy
a.pData = new char [nLen];
memcpy(a.pData, b.pData, nLen);
另一種(引用方式):
a.pData = b.pData
經過對比就能夠看到,他們是不一樣的
每每把第一種用copy使用,第二種用=實現
你只要記住拷貝構造函數是用於類中指針,對象間的COPY
3) 拷貝構造函數首先是一個構造函數,它調用的時候產生一個對象,是經過參數傳進來的那個對象來初始化,產生的對象。
operator=();是把一個對象賦值給一個原有的對象,因此若是原來的對象中有內存分配要先把內存釋放掉,並且還要檢查一下兩個對象是否是同一個對象,若是是的話就不作任何操做。
還要注意的是拷貝構造函數是構造函數,不返回值
而賦值函數須要返回一個對象自身的引用,以便賦值以後的操做
4) 在形式上
類名(形參表列); //普通構造函數的聲明,如Box(int h,int w,int len);
類名(類名& 對象名); //複製構造函數的聲明,如Box(Box &b);
5) 在創建對象時,實參類型不一樣。系統會根據實參的類型決定調用普通構造函數或複製構造函數。如:
Box box1(12,15,16); //實參爲整數,調用普通構造函數
Box box2(box1); //實參是對象名,調用複製構造函數編程
拷貝構造函數,是一種特殊的構造函數,它由編譯器調用來完成一些基於同一類的其餘對象的構建及初始化。其惟一的參數(對象的引用)是不可變的(const類型)。此函數常常用在函數調用時用戶定義類型的值傳遞及返回。拷貝構造函數要調用基類的拷貝構造函數和成員函數。
在C++中,下面三種對象須要調用拷貝構造函數: 數組
1) 一個對象以值傳遞的方式傳入函數體; 函數
2) 一個對象以值傳遞的方式從函數返回; 指針
3) 一個對象須要經過另一個對象進行初始化; 對象
若是在前兩種狀況不使用拷貝構造函數的時候,就會致使一個指針指向已經被刪除的內存空間。對於第三種狀況來講,初始化和賦值的不一樣含義是構造函數調用的緣由。事實上,拷貝構造函數是由普通構造函數和賦值操做符共同實現的。描述拷貝構造函數和賦值運算符的異同的參考資料有不少。 內存
拷貝構造函數不能夠改變它所引用的對象,其緣由以下:當一個對象以傳遞值的方式傳一個函數的時候,拷貝構造函數自動的被調用來生成函數中的對象。若是一個對象是被傳入本身的拷貝構造函數,它的拷貝構造函數將會被調用來拷貝這個對象這樣複製才能夠傳入它本身的拷貝構造函數,這會致使無限循環直至棧溢出(Stack Overflow)。除了當對象傳入函數的時候被隱式調用之外,拷貝構造函數在對象被函數返回的時候也一樣的被調用。資源