賦值函式

每一個類只有一個析構函式,但可以有多個構造函式(包含一個默認構造函式,一個拷貝構造函式,和其他普通構造函式)和多個賦值函式(包含一個拷貝賦值函式,其他的為普通賦值函式)。

定義

每個類只有一個賦值函式.

一般情況下,對於任意一個類A,如果程式設計師不顯示的聲明和定義上述函式,C++編譯器將會自動的為A產生4個public inline 的默認函式,這4個函式最常見的形式為:

A() //默認構造函式

A(const A&) //默認拷貝構造函式

~A() //默認析構函式

A& operator = (const A &) //默認賦值函式。

注意事項

由於並非所有的對象都會使用拷貝構造函式和賦值函式,程式設計師可能對這兩個函式有些輕視。請先記住以下的警告,在閱讀正文時就會多心:

1.如果不主動編寫拷貝構造函式和賦值函式,編譯器將以“位拷貝”的方式自動生成預設的函式。倘若類中含有指針變數,那么這兩個預設的函式就隱含了錯誤。以類String的兩個對象a,b為例,假設a.m_data的內容為“hello”,b.m_data的內容為“world”。

現將a賦給b,預設賦值函式的“位拷貝”意味著執行b.m_data = a.m_data。這將造成三個錯誤:一是b.m_data原有的記憶體沒被釋放,造成記憶體泄露;二是b.m_data和a.m_data指向同一塊記憶體,a或b任何一方變動都會影響另一方;三是在對象被析構時,m_data被釋放了兩次。

2.拷貝構造函式和賦值函式非常容易混淆,常導致錯寫、錯用。拷貝構造函式是在對象被創建時調用的,而賦值函式只能被已經存在了的對象調用。以下程式中,第三個語句和第四個語句很相似,你分得清楚哪個調用了拷貝構造函式,哪個調用了賦值函式嗎?

String a(“hello”);

String b(“world”);

String c = a; // 調用了拷貝構造函式,最好寫成 c(a);

c = b; // 調用了賦值函式

本例中第三個語句的風格較差,宜改寫成String c(a) 以區別於第四個語句。

類String的拷貝構造函式與賦值函式

// 拷貝構造函式

String::String(const String &other)

{

// 允許操作other的私有成員m_data

int length = strlen(other.m_data);

m_data = new char[length+1];

strcpy(m_data, other.m_data);

}

// 賦值函式

String & String::operator =(const String &other)

{

// (1) 檢查自賦值

if(this == &other)

return *this;

// (2) 釋放原有的記憶體資源

delete [] m_data;

// (3)分配新的記憶體資源,並複製內容

int length = strlen(other.m_data);

m_data = new char[length+1];

strcpy(m_data, other.m_data);

// (4)返回本對象的引用

return *this;

}

類String拷貝構造函式與普通構造函式的區別是:在函式入口處無需與NULL進行比較,這是因為“引用”不可能是NULL,而“指針”可以為NULL。

類String的賦值函式比構造函式複雜得多,分四步實現:

(1)第一步,檢查自賦值。你可能會認為多此一舉,難道有人會愚蠢到寫出 a = a 這樣的自賦值語句!的確不會。但是間接的自賦值仍有可能出現,例如

// 內容自賦值

b = a;

c = b;

a = c;

// 地址自賦值

b = &a;

a = *b;

也許有人會說:“即使出現自賦值,我也可以不理睬,大不了化點時間讓對象複製自己而已,反正不會出錯!”

他真的說錯了。看看第二步的delete,自殺後還能複製自己嗎?所以,如果發現自賦值,應該馬上終止函式。注意不要將檢查自賦值的if語句

if(this == &other)

錯寫成為

if( *this == other)

(2)第二步,用delete釋放原有的記憶體資源。如果不即時釋放,就沒機會再釋放,將造成記憶體泄露。

(3)第三步,分配新的記憶體資源,並複製字元串。注意函式strlen返回的是有效字元串長度,不包含結束符‘\0’。函式strcpy則連‘\0’一起複製。

(4)第四步,返回本對象的引用,目的是為了實現象 a = b = c 這樣的鏈式表達。注意不要將 return *this 錯寫成 return this 。那么能否寫成return other 呢?效果不是一樣嗎?

不可以!因為我們不知道參數other的生命期。有可能other是個臨時對象,在賦值結束後它馬上消失,那么return other返回的將是垃圾。

偷懶的辦法處理拷貝構造函式與賦值函式

如果我們實在不想編寫拷貝構造函式和賦值函式,又不允許別人使用編譯器生成的預設函式,怎么辦?

偷懶的辦法是:只需將拷貝構造函式和賦值函式聲明為私有函式,不用編寫代碼。

例如:

class A

{ …

private:

A(const A &a); // 私有的拷貝構造函式

A & operate =(const A &a); // 私有的賦值函式

};

如果有人試圖編寫如下程式:

A b(a); // 調用了私有的拷貝構造函式

b = a; // 調用了私有的賦值函式

編譯器將指出錯誤,因為外界不可以操作A的私有函式。

注意:以上例子在vc中可能編譯不過,因關鍵字不是operate ,而是operator

3.在編寫派生類的賦值函式時,注意不要忘記對基類的數據成員重新賦值.

相關詞條

熱門詞條

聯絡我們