讓operator 返回 this的引用

2021-05-23 18:48:30 字數 2900 閱讀 7951

c++的設計者bjarne stroustrup下了很大的功夫想使使用者自定義型別盡可能地和固定型別的工作方式相似。這就是為什麼你可以過載運算子,寫型別轉換函式,控制賦值和拷貝建構函式,等等。他做了這麼多努力,那你最少也該繼續做下去。

讓我們看看賦值。用固定型別的情況下,賦值操作可以象下面這樣鏈起來:

int w, x, y, z;

w = x = y = z = 0;

所以,你也應該可以將使用者自定義型別的賦值操作鏈起來:

string w, x, y, z;	// string是由標準c++庫

// 「自定義」的型別

w = x = y = z = "hello";

因為賦值運算子的結合性天生就是由右向左,所以上面的賦值可以解析為:

w = (x = (y = (z = "hello")));
很值得把它寫成乙個完全等價的函式形式。除非是個lisp程式設計師,否則下面的例子會很令人感到高興,因為它定義了乙個中綴運算子:

w.operator=(x.operator=(y.operator=(z.operator=("hello"))));
這個格式在此很具有說明性,因為它強調了w.operator=, x.operator=和y.operator=的引數是前乙個operator=呼叫的返回值。所以operator=的返回值必須可以作為乙個輸入引數被函式自己接受。在乙個類c中,預設版本的operator=函式具有如下形式:

c& c::operator=(const c&);
一般情況下幾乎總要遵循operator=輸入和返回的都是類物件的引用的原則,然而有時候需要過載operator=使它能夠接受不同型別的引數。例如,標準string型別提供了兩個不同版本的賦值運算子:

string&	// 將乙個string

operator=(const string& rhs); // 賦給乙個string

string& // 將乙個char*

operator=(const char *rhs); // 賦給乙個string

請注意,即使在過載時,返回型別也是類的物件的引用。

c++程式設計師經常犯的乙個錯誤是讓operator=返回void,這好象沒什麼不合理的,但它妨礙了連續(鏈式)賦值操作,所以不要這樣做。

另乙個常犯的錯誤是讓operator=返回乙個const物件的引用,象下面這樣:

class widget ;
這樣做通常是為了防止程式中做象下面這樣愚蠢的操作:

widget w1, w2, w3;

...(w1 = w2) = w3; // w2賦給w1, 然後w3賦給其結果

//(給operator=乙個const返回值

// 就使這個語句不能通過編譯)

這可能是很愚蠢,但固定型別這麼做並不愚蠢:

int i1, i2, i3;

...(i1 = i2) = i3; // 合法! i2賦給i1

// 然後i3賦給i1!

這樣的做法實際中很少看到,但它對int來說是可以的,對我和我的類來說也可以。那它對你和你的類也應該可以。為什麼要無緣無故地和固定型別的常規做法不相容呢?

採用預設形式定義的賦值運算子裡,物件返回值有兩個很明顯的候選者:賦值語句左邊的物件(被this指標指向的物件)和賦值語句右邊的物件(參數列中被命名的物件)。哪乙個是正確的呢?

例如,對string類(假設你想在這個類中寫賦值運算子)來說有兩種可能:

string& string::operator=(const string& rhs)

string& string::operator=(const string& rhs)

對你來說,這好象是拿六個一和十二的一半來比較一樣為難。實際上他們有很大的不同。

首先,返回rhs的那個版本不會通過編譯,因為rhs是乙個const string的引用,而operator=要返回的是乙個string的引用。當要返回乙個非const的引用而物件自身是const時,編譯器會給你帶來無盡的痛苦。看起來這個問題很容易解決——只用象這樣重新宣告operator=:

string& string::operator=(string& rhs)
這次又輪到用到它的應用程式不能通過編譯了!再看看最初那個連續賦值語句的後面部分:

x = "hello";	// 和x.op = ("hello"); 相同
因為賦值語句的右邊引數不是正確的型別——它是乙個字元陣列,不是乙個string——編譯器就要產生乙個臨時的string物件(通過stirng建構函式——參見條款m19)使得函式繼續執行。就是說,編譯器必須產生大致象下面這樣的**:

const string temp("hello");	// 產生臨時string

x = temp; // 臨時string傳給operator=

編譯器一般會產生這樣的臨時值(除非顯式地定義了所需要的建構函式——見條款19),但注意臨時值是乙個const。這很重要,因為它可以防止傳遞到函式內的臨時值被修改。否則,程式設計師就會很奇怪地發現,只有編譯器產生的臨時值可以修改而他們在函式呼叫時實際傳進去的引數卻不行。(關於這一點是有事實根據的,早期版本的c++允許這類的臨時值可以被產生,傳遞,修改,結果很多程式設計師感到很奇怪)

現在我們就可以知道如果string的operator=宣告傳遞乙個非const的stirng引數,應用程式就不能通過編譯的原因了:對於沒有宣告相應引數為const的函式來說,傳遞乙個const物件是非法的。這是乙個關於const的很簡單的規定。

所以,結論是,這種情況下你將別無選擇:當定義自己的賦值運算子時,必須返回賦值運算子左邊引數的引用,*this。如果不這樣做,就會導致不能連續賦值,或導致呼叫時的隱式型別轉換不能進行,或兩種情況同時發生。

宣告:本文為effective c++ 中原文,個人感覺很好,所以粘了來。

讓Solr返回JSON資料

http localhost 1985 solr select q 3a version 2.2 start 0 rows 10 indent on wt json solr的http請求後加乙個wt引數 返回則是text plain的json字串。如下圖所示 預設的是返回xml資料,將以上請求引數...

讓函式返回多個值

讓函式返回多個值 include using namespace std int func int a,int b,int c int main int func int a,int b,int c 輸出 a 1b 2c 3a 1b 4c 27 我們知道函式只能返回乙個值,那麼假如有的時候我們需要函...

讓Solr返回JSON資料

http localhost 1985 solr select q 3a version 2.2 start 0 rows 10 indent on wt json solr的http請求後加乙個wt引數 返回則是text plain的json字串。如下圖所示 預設的是返回xml資料,將以上請求引數...