曾經有C++高手說過:看一個C++程序員功底是否夠硬,讓他寫個賦值重載函數就能看出來了!在我看來,這種說法並不誇張。因爲能將operator=函數寫好確實需要紮實的基礎,其中的陷阱真不少。
- 陷阱一:不懂規避自我拷貝
先看代碼
string& string::operator=(const string& rhs)
{
if (m_pStr != NULL)
delete [] m_pStr;
m_pStr = new char[....];
strcpy(....);
return *this;
}
此代碼就是沒有規避自我賦值,那麼如果進行以下的調用,那麼後果很嚴重。
string myStr("abc");
myStr = myStr;
賦值操作中,會先把自己的數據delete掉,然後再strcpy一個空值,程序立馬掛了。
所以,在賦值函數開始的時候,需要防止自我複製。寫法如下:
string& string::operator=(const string& rhs)
{
// 防止自我賦值
if (this == &rhs)
return * this;
...
}
但有些書籍上面使用以下寫法,不敢苟同。
string& string::operator=(const string& rhs)
{
// 防止自我賦值
if (*this == rhs) // 這只是判斷內容是否相同,並不能判定是自我賦值!!!
return * this;
...
}
- 陷阱二:返回值的類型用啥
在初學者的羣體當中,有出現諸如以下幾種返回值版本:
// 版本一
string string::operator=(const string& rhs)
{
...
return * this;
}
或// 版本二
const string& string::operator=(const string& rhs)
{
...
return * this;
}
或// 版本三
string* string::operator=(const string& rhs)
{
...
return this;
}
版本一的缺陷:多了一個臨時對象的生成和拷貝,影響程序效率。
版本二的缺陷:非const類型的變量想得到它的連續賦值變得不可能,而且編譯器也會報錯。
版本三的缺陷:不能保持連續賦值時類型的統一性,違反了編程的慣例。如
// 版本三的缺陷
string a, b, c;
*a = b = c; // 必須這樣賦值給a,類型不統一!!
- 陷阱三:未做深拷貝
任何未做深拷貝的版本都退化爲默認版本。
string& string::operator=(const string& rhs)
{
if (this == &rhs) return *this;
if (m_pStr != NULL)
delete [] m_pStr;
m_pStr = rhs.m_pStr; // 淺拷貝
return *this;
}