一、拷貝賦值函數與拷貝構造函數
拷貝賦值函數和拷貝構造函數,都是通過已存在的一個類對象對另外一個類對象進行初始化的操作,但兩者有着本質上的區別:
- 拷貝賦值函數:針對一個已經存在的對象進行初始化操作。
- 拷貝構造函數:針對一個新創建的對象進行初始化操作。
Plane a1;
//拷貝構造
Plane a2 = a1;
Plane a3;
//拷貝賦值
a2 = a3;
二、拷貝構造函數
拷貝構造函數,又稱複製構造函數,它可以生成一個當前對象的副本,然後將其的參數成員逐個拷貝到正在創建的本類對象中。
class Plane{
public:
Plane(int height,int width){
height = 10;
width = 20;
}
//聲明拷貝構造函數
Plane(const Plane &a);
int getH(){return height;}
int getW(){return width;}
private:
int height;
int width;
};
//定義拷貝構造函數
Plane::Plane(Plane &a){
height = a.height;
width = a.width;
}
在C++中,拷貝構造函數的有三種情況會被調用。
- 一個對象用於給另外一個對象進行初始化(常稱爲賦值初始化)。
Plane a1(15,25);
Plane a2(a1);
- 一個對象作爲函數返回值,以值傳遞的方式從函數返回;
Plane fun1(){
Plane a1(15,25);
return a1;
}
int mian(){
Plane a2;
a2 = fun1();
}
- 一個對象作爲函數參數,以值傳遞的方式傳入函數體;
void fun2(Plane a){
//具體代碼
}
int main(){
Plane a1(15,25);
fun2(a1);
}
值得注意的是,在後兩種情況下如果不使用拷貝構造函數,就會導致指針指向一個已經被刪除的內存空間。
三、淺拷貝、深拷貝
淺拷貝和深拷貝都屬於拷貝構造函數。
- 淺拷貝:在拷貝的同時增加一個指針,但是通過淺拷貝的對象和原有對象共用同一個內存地址,當對象被修改時,通過淺拷貝的對象均被修改;如果對象調用析構函數時,則會出現同一資源被多次釋放的錯誤。
class Plane{
public:
Plane(int height,int width){
height = 10;
width = 20;
}
int getH(int h){return height;}
int getW(int w){return width;}
int changeH(int h){return height = h;}
int changeW(int w){return width = w;}
private:
int height;
int width;
};
int main(){
Plane a1(15,25);
Plane a2(a1);
a1.changeH(30);
//此時a1和a2的height的值都是30
cout<<a1.getH()<<a2.getH()<<endl;
}
- 深拷貝:在拷貝的同時增加一個指針,並重新申請一塊新的內存空間,使得該指針指向新申請的內存空間。使用深拷貝後能夠避免出現淺拷貝中的錯誤情況。
class Plane{
public:
Plane(int height,int width){
height = 10;
width = 20;
}
int getH(int h){return height;}
int getW(int w){return width;}
int changeH(int h){return height = h;}
int changeW(int w){return width = w;}
private:
int height;
int width;
};
int main(){
Plane a1(15,25);
Plane a2(a1);
a1.changeH(30);
//此時a1的height值是30,而a2的height值則不變
cout<<a1.getH()<<a2.getH()<<endl;
}
四、使用的注意事項
- 拷貝構造函數必須以引用的形式進行傳遞
如果拷貝構造函數進行值傳遞的話,首先會將實參傳遞給形參,此時系統又會默認調用拷貝構造函數,這樣就會不斷進行遞歸調用,從而陷入死循環。 - 拷貝構造函數最好使用const關鍵字
拷貝構造函數的目的是對對象進行成員複製,而不是修改原對象,爲了避免複製過程中發生對原對象的修改,應該使用const關鍵字。
Plane(const Plane &p);