在類內給成員變量分配內存使用new和delete中最常遇到的問題就是內存泄漏,造成這樣的原因往往是不對應,而且對C++的構造函數和析構函數不是很瞭解。C++會默認提供下面的函數:
• 默認構造函數,如果沒有定義構造函數:
• 默認析構函數,如果沒有定義;
• 複製構造函數,如果沒有定義;
• 賦值運算符,如果沒有定義:
• 地址運算符,如果沒有定義。
如果沒有構造函數就會存在提供默認構造函數,如果定義了,就不會提供,但是複製構造函數是默認隱藏提供的,而且賦值運算符,即將一個類對象賦值給同類的另一個對象。這裏分別講解需要注意的要點。
1、複製構造函數:複製構造函數用於將一個對象複製到另一個對象,其原型爲: Classname (const Classname &);
需要知道何時被使用,其使用的地方大致集中在3處:在用一個對象給另一個對像用=初始化時;在傳入實參的時候會默認複製一個副本,調用複製構造函數;在函數返回值的時候也會返回副本,即調用一個複製構造函數。第二第三個都比較好理解,着重說一下第一個,因爲涉及到好多的初始化方式:
StringBad ditto (motto) ; // calls StringBad (const StringBad A)
StringB admetoo = motto; // calls StringBad (const StringBad &)
StringBad also = StringBad(motto); // calls StringBad (const StringBad &)
StringBad * pStringBad = new StringBad (motto);
以其中的第二條作爲例子,說下過程,這裏首先調用admetoo的複製構造函數,並把motto作爲參數穿進去,將motto的所有成員變量的值都傳給admetoo的成員變量,這樣看上去兩者就有相同的值了。
隱式的構造函數會帶來很多不便,隱式的複製構造函數會將原對象的屬性值原封不動的複製給新對象,這樣在複製指針的時候實際上覆制的爲同一個指針,當操作被複制的新對象的指針後,原來對象的指針也會受到影響。所以在一個類中含有指針,或者含有靜態變量,或者在構造函數中使用new的一般都要顯示的重新定義複製構造函數(此過程被叫做深度複製)。
2、默認的賦值運算符:Class_name & Class_name::operator=(const Class_name &) ;
雖然與複製構造函數稍有差別,但是在使用=的時候,實際是將原對象的數據賦值給新對象,新對象的指針等數據依然是原對象的值,會導致指針的不恰當操作。
//這種方式是:用有參數的和沒參數的構造函數分別定義兩個對象,然後讓對象1給對象2賦值
StringBad headlinel("CeleryS talks at Midnight");
StringBad knot;
knot = headlinel; // assignment operator invoked
其最標準的寫法如下:
StringBad & StringBad::operator = (const StringBad & st)
{
if (this == &st) //object assigned to itself
return *this; // all done
delete [] str; // free old string
len = st.len;
str = new char [len + 1 ]; // get space for new string
std :: strcpy(str, st.str ); // copy the string
return *this ; // return reference to invoking
}
其實說到底就是在重載=,當然還可以傳入不同的參數來重載出不同的運算符,如傳入char*,就可以將char*賦值給stringbad。
3.包含類成員的類的逐成員賦值:
如果類包含多個類,但是這些類都需要new屬性,那麼還需要在爲這個大類重寫複製構造函數嗎?答案是不用的,這個類是有一定的智能型的,根據組件類的複製構造函數去逐個賦值。
總結來說,當構造函數中包含new等動態分配內存的操作時,就要對賦值構造函數,賦值運算符等進行顯示的定義,從而進行深度複製,防止使用默認的構造函數,使指針操作異常。