C++:拷貝建構函式&賦值運算子的過載函式
拷貝建構函式:
用一個已經存在的物件來生成一個相同型別的新物件。(淺拷貝)
預設的拷貝建構函式:
如果自定義了拷貝建構函式,編譯器就不在生成預設的拷貝建構函式。
如果沒有自定義拷貝建構函式,但在程式碼中用到了拷貝建構函式,編譯器會生成預設的拷貝建構函式。
深拷貝&淺拷貝:
系統預設的拷貝建構函式是淺拷貝,類中含有指標型別的變數,須自定義拷貝建構函式用深拷貝來實現。
淺拷貝只是對指標的拷貝,拷貝後兩個指標指向同一個記憶體空間,所指向的空間內容並沒有複製,而是由兩個物件共用。深拷貝不但對指標進行拷貝,而且對指標指向的內容進行拷貝,經深拷貝後的指標是指向兩個不同地址的指標。
如圖:
思考:
當物件中存在指標成員時,為什麼需要自己實現拷貝建構函式?如果不,會出現怎樣的問題?
看程式碼:
#include<iostream> class CGoods { public: CGoods(const char* name, double price, int amount)//帶有三個引數的建構函式 { std::cout << this << " :CGoods::CGoods(char*,float,int)" << std::endl; mname = new char[strlen(name) + 1](); strcpy(mname, name); mprice = price; mamount = amount; } CGoods()//不帶引數的建構函式 { std::cout << this << " :CGoods::CGoods()" << std::endl; mname = new char[1](); } ~CGoods() { std::cout << this << " :CGoods::~CGoods()" << std::endl; delete[] mname; } private: char* mname; double mprice; int mamount; }; int main() { CGoods good1("car1", 10.1, 10); CGoods good2 = good1; return 0; }
程式執行結果:調一次建構函式,調兩次解構函式。
分析:兩個物件的指標成員所指記憶體相同,這會導致什麼問題呢?
mname指標被分配一次記憶體,但是程式結束時該記憶體卻被釋放了兩次,會造成記憶體洩漏問題,這是一個不容忽視的問題。我們不得不自己寫一個拷貝建構函式。
CGoods(const CGoods &rhs) { std::cout << this << " :CGoods::CGoods(const CGoods &rhs)" << std::endl; mname = new char[strlen(rhs.mname) + 1](); strcpy(mname, rhs.mname); mprice = rhs.mprice; mamount = rhs.mamount; }
程式執行結果
注意:拷貝建構函式傳參必須傳引用。若正常傳,則會不斷地遞迴去生成新形參物件,最後導致棧溢位。也不能用*,若寫成 CGoods(const CGoods* rhs),就會變成一個建構函式,CGoods*傳的是已存在物件的地址。
賦值運算子的過載函式:
用一個已存在的物件賦值給相同型別的已存在物件。(淺拷貝)
預設賦值運算子的過載函式:
賦值運算子過載函式用於類物件的賦值操作,當我們未實現該函式時,編譯器會自動為我們實現該函式。同拷貝建構函式一樣,系統預設的賦值運算子過載函式是淺拷貝,類中含有指標型別的變數,須自定義賦值運算子過載函式用深拷貝來實現。
CGoods& operator=(const CGoods& rhs)
{
std::cout << this << " CGoods::operator=(const CGoods&)" << std::endl;
if (this != &rhs)//判斷是否給自己賦值
{
delete[] mname;//防止記憶體洩漏
mname = new char[strlen(rhs.mname) + 1]();
strcpy(mname, rhs.mname);
mprice = rhs.mprice;
mamount = rhs.mamount;
}
return *this;
}
int main()
{
CGoods good1("car1", 10.1, 10);
CGoods good2;
good2 = good1;
return 0;
}
程式執行結果:
思考:為什麼要避免自賦值呢?
1)自己給自己賦值完全是毫無意義,為了效率。
2)如果類的資料成員中含有指標,自賦值有時會導致災難性的後果。對於指標間的賦值,先要將p所指向的空間delete掉,然後再為p重新分配空間,將被拷貝指標所指的內容拷貝到p所指的空間。如果是自賦值,那麼p和被拷貝指標是同一指標,在賦值操作前對p的delete操作,將導致p所指的資料同時被銷燬。