一、重載賦值運算符“=”
賦值運算符“=”要求左右兩個操作數的類型是匹配的,或至少是兼容的。有時候希望賦值運算符兩邊的類型可以不匹配,比如,把一個int類型變量賦值給一個Complex對象,或把一個 char * 類型的字符串賦值給一個字符串對象,此時就需要重載賦值運算符“=”。C++規定,賦值運算符“=”只能重載為成員函數。
程序示例分析:
#include<iostream> using namespace std; class String { private: char* str; public: String() :str(new char[1]) { str[0] = 0; } const char* c_str() { return str; }; String& operator = (const char* s); String::~String() { delete[] str; } }; String& String::operator = (const char* s) { //重載“=”以使得 obj = “hello”能夠成立 delete[] str; str = new char[strlen(s) + 1]; strcpy(str, s); return *this; } int main() { String s; s = "Good Luck,"; //等價於 s.operator=("Good Luck,"); cout << s.c_str() << endl; // String s2 = "hello!"; //這條語句要是不注釋掉就會出錯 s = "Shenzhou 8!"; //等價於 s.operator=("Shenzhou 8!"); cout << s.c_str() << endl; return 0; }
輸出結果:
Good Luck, Shenzhou 8!
二、淺拷貝和深拷貝
同類對象之間可以通過賦值運算符“=”互相賦值。如果沒有經過重載,“=”的作用就是把左邊的對象的每個成員都變得和右邊的對象相等,即執行逐個字節拷貝的工作,這種拷貝叫作“淺拷貝” 。
經過重載,賦值號“=”的功能不再是淺拷貝,而是將一個對象中指針成員變量指向的內容復制到另一個對象中指針成員變量指向的地方,這種拷貝叫作“深拷貝” 。
class String { private: char* str; public: String() :str(new char[1]) { str[0] = 0; } const char* c_str() { return str; }; String& operator = (const char* s) { delete[] str; str = new char[strlen(s) + 1]; strcpy(str, s); return *this; }; ~String() { delete[] str; } };
按照這個String類的寫法,下面的程序片段會引發問題
String S1, S2; S1 = “this”; S2 = “that”; S1 = S2;
如不定義自己的賦值運算符,那么S1=S2實際上導致 S1.str和 S2.str指向同一地方。
如果S1對象消亡,析構函數將釋放 S1.str指向的空間,則S2消亡時還要釋放一次,不妥。
另外,如果執行 S1 = "other";會導致S2.str指向的地方被delete
因此要在 class String里添加成員函數:
String & operator = (const String & s) { delete [] str; str = new char[strlen( s.str)+1]; strcpy( str,s.str); return * this; }
Question1:
考慮下面語句是否會有問題?
String s; s = "Hello"; s = s;
解決辦法:
解決辦法: String & operator = (const String & s){ if( this == & s) return * this; delete [] str; str = new char[strlen(s.str)+1]; strcpy( str,s.str); return * this; }
Question2:
對 operator = 返回值類型的討論
void 好不好?
String 好不好?
為什么是 String &
對運算符進行重載的時候,好的風格是應該盡量保留運算符原本的特性
考慮
a = b = c;
和
(a=b)=c; //會修改a的值
分別等價於:
a.operator=(b.operator=(c)); (a.operator=(b)).operator=(c);
Question3:
上面的String類是否就沒有問題了?
為 String類編寫復制構造函數的時候,會面臨和 = 同樣的問題,用同樣的方法處理。
String( String & s) { str = new char[strlen(s.str)+1]; strcpy(str,s.str); }
Question4:
以下說法正確的是:
A) 成員對象都是用無參構造函數初始化的
B) 封閉類中成員對象的構造函數先於封閉類的構造函數被調用
C) 封閉類中成員對象的析構函數先於封閉類的析構函數被調用
D) 若封閉類有多個成員對象,則它們的初始化順序取決於封閉類構造函數中的成員初始化列表