標籤:style blog color 資料 ar 問題 div 工作
看下面一個表示string對象的類:
// 一個很簡單的string類class string {public: string(const char *value); ~string(); ... // 沒有拷貝建構函式和operator=private: char *data;};string::string(const char *value){ if (value) { data = new char[strlen(value) + 1]; strcpy(data, value); } else { data = new char[1]; *data = ‘\0‘; }}inline string::~string() { delete [] data; }
請注意這個類裡沒有聲明賦值操作符和拷貝建構函式。這會帶來一些不良後果。
如果這樣定義兩個對象:
string a("hello"); string b("world");
其結果就會如下所示:
a: data——> "hello\0"
b: data——> "world\0"
對象a的內部是一個指向包含字串"hello"的記憶體的指標,對象b的內部是一個指向包含字串"world"的記憶體的指標。如果進行下面的賦值:
b = a;
因為沒有自訂的operator=可以調用,c++會產生並調用一個預設的operator=操作符(見條款45)。這個預設的賦值操作符會執行從a的成員到b的成員的逐個成員的賦值操作
這種情況下至少有兩個問題。第一,b曾指向的記憶體永遠不會被刪除,因而會永遠丟失。這是產生記憶體流失的典型例子。第二,現在a和b包含的指標指向同一個字串,那麼只要其中一個離開了它的生存空間,其解構函式就會刪除掉另一個指標還指向的那塊記憶體。
拷貝建構函式的情況和賦值操作符還有點不同。在傳值調用的時候,它會產生問題。當然正如條款22所說明的,一般很少對對象進行傳值調用,但還是看看下面的例子:
void donothing(string localstring) {}string s = "the truth is out there";donothing(s);
因為被傳遞的localstring是一個值,它必須從s通過(預設)拷貝建構函式進行初始化。於是localstring擁有了一個s內的指標的拷貝。當donothing結束運行時,localstring離開了其生存空間,調用解構函式。其結果也將是:s包含一個指向localstring早已刪除的記憶體的指標。
用delete去刪除一個已經被刪除的指標,其結果是不可預測的。
解決這類指標混亂問題的方案在於,只要類裡有指標時,就要寫自己版本的拷貝建構函式和賦值操作符函數。在這些函數裡,你可以拷貝那些被指向的資料結構,從而使每個對象都有自己的拷貝(深拷貝);或者你可以採用某種引用計數機制(見條款 m29)去跟蹤當前有多少個對象指向某個資料結構。引用計數的方法更複雜,而且它要求建構函式和解構函式內部做更多的工作,但在某些(雖然不是所有)程式裡,它會大量節省記憶體並切實提高速度。