c 中有些過載運算子為什麼要返回引用

2021-10-07 22:51:55 字數 4463 閱讀 8405

**

事實上,我們的過載運算子返回void、返回物件本身、返回物件引用都是可以的,並不是說一定要返回乙個引用,只不過在不同的情況下需要不同的返回值。

那麼什麼情況下要返回物件的引用呢?

原因有兩個:

1.允許進行連續賦值

2.防止返回物件(返回物件也可以進行連續賦值(常規的情況,如a = b = c,而不是(a = b) = c))的時候呼叫拷貝建構函式和析構函式導致不必要的開銷,降低賦值運算子的效率。

對於第二點原因:如果用」值傳遞「的方式,雖然功能仍然正確,但由於return語句要把*this拷貝到儲存返回值的外部儲存單元之中,增加了不必要的開銷,會降低賦值函式的效率。

場景:需要返回物件引用或者返回物件(效率沒有返回引用高),需要實現連續賦值,使過載的運算子更符合c++本身的運算子語意,如連續賦值 = += -= *= 、=,《輸出流

關於賦值 =,我們知道賦值=有連續等於的特性

1 int x,y,z;

2 x=y=z=15;

同樣有趣的是,賦值採用的是右結合律,所以上述連鎖賦值被解析為:

1 x=(y=(z=15));//賦值連鎖形式

這裡15先被賦值給z,然後其結果(更新後的z)再被賦值給y,然後其結果(更新後的y)再被賦值給x。

為了實現」連鎖賦值「,賦值操作符號返回乙個reference(引用)指向操作符號的左側實參(而事實上過載運算子的左側實參就是呼叫物件本身,比如= += -=等),這是你為classes實現賦值操作符時應該遵循的協議:這個協議不僅僅適用於以上的標準賦值形式,也適用於所有賦值運算。

複製**

1 class widght

9 widget& operator+=(cosnt widget& rhs)

10 14

15 widget& operator-=(cosnt widget& rhs)

16 20

21 widget& operator*=(cosnt widget& rhs)

22 26

27 widget& operator/=(cosnt widget& rhs)

28 32 …

33 };

複製**

注意,這只是個協議,並無強制性,如果不遵循它,**一樣可以通過編譯,然而這份協議被所有內建型別和標準程式庫提供的型別入string,vector,complex,std::trl::shared_ptr或者即將提供的型別共同遵守。因此除非你有乙個標新立異的好理由,不然還是隨眾吧。

下面看乙個賦值運算子過載的例子:(連續賦值,常規的情況(a = b = c))

1、首先是返回物件的情況:

複製**

1 #include

2 using namespace std;

3 class string

4 15

16 /copy construct/

17 string(const string& other)

18 24

25 ~string()

26 30 };

3132 string::string(const char* s)//建構函式定義

33 38

39 string string::operator=(const string &other)//運算子過載

40 51

52 int main()

53 複製**

執行結果:

2、下面是返回引用的情況(string& operator = (const string& str)),直接貼執行結果:

當運算子過載返回的是物件時,會在連續賦值運算過程的返回途中,呼叫兩次拷貝建構函式和析構函式(因為return的是個新的物件)

如果採用string& operator = (const string& str)這樣就不會有多餘的呼叫(因為這裡直接return乙個已經存在的物件的引用)

上面的栗子也說明一點:析構函式的呼叫是在變數作用域結束的時候(以及程式執行結束的時候)

如果採用return物件,那麼第二次賦值運算呼叫的情況就是:

將乙個新的string物件(returnstringobj)傳遞到operator = (const string& str)的引數中去 相當於

const string&str = returnstringobj;

如果採用return物件引用,那麼第二次賦值運算的情況就是:

將乙個已經存在的string物件的引用((其實就是str1))傳遞給operator = (const string& str)的引數中去

const string&str = returnreference; //(string& returnreference = str1;)

+=等運算子也是同樣的考慮,比如

複製**

1 int main()

2 複製**

如果使用+=或其它上面舉出的運算子進行連續操作時,,則這些運算子的返回值一定要是乙個物件或者引用才行,不然就會出現錯誤(引數型別不符合)。什麼意思呢,下面舉個栗子。

我現在讓運算子過載返回的型別為空,單個賦值,不使用連續賦值:

複製**

1 #include

2 using namespace std;

3 class string

4 15

16 /copy construct/

17 string(const string& other)

18 24

25 ~string()

26 30 };

3132 string::string(const char* s)

33 38

39 void string::operator=(const string &other)

40 51

52 int main()

53 複製**

執行結果:

但當我把主函式中str1,str2,str3改為連續賦值時:

複製**

1 int main()

2 複製**

出錯:所以,當你確定不使用連續賦值時,直接返回void也是可以的。要明白一點:

運算子左側的物件就是操作物件,比如

1 objecta = objectb 等同於objecta.operator=(objectb)

2 objecta+=objectb 等同於objecta.operator+(objectb)

最後要說明一點:並非必須返回引用,返回引用的好處既可以避免拷貝建構函式和析構函式的呼叫,又可以保證= +=等運算子的原始語義清晰。

啥叫原始語義清晰呢?

如1 (str3 = str1) = str2;

我們的意識裡,就是先執行括號內容,即str1賦值給str3,然後str2再賦值給str3,最後str3輸出的內容是str2的。

即如果運算子過載返回的是物件引用時,

複製**

1 //返回的是物件引用的情況

2 #include

3 using namespace std;

4 class string

5 16

17 /copy construct/

18 string(const string& other)

19 25

26 ~string()

27 31 };

3233 string::string(const char* s)

34 39

40 string& string::operator=(const string &other)

41 52

53 int main()

54 複製**

執行結果:

str3得到了str2的內容,與我們認識的『=』運算子邏輯相符。

而如果運算子過載返回的是物件時,

複製**

1 //這是返回型別為物件的情況

2 #include

3 using namespace std;

4 class string

5 16

17 /copy construct/

18 string(const string& other)

19 25

26 ~string()

27 31 };

3233 string::string(const char* s)

34 39

40 string string::operator=(const string &other)

41 52

53 int main()

54 複製**

執行結果:

str3只得到了str1的內容,並沒有得到str2的內容,這是因為執行(str3=str1)後,因為返回的是物件(乙個臨時物件,str3的乙個拷貝),不是引用,所以此時str3不在後面的『=str2』的操作中,而是str2對乙個臨時物件賦值,所以str3的內容保持不變(等於str1)。

總結所以,對此類運算子過載時,還是老老實實的返回引用,少搞事,做個好男孩:)

C 過載運算子

運算子過載是一種形式的c 多型。在c 中,編譯器有能力把乙個由資料 物件和操作符共同組成的表示式,解釋為對乙個全域性或成員函式的呼叫。該全域性或成員函式被稱為操作符函式,通過重定義操作符函式,可以實現針對自定義型別 結構,類 的運算法則,並使之與內建型別一樣參與各種表示式。過載運算子可使 看起來更加...

C 過載運算子

本文主要講述加號運算子 自增運算子 流提取運算子運 流插入運算子 先給出vector類 class vector 建構函式 vector const vector v 拷貝建構函式 vector operator const vector v 過載 vector operator 過載前置自增運算子...

C 過載運算子

過載的運算子是帶有特殊名稱的函式,函式名是由關鍵字 operator 和其後要過載的運算子符號構成的。與其他函式一樣,過載運算子有乙個返回型別和乙個引數列表。如果我們定義的函式為類的成員函式 box operator const box 如果我們定義的函式為非成員函式,那麼我們需要為每次操作傳遞兩個...