❶ 請問構造函數與拷貝構造函數的相同點有哪些不同點有哪些
構造函數是一種特殊的類成員函數,是當創建一個類的對象時,它被調用來對類的數據成員進行初始化和分配內存。(構造函數的命名必須和類名完全相同)
拷貝構造函數是C++獨有的,它是一種特殊的構造函數,用基於同一類的一個對象構造和初始化另一個對象。
當沒有重載拷貝構造函數時,通過默認拷貝構造函數來創建一個對象
在C++中,3種對象需要復制,此時拷貝構造函數會被調用
1)一個對象以值傳遞的方式傳入函數體
2)一個對象以值傳遞的方式從函數返回
3)一個對象需要通過另一個對象進行初始化
什麼時候編譯器會生成默認的拷貝構造函數:
1)如果用戶沒有自定義拷貝構造函數,並且在代碼中使用到了拷貝構造函數,編譯器就會生成默認的拷貝構造函數。但如果用戶定義了拷貝構造函數,編譯器就不在生成。
2)如果用戶定義了一個構造函數,但不是拷貝構造函數,而此時代碼中又用到了拷貝構造函數,那編譯器也會生成默認的拷貝構造函數。
❷ 拷貝函數的形式c++
對於普通類型的對象來說,它們之間的復制是很簡單的,例如:
int a=100;
int b=a;
而類對象與普通對象不同,類對象內部結構一般較為復雜,存在各種成員變數。下面看一個類對象拷貝的簡單例子。 #include <iostream>
using namespace std;
class CA
{
public:
CA(int b)
{
a=b;
}
void Show ()
{
cout<<a<<endl;
}
private:
int a;
};
int main()
{
CA A(100);
CA B=A;
B.Show ();
return 0;
}
運行程序,屏幕輸出100。從以上代碼的運行結果可以看出,系統為對象B分配了內存並完成了與對象A的復制過程。就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個復制過程的。下面我們舉例說明拷貝構造函數的工作過程。 #include <iostream>
using namespace std;
class CA
{
public:
CA(int b)
{
a=b;
}
CA(const CA& C)
{
a=C.a;
}
void Show()
{
cout<<a<<endl;
}
private:
int a;
};
int main()
{
CA A(100);
CA B=A;
B.Show ();
return 0;
}
CA(const CA& C)就是我們自定義的拷貝構造函數。可見,拷貝構造函數是一種特殊的構造函數,函數的名稱必須和類名稱一致,它的唯一的一個參數是本類型的一個引用變數,該參數是const類型,不可變的。例如:類X的拷貝構造函數的形式為X(X& x)。
當用一個已初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象需要拷貝時,拷貝構造函數將會被調用。以下情況都會調用拷貝構造函數:
一個對象以值傳遞的方式傳入函數體
一個對象以值傳遞的方式從函數返回
一個對象需要通過另外一個對象進行初始化。
如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝,後面將進行說明。
自定義拷貝構造函數是一種良好的編程風格,它可以阻止編譯器形成默認的拷貝構造函數,提高源碼效率。
淺拷貝和深拷貝
在某些狀況下,類內成員變數需要動態開辟堆內存,如果實行位拷貝,也就是把對象里的值完全復制給另一個對象,如A=B。這時,如果B中有一個成員變數指針已經申請了內存,那A中的那個成員變數也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。
深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當這個類的對象發生復制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。下面舉個深拷貝的例子。
#include <iostream>
using namespace std;
class CA
{
public:
CA(int b,char* cstr)
{
a=b;
str=new char[b];
strcpy(str,cstr);
}
CA(const CA& C)
{
a=C.a;
str=new char[a]; //深拷貝
if(str!=0)
strcpy(str,C.str);
}
void Show()
{
cout<<str<<endl;
}
~CA()
{
delete str;
}
private:
int a;
char *str;
};
int main()
{
CA A(10,"Hello!");
CA B=A;
B.Show();
return 0;
}
❸ C++11 能否禁止類默認的復制構造函數
為了能夠顯式的禁用某個函數,C++11 標准引入了一個新特性:deleted 函數。程序員只需在函數聲明後加上「=delete;」,就可將該函數禁用。類 X 的拷貝構造函數以及拷貝賦值操作符聲明為 deleted 函數,就可以禁止類 X 對象之間的拷貝和賦值
❹ c++中拷貝構造函數問題
1、對,如果你定義了任意形式的構造函數,默認構造函數就沒有了,如果需要就要自己顯示定義。
2、簡單來說,當你將構造函數聲明為private,那麼就只有你的類的內部函數才可以構造類對象,而不允許外部構造。 這樣編譯器就不能使用構造函數了。 還有的情況也是可以:比如 你要用類似工廠類, 通過getInstance函數產生類,而不允許直接生成類,在getInstance中構造類。
❺ C++中什麼情況下編譯器會自動生成默認的拷貝構造函數
如果沒有顯式的指定構造函數,編譯器會提供無參的構造函數和拷貝構造函數。
❻ c++中拷貝構造函數的用法
1.什麼是拷貝構造函數:
拷貝構造函數嘛,當然就是拷貝和構造了。(其實很多名字,只要靜下心來想一想,就真的是顧名思義呀)拷貝又稱復制,因此拷貝構造函數又稱復制構造函數。網路上是這樣說的:拷貝構造函數,是一種特殊的構造函數,它由編譯器調用來完成一些基於同一類的其他對象的構建及初始化。其唯一的參數(對象的引用)是不可變的(const類型)。此函數經常用在函數調用時用戶定義類型的值傳遞及返回。
2.拷貝構造函數的形式
Class X
{
public:
X();
X(const X&);//拷貝構造函數
}
2.1為什麼拷貝構造參數是引用類型?
其原因如下:當一個對象以傳遞值的方式傳一個函數的時候,拷貝構造函數自動被調用來生成函數中的對象(符合拷貝構造函數調用的情況)。如果一個對象是被傳入自己的拷貝構造函數,它的拷貝構造函數將會被調用來拷貝這個對象,這樣復制才可以傳入它自己的拷貝構造函數,這會導致無限循環直至棧溢出(Stack Overflow)。
3.拷貝構造函數調用的三種形式
3.1.一個對象作為函數參數,以值傳遞的方式傳入函數體;
3.2.一個對象作為函數返回值,以值傳遞的方式從函數返回;
3.3.一個對象用於給另外一個對象進行初始化(常稱為復制初始化)。
總結:當某對象是按值傳遞時(無論是作為函數參數,還是作為函數返回值),編譯器都會先建立一個此對象的臨時拷貝,而在建立該臨時拷貝時就會調用類的拷貝構造函數。
4.深拷貝和淺拷貝
如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝。(位拷貝又稱淺拷貝,後面將進行說明。)自定義拷貝構造函數是一種良好的編程風格,它可以阻止編譯器形成默認的拷貝構造函數,提高源碼效率。
在某些狀況下,類內成員變數需要動態開辟堆內存,如果實行位拷貝,也就是把對象里的值完全復制給另一個對象,如A=B。這時,如果B中有一個成員變數指針已經申請了內存,那A中的那個成員變數也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。事實上這就要用到深拷貝了,要自定義拷貝構造函數。
深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當這個類的對象發生復制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。下面舉個深拷貝的例子。
#include <iostream>
using namespace std;
class CA
{
public:
CA(int b,char* cstr)
{
a=b;
str=new char[b];
strcpy(str,cstr);
}
CA(const CA& C)
{
a=C.a;
str=new char[a]; //深拷貝
if(str!=0)
strcpy(str,C.str);
}
void Show()
{
cout<<str<<endl;
}
~CA()
{
delete str;
}
private:
int a;
char *str;
};
int main()
{
CA A(10,"Hello!");
CA B=A;
B.Show();
return 0;
}
淺拷貝資源後在釋放資源的時候會產生資源歸屬不清的情況導致程序運行出錯。一定要注意類中是否存在指針成員。
5.拷貝構造函數與「=「賦值運算符
例如:
class CExample
{};
int main()
{
CExample e1 = new CExample;
CExample e2 = e1;//調用拷貝構造函數
CExample e3(e1);//調用拷貝構造函數
CExample e4;
e4 = e1;//調用=賦值運算符
}
通常的原則是:①對於凡是包含動態分配成員或包含指針成員的類都應該提供拷貝構造函數;②在提供拷貝構造函數的同時,還應該考慮重載"="賦值操作符號。
❼ 什麼時候需要自定義復制構造函數若程序員沒有定義復制構造函數,則編譯器自動生成一個默認的復制構造函數
系統默認的復制構造函數是不帶參數的,沒有參數傳遞的時候可以不用,即使有參數,復制構造函數可以不寫,用引用代替,比較方便。
要是既沒有引用,又沒有復制構造函數,會提示缺少構造函數,,default constructor
❽ C++ 空類,默認產生哪些成員函數
。 020202 默認構造函數、默認拷貝構造函數、默認析構函數、默認賦值運算符 這四個是我們通常大都知道的。但是除了這四個,還有兩個,那就是取址運算符和 取址運算符 const即總共有六個函數。020202 一個示例如下: 1:class Empty 2:02 3: { 4:02 5:public: 6:02 7: Empty(); // 預設構造函數 8:02 9: Empty( const Empty& ); // 拷貝構造函數 10:02 11: ~Empty(); // 析構函數 12:02 13: Empty& operator=( const Empty& ); // 賦值運算符 14:02 15: Empty* operator&(); // 取址運算符 16:02 17:const Empty* operator&() const; // 取址運算符 const 18:02 19: };02 但是,C++默認生成的函數,只有在被需要的時候,才會產生。即當我們定義一個類,而不創建類的對象時,就不會創建類的構造函數、析構函數等。021.對於一個類X,如果一個構造函數的第一個參數是下列之一:020202 a) X&020202 b) const X&020202 c) volatile X&020202 d) const volatile X&020202 且沒有其他參數或其他參數都有默認值,那麼這個函數是拷貝構造函數。020202 X::X(const X&); //是拷貝構造函數020202 X::X(X&, int=1); //是拷貝構造函數022.類中可以存在超過一個拷貝構造函數,class X {public:0202020202 X(const X&);
0202020202 X(X&);0202020202020202020202 // OK
};注意,如果一個類中只存在一個參數為X&的拷貝構造函數,那麼就不能使用const X或volatile X的對象實行拷貝初始化。class X {public:02020202 X();
02020202 X(X&);
};const X cx;
02020202020202 X x = cx;020202 // error020202 如果一個類中沒有定義拷貝構造函數,那麼編譯器會自動產生一個默認的拷貝構造函數。020202 這個默認的參數可能為X::X(const X&)或X::X(X&),由編譯器根據上下文決定選擇哪一個。02測試代碼如下: 1:#include <iostream> 2:usingnamespace std; 3:02 4:class X { 5:public: 6: X() { cout << "X()" << endl;} 7: X(X& x, int a = 1) {cout << "X(X&)" << endl;} 8: X(const X& x) {cout << "const X(X&)" << endl;} 9: X(volatile X& x) {cout << "volatile X(X&)" << endl;} 10: }; 11:02 12:int main(int argc, char** argv) { 13: X x1; 14: X x2(x1); 15:const X x3; 16: X x4(x3); 17:volatile X x5; 18: X x6(x55; 19:return 0; 20: }運行結果:而如果注釋掉上面的8 和9行(即取消const 和volatile 拷貝構造函數),會編譯錯誤,如下:020202 默認拷貝構造函數的行為如下:020202 默認的拷貝構造函數執行的順序與其他用戶定義的構造函數相同,執行先父類後子類的構造。020202 拷貝構造函數對類中每一個數據成員執行成員拷貝(memberwise Copy)的動作。020202 a)如果數據成員為某一個類的實例,那麼調用此類的拷貝構造函數。020202 b)如果數據成員是一個數組,對數組的每一個執行按位拷貝。020202 c)如果數據成員是一個數量,如int,double,那麼調用系統內建的賦值運算符對其進行賦值。02023. 拷貝構造函數不能由成員函數模版生成。struct X{
02020202020202020202020202 template<typename T>
02020202020202020202020202 X( const T& );020202 // NOT ctor, T can't be X020202020202 template<typename T>
02020202020202020202020202 perator=( const T& ); // NOT ass't, T can't be X
02020202020202 };02020202020202 原因很簡單,成員函數模版並不改變語言的規則,而語言的規則說,如果程序需要一個拷貝構造函數而你沒
有聲明它,那麼編譯器會為你自動生成一個。所以成員函數模版並不會阻止編譯器生成拷貝構造函數, 賦值運算
❾ 什麼是拷貝構造函數拷貝構造函數何時被調用
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 ctor, T can't be X
template typename T>
operator=( const T& ); // NOT 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) 一般來說是在數據成員包含指針對象的時候,應付兩種不同的處理需求的 一種是復制指針對象,一種是引用指針對象 大多數情況下是復制,=則是引用對象的
例子:
class A
{
int nLen;
char * pData;
}
顯然
A a, b;
a=b的時候,對於pData數據存在兩種需求
第一種
a.pData = new char [nLen];
memcpy(a.pData, b.pData, nLen);
另外一種(引用方式):
a.pData = b.pData
通過對比就可以看到,他們是不同的
往往把第一種用使用,第二種用=實現
你只要記住拷貝構造函數是用於類中指針,對象間的COPY
3) 拷貝構造函數首先是一個構造函數,它調用的時候產生一個對象,是通過參數傳進來的那個對象來初始化,產生的對象。
operator=();是把一個對象賦值給一個原有的對象,所以如果原來的對象中有內存分配要先把內存釋放掉,而且還要檢查一下兩個對象是不是同一個對象,如果是的話就不做任何操作。
還要注意的是拷貝構造函數是構造函數,不返回值
而賦值函數需要返回一個對象自身的引用,以便賦值之後的操作
4) 在形式上
類名(形參表列); //普通構造函數的聲明,如Box(int h,int w,int len);
類名(類名& 對象名); //復制構造函數的聲明,如Box(Box &b);
5) 在建立對象時,實參類型不同。系統會根據實參的類型決定調用普通構造函數或復制構造函數。如:
Box box1(12,15,16); //實參為整數,調用普通構造函數
Box box2(box1); //實參是對象名,調用復制構造函數
❿ 如何禁止對象的拷貝和賦值操作
在c++的實現過程中,我們在有些特殊情況下,往往希望禁止對象的拷貝和賦值操作,最常見的方法是把對應類中的拷貝構造函數和賦值函數定義為私有類型,並且沒有任何實現代碼。這樣做的好處在於編譯器能在編譯期間幫助程序員發現錯誤,即使編譯能夠通過,也會在鏈接期間發現錯誤。
對於這些重復的勞動是令人討厭的,我們是否有一種相對容易的方法來實現這些限制呢?繼承的特性能夠輕易的搞定這些羅嗦的事情,只要我們在對應類的聲明中私有繼承下面的類就萬事大吉了:-)。
class Nonable
{
Nonable& operator=( const Nonable &rhs );
Nonable( const Nonable &rhs );
protected:
Nonable(){}
virtual ~Nonable(){}
};
測試它!
#include <iostream>
using namespace std;
class Nonable
{
Nonable& operator=( const Nonable &rhs );
Nonable( const Nonable &rhs );
protected:
Nonable(){}
virtual ~Nonable(){}
};
class C : private Nonable
{
public:
C()
{
cout << "C constructor" << endl;
}
virtual ~C()
{
cout << "C destructor" << endl;
}
};
int main( void )
{
C c1;
C c2 = c1;
C c3;
c3 = c1;
return 0;
}