C++運(yùn)算符重載-上篇

C++運(yùn)算符重載-上篇

本章內(nèi)容:
1. 運(yùn)算符重載的概述
2. 重載算術(shù)運(yùn)算符
3. 重載按位運(yùn)算符和二元邏輯運(yùn)算符
4. 重載插入運(yùn)算符和提取運(yùn)算符
5. 重載下標(biāo)運(yùn)算符
6. 重載函數(shù)調(diào)用運(yùn)算符
7. 重載解除引用運(yùn)算符
8. 編寫(xiě)轉(zhuǎn)換運(yùn)算符
9. 重載內(nèi)存分配和釋放運(yùn)算符

1. 運(yùn)算符重載的概述

  • C++中的運(yùn)算符是一些類似于+、<、*和<<的符號(hào)。這些符號(hào)可以應(yīng)用于內(nèi)建類型,例如int和double,從而實(shí)現(xiàn)算術(shù)操作、邏輯操作和其他操作。還有->和*運(yùn)算符可以對(duì)指針進(jìn)行解除引用操作。C++中運(yùn)算符包括[] (數(shù)組索引)、()函數(shù)調(diào)用、類型轉(zhuǎn)換以及內(nèi)存分配和釋放例程??赏ㄟ^(guò)運(yùn)算符重載來(lái)改變語(yǔ)言運(yùn)算符對(duì)自定義類的行為。

1.1 重載運(yùn)算符原因

  • 運(yùn)算符重載的基本指導(dǎo)原則是為了讓自定義類的行為和內(nèi)建類型一樣。自定義類的行為越接近內(nèi)建類型,就越便于這些類的客戶使用。例如,如果要編寫(xiě)一個(gè)表示分?jǐn)?shù)的類,最好定義+、-、*和/運(yùn)算符應(yīng)用于這個(gè)類的對(duì)象時(shí)的意義。
  • 重載運(yùn)算符的第二個(gè)原因是為了獲得對(duì)程序行為更大的控制權(quán)。例如,可對(duì)自定義類重載內(nèi)存分配和內(nèi)存釋放例程,來(lái)精確控制每個(gè)對(duì)象的內(nèi)存分配和內(nèi)存回收。
  • 需要強(qiáng)調(diào)的是,運(yùn)算符重載未必能給類開(kāi)發(fā)者帶來(lái)方便;主要用途是給類的客戶帶來(lái)方便。

1.2 運(yùn)算符重載的限制

下面列出重載運(yùn)算符時(shí)不能做的事情:

  • 不能添加新的運(yùn)算符符號(hào)。只能重定義語(yǔ)言中已經(jīng)存在的運(yùn)算符的意義。
  • 有少數(shù)運(yùn)算符不能重載,例如.(對(duì)象成員訪問(wèn))、::(作用域解析運(yùn)算符)、sizeof、?:(三元運(yùn)算符)以及其他幾個(gè)運(yùn)算符。不能重載的運(yùn)算符通常是不需要重載的,因此這些限制應(yīng)該不會(huì)令人感到受限。
  • arity描述了運(yùn)算符關(guān)聯(lián)的參數(shù)或操作數(shù)的數(shù)量。只能修改函數(shù)調(diào)用、new和delete運(yùn)算符的arity。其他運(yùn)算符的arity不能修改。一元運(yùn)算符,例如++,只能用于一個(gè)操作數(shù)。二元運(yùn)算符,例如+,只能用于2個(gè)操作數(shù)。
  • 不能修改運(yùn)算符的優(yōu)先級(jí)和結(jié)合性。這些規(guī)則確定了運(yùn)算符在語(yǔ)句中的求值順序。同樣,這條約束對(duì)于大多數(shù)程序來(lái)說(shuō)不是問(wèn)題,因?yàn)楦淖兞酥淀樞虿⒉粫?huì)帶來(lái)什么好處。
  • 不能對(duì)內(nèi)建類型重定義運(yùn)算符。運(yùn)算符必須是類中的一個(gè)方法,或者全局重載運(yùn)算符函數(shù)至少有一個(gè)參數(shù)必須是一個(gè)用戶定義的類型(例如一個(gè)類)。這意味著不允許做一些荒唐的事情,例如將int的+重定義為減法(盡管自定義類可以這么做)。這條規(guī)則有一個(gè)例外,那就是內(nèi)存分配和釋放例程;可以替換程序中所有的內(nèi)存分配使用的全局例程。

有一些運(yùn)算符已經(jīng)有兩種不同的含義。例如,-運(yùn)算符可以作為二元運(yùn)算符,如x = y-z;,還可以作為一元運(yùn)算符,如x = -y;。*運(yùn)算符可以作乘法操作,也可以用于解除指針的引用。根據(jù)上下文的不同,<<可以是插入運(yùn)算符,也可以是左移運(yùn)算符。可以重載具有雙意義的運(yùn)算符的兩個(gè)意義。

1.3 運(yùn)算符重載的選擇

  • 重載運(yùn)算符時(shí),需要編寫(xiě)名為operator X的函數(shù)或者方法,X是表示這個(gè)運(yùn)算符的符號(hào),可以在operator和X之間添加空白字符。例如,operator+,如下:

       friend const SpreadsheetCell Operator+(const SpreadsheetCell &lhs, const SpreadsheetCell &rhs);
    

下面幾節(jié)描述了編寫(xiě)每個(gè)重載運(yùn)算符函數(shù)或者方法時(shí)需要做出的選擇。

(1) 方法還是全局函數(shù)
  • 要決定運(yùn)算符應(yīng)該實(shí)現(xiàn)為類的方法還是全局函數(shù)(通常是類的友元)。首先,需要理解這兩個(gè)選擇之間的區(qū)別。當(dāng)運(yùn)算符是類的方法時(shí),運(yùn)算符表達(dá)式的左側(cè)必須是這個(gè)類的對(duì)象。當(dāng)編寫(xiě)全局函數(shù)時(shí),運(yùn)算符的左側(cè)可以是不同類型的對(duì)象。
  • 有三種不同類型的運(yùn)算符:
  • (i) 必須為方法的運(yùn)算符:C++語(yǔ)言要求一些運(yùn)算符必須是類中的方法,因?yàn)檫@些運(yùn)算符在類外部沒(méi)有意義。例如,operator=和類綁定的非常緊密,不能出現(xiàn)在其它地方。
  • (ii) 必須為全局函數(shù)的運(yùn)算符:如果允許運(yùn)算符左側(cè)的變量除了自己定義之外的任何類型,那么必須將這個(gè)運(yùn)算符定義為全局函數(shù)。確切的說(shuō),這條規(guī)則應(yīng)用于operator<<和operator>>,這兩個(gè)運(yùn)算符的左側(cè)是iostream對(duì)象,而不是自定義類的對(duì)象。此外,可交換的運(yùn)算符(例如二元的+和-)允許運(yùn)算符左側(cè)的變量不是自定義類的對(duì)象。
  • (iii) 既可以為方法又可以為全局函數(shù)的運(yùn)算符:有關(guān)編寫(xiě)方法重載運(yùn)算符更好還是編寫(xiě)全局函數(shù)重載運(yùn)算符更好的問(wèn)題存在一些爭(zhēng)議。不過(guò)建議的規(guī)則如下:把所有運(yùn)算符都定義為方法,除非根據(jù)以上的描述必須定義為全局函數(shù)。這條規(guī)則的一個(gè)主要優(yōu)點(diǎn)是方法可以是virtual的,但是friend函數(shù)不能。因此,如果準(zhǔn)備在繼承樹(shù)種編寫(xiě)重載的運(yùn)算符,那么應(yīng)該盡可能將這些運(yùn)算符定義為方法。

將重載的運(yùn)算符定義為方法時(shí),如果這個(gè)運(yùn)算符不能修改對(duì)象,應(yīng)該將整個(gè)方法標(biāo)記為const。這樣,就可以對(duì)const對(duì)象調(diào)用這個(gè)方法。

(2) 選擇參數(shù)類型
  • 參數(shù)類型的選擇有一些限制,因?yàn)槿缜懊婷枋觯蠖鄶?shù)運(yùn)算符不能修改參數(shù)的數(shù)量。例如,operator/在作為全局函數(shù)的情況下必須總是接受兩個(gè)參數(shù);在作為類方法的情況下必須總接受一個(gè)參數(shù)。如果不符合這個(gè)規(guī)則,編譯器會(huì)產(chǎn)生錯(cuò)誤。從這個(gè)角度看,運(yùn)算符函數(shù)和普通函數(shù)有區(qū)別,普通函數(shù)可以使用任意數(shù)量的參數(shù)重載。此外,盡管可以編寫(xiě)接受任何類型參數(shù)的運(yùn)算符,但是可選范圍通常受到了這個(gè)運(yùn)算符所在的類的限制。例如,如果要為類T實(shí)現(xiàn)一個(gè)加法操作,就不能編寫(xiě)接受兩個(gè)string的operator+。真正需要選擇的地方在于判斷是按值還是引用接受參數(shù),以及是否需要把參數(shù)標(biāo)記為const。
  • 按值傳遞還是按引用傳遞的決策如下:應(yīng)該按引用接受每一個(gè)非基本類型的參數(shù)。如果能按引用傳遞,就永遠(yuǎn)不要用按值傳遞對(duì)象。
  • const的決策如下:除非要真正修改參數(shù),否則每一個(gè)參數(shù)都設(shè)置為const。
(3) 選擇返回類型
  • C++不是根據(jù)返回類型來(lái)解析重載。因此,在編寫(xiě)重載運(yùn)算符時(shí),可以指定任意返回類型。然而,可以做某件事情并不意味著應(yīng)該做這件事情。這種靈活性可能會(huì)導(dǎo)致令人迷惑的代碼,例如比較運(yùn)算符返回指針,算術(shù)運(yùn)算符返回bool類型。不應(yīng)該編寫(xiě)這樣的代碼。其實(shí)在編寫(xiě)重載運(yùn)算符時(shí),應(yīng)該讓運(yùn)算符返回的類型和運(yùn)算符對(duì)內(nèi)建類型操作時(shí)返回的類型一樣。如果編寫(xiě)比較運(yùn)算符,那么應(yīng)該返回bool。如果編寫(xiě)的是算術(shù)運(yùn)算符,那么應(yīng)該返回表示運(yùn)算結(jié)果的對(duì)象。
  • 引用和const標(biāo)記的決策也適用于返回類型。不過(guò)對(duì)于返回值來(lái)說(shuō),這些決策要更困難一些。值還是引用的一般原則:如果可以,就返回一個(gè)引用,否則返回一個(gè)值。如何判斷何時(shí)能返回引用?這個(gè)決策只能應(yīng)用于返回對(duì)象的運(yùn)算符:對(duì)于返回bool值的比較運(yùn)算符、沒(méi)有返回類型的轉(zhuǎn)換運(yùn)算符和函數(shù)調(diào)用運(yùn)算符(可能返回所需的任何類型)來(lái)說(shuō),這個(gè)決策沒(méi)有意義。如果運(yùn)算符構(gòu)造了一個(gè)新的對(duì)象,那么必須按值返回新的對(duì)象。如果不構(gòu)造新對(duì)象,那么可以返回調(diào)用這個(gè)運(yùn)算符的對(duì)象的引用,或者返回其中一個(gè)參數(shù)的引用。
  • 可以作為左值(賦值表達(dá)式左側(cè)的部分)修改的返回值必須是非const。否則,這個(gè)值應(yīng)該是const。大部分很容易想到的運(yùn)算符都要求返回左值,包括所有的賦值運(yùn)算符(operator=、operator+=和operator-=等)。
(4) 選擇行為
  • 在重載的運(yùn)算符中,可以提供任意需要的實(shí)現(xiàn)。例如,可以編寫(xiě)一個(gè)啟動(dòng)Scrabble拼字游戲的operator+。通常情況下,應(yīng)該將實(shí)現(xiàn)約束為客戶期待的行為。編寫(xiě)operator+時(shí),使這個(gè)運(yùn)算符能夠執(zhí)行加法,或其他類似加法的操作,例如字符串串聯(lián)。

1.4 不要重載的運(yùn)算符

  • 有一些運(yùn)算符即使允許重載,也不應(yīng)該重載。具體來(lái)說(shuō),取地址運(yùn)算符(operator&)的重載一般沒(méi)有特別的用途,如果重載時(shí)會(huì)導(dǎo)致混亂,因?yàn)檫@樣做會(huì)以可能異常的方式修改基礎(chǔ)語(yǔ)言的行為(獲得變量的地址)。整個(gè)STL大量使用了運(yùn)算符重載,但從沒(méi)有重載取地址運(yùn)算符。
  • 此外,還要避免重載二元布爾運(yùn)算符operator&&和operator||,因?yàn)檫@樣會(huì)使C++的短路求值規(guī)范失效。
  • 最后,不要重載逗號(hào)運(yùn)算符(operator,)。C++中確實(shí)有一個(gè)逗號(hào)運(yùn)算符,它也稱之為序列運(yùn)算符,用于分隔一條語(yǔ)句中的兩個(gè)表達(dá)式,確保從左至右的求值順序。幾乎沒(méi)有什么正當(dāng)?shù)睦碛尚枰剌d這個(gè)運(yùn)算符。

1.5 可重載運(yùn)算符小結(jié)

  • 下表1-1中總結(jié)了什么時(shí)候應(yīng)該(或不應(yīng)該)重載,并提供了示例原型,展示了正確的返回值。
  • 下表1-1中,T表示要編寫(xiě)的重載運(yùn)算符的類名,E是一個(gè)不同的類型(不是這個(gè)類的名稱)。
運(yùn)算符 名稱或類別 方法還是全局friend函數(shù) 何時(shí)重載 示例原型
operator+ operator- operator* operator/ operator% 二元算術(shù)運(yùn)算符 建議使用全局friend函數(shù) 類需要提供這些操作時(shí) friend const T operator+(const T&, const T&); friend T operator+(const T&, const E&);
operator- operator+ operator~ 一元算術(shù)運(yùn)算符和按位運(yùn)算符 建議使用方法 類需要提供這些操作時(shí) const T operator~() const;
operator++ operator-- 前綴遞增運(yùn)算符和遞減運(yùn)算符 建議使用方法 重載了++和--時(shí) T& operator++();
operator++ operator-- 后綴遞增運(yùn)算符和遞減運(yùn)算符 建議使用方法 重載了++和--時(shí) T operator++(int);
operator= 賦值運(yùn)算符 必須使用方法 在類中動(dòng)態(tài)分配了內(nèi)存或資源,或者成員是引用時(shí) T& operator=(const T&);
operator+= operator-= operator*= operator/= operator%= 算術(shù)運(yùn)算符賦值的簡(jiǎn)寫(xiě) 建議使用方法 重載了二元算術(shù)運(yùn)算符,且類沒(méi)有設(shè)計(jì)為不可變時(shí) T& operator+=(const T&); T& operator=(const E&);
operator<< operator>> operator& operator▏ operator^ 二元按位運(yùn)算符 建議使用全局friend函數(shù) 需要提供這些操作時(shí) friend const T operator<<(const T&, const T&); friend T operator<<(const T&, const E&);
operator<<= operator>>= operator&= operator ▏= operator^= 按位運(yùn)算符賦值的簡(jiǎn)寫(xiě) 建議使用方法 重載了二元按位運(yùn)算符,類沒(méi)有設(shè)計(jì)為不可變時(shí) T& operator<<=(const T&); T& operator<<=(const E&);
operator< operator> operator<= operator>= operator== operator!= 二元比較運(yùn)算符 建議使用全局friend函數(shù) 需要提供這些操作時(shí) friend bool operator<(const T&, const T&); friend bool operator<(const T&, const E&);
operator++ operator-- I/O流運(yùn)算符(插入操作和提取操作) 建議使用全局friend函數(shù) 需要提供這些操作時(shí) friend ostream& operator<<(ostream&, const T&); friend istream& operator>>(istream&, T&);
operator! 布爾非運(yùn)算符 建議采用成員函數(shù) 很少重載:應(yīng)該改為bool或void*類型轉(zhuǎn)換 bool operator!() const;
operator&& operator ▏▏ 二元布爾運(yùn)算符 建議使用全局friend函數(shù) 很少重載 friend bool operator&&(const T& lhs, const T& rhs);
operator[] 下標(biāo)(數(shù)組索引)運(yùn)算符 必須使用方法 需要支持下標(biāo)訪問(wèn)時(shí) E& operator; const E& operator const;
operator() 函數(shù)調(diào)用運(yùn)算符 必須使用方法 需要讓對(duì)象的行為和函數(shù)指針一致時(shí) 返回類型和參數(shù)可以多種多樣,下文有詳細(xì)講解
operator type() 轉(zhuǎn)換(或強(qiáng)制類型轉(zhuǎn)換,cast)運(yùn)算符(每種類型有不同的運(yùn)算符) 必須使用方法 需要將自己編寫(xiě)的類型轉(zhuǎn)換為其它類型時(shí) operator type() const;
operator new operator new[] 內(nèi)存分配例程 建議使用方法 需要控制類的內(nèi)存分配時(shí)(很少見(jiàn)) void* operator new(size_t size); void* operator new[](size_t size);
operator delete operator delete[] 內(nèi)存釋放例程 建議使用方法 重載了內(nèi)存分配例程時(shí) void* operator delete(void* ptr) noexcept; void* operator delete[](void* ptr) noexcept;
operator* operator-> 解除引用運(yùn)算符 對(duì)于operator*,建議使用方法;對(duì)于operator->,必須使用方法 適用于智能指針 E& operator() const; E operator->() const;
operator& 取地址運(yùn)算符 不可用 永遠(yuǎn)不要 不可用
operator->* 解除引用指針-成員 不可用 永遠(yuǎn)不要 不可用
operator, 逗號(hào)運(yùn)算符 不可用 永遠(yuǎn)不要 不可用
operator& 取地址運(yùn)算符 不可用 永遠(yuǎn)不要 不可用

表1-1

1.6 右值引用

  • 表1-1中列出的普通賦值運(yùn)算符的原型如下所示:

  •   T& operator=(const T&); 
    
  • 移動(dòng)賦值運(yùn)算符的原型幾乎一致,但使用了右值引用。這個(gè)運(yùn)算符會(huì)修改參數(shù),因此不能傳遞const參數(shù),如下所示:

      T& operator=(T&&);
    
  • 表1-1中沒(méi)有包含右值引用語(yǔ)義的示例原型。然而,對(duì)于大部分運(yùn)算符來(lái)說(shuō),編寫(xiě)一個(gè)使用普通左值引用的版本和一個(gè)使用右值引用的版本都是有意義的,但是否真正有意義取決于類的實(shí)現(xiàn)細(xì)節(jié)。比如通過(guò)operator+避免不必要的內(nèi)存分配。例如STL中的std::string類利用右值引用實(shí)現(xiàn)了operator+,如下所示(簡(jiǎn)化版本):

      string operator+(string&& lhs, string&& rhs);
    
  • 這個(gè)運(yùn)算符的實(shí)現(xiàn)會(huì)重用其中一個(gè)參數(shù)的內(nèi)存,因?yàn)檫@些參數(shù)是以右值引用傳遞的,也就是說(shuō)這兩個(gè)參數(shù)表示的都是operator+完成之后銷毀的臨時(shí)對(duì)象。上述operator+的實(shí)現(xiàn)具有以下效果(具體取決于兩個(gè)操作數(shù)的大小和容量):

      return std::move(lhs.append(rhs));
    
  •   return std::move(rhs.insert(0, lhs));
    
  • 事實(shí)上,std::string定義了幾個(gè)重載的具有不同左值引用和右值引用組合的operator+運(yùn)算符。下面列出std::string中所有接受兩個(gè)string參數(shù)的operator+運(yùn)算符(簡(jiǎn)化版本):

      string operator+(const string& lhs, const string& rhs);
      string operator+(string&& lhs, const string& rhs);
      string operator+(const string& lhs, string&& rhs);
      string operator+(string&& lhs, string&& rhs);
    
  • 重用其中一個(gè)右值引用參數(shù)的內(nèi)存的實(shí)現(xiàn)方式和移動(dòng)賦值運(yùn)算符一致。

1.7 關(guān)系運(yùn)算符

  • C++標(biāo)準(zhǔn)庫(kù)中一個(gè)方便的<utility>頭文件,它包含幾個(gè)輔助函數(shù)和類,還在std::rel_ops命名空間中給關(guān)系運(yùn)算符包含如下函數(shù)模板:

      template<class T> bool operator!=(const T& a, const T& b);   //需要operator==
      template<class T> bool operator>(const T& a, const T& b);    //需要operator<
      template<class T> bool operator<=(const T& a, const T& b);   //需要operator<
      template<class T> bool operator>=(const T& a, const T& b);   //需要operator<
    
  • 這些函數(shù)模板根據(jù)==和<運(yùn)算符給任意類定義了運(yùn)算符!=、>、<=和>=。如果在類中實(shí)現(xiàn)operator==和operator<,就會(huì)通過(guò)這些模板自動(dòng)獲得其他關(guān)系運(yùn)算符。只要添加#include <utility>和下面的using聲明,就可以將這些運(yùn)算符用于自己的類:

      using std::rel_ops::operator!=;
      using std::rel_ops::operator>;
      using std::rel_ops::operator>=;
      using std::rel_ops::operator<=;
    

2. 重載算術(shù)運(yùn)算符

  • 本節(jié)主要講如何重載其他算術(shù)運(yùn)算符的相關(guān)方法。

2.1 重載一元負(fù)號(hào)和一元正號(hào)

  • C++有幾個(gè)一元算術(shù)運(yùn)算符,其中包括一元負(fù)號(hào)和一元正號(hào)。下面列出一些使用int的運(yùn)算符例子:

      int i, j = 4;
      i = -j;           //一元負(fù)號(hào)
      j = +i;           //一元正號(hào)
      j = +(-i);        //對(duì)i做一元負(fù)號(hào)產(chǎn)生的結(jié)果再做一元正號(hào)運(yùn)算
      j = -(-i);        //對(duì)i做一元負(fù)號(hào)產(chǎn)生的結(jié)果再做一元負(fù)號(hào)運(yùn)算
    
  • 一元負(fù)號(hào)運(yùn)算符對(duì)其操作數(shù)取反,而一元正號(hào)運(yùn)算符直接返回操作數(shù)。注意,可以對(duì)一元正號(hào)或一元負(fù)號(hào)產(chǎn)生的結(jié)果應(yīng)用一元正號(hào)或一元負(fù)號(hào)。這些運(yùn)算符不改變調(diào)用它們的對(duì)象,所以應(yīng)該把它們標(biāo)記為const。

  • 下面的例子把一元operator-運(yùn)算符重載為SpreadsheetCell類的成員函數(shù)。一元正號(hào)通常是恒等運(yùn)算,因此這個(gè)類沒(méi)有重載這個(gè)運(yùn)算符:

      SpreadsheetCell SpreadsheetCell::operator-() const
      {
          SpreadsheetCell newCell(*this);
          newCell.set(-mValue);           //調(diào)用set方法去更新mValue和mString
          return newCell;
      }
    
  • operator-沒(méi)有修改操作數(shù),因此這個(gè)方法必須構(gòu)造一個(gè)帶有相反值的新SpreadsheetCell,并返回這個(gè)對(duì)象的副本。因此,這個(gè)運(yùn)算符不能返回引用。

2.2 重載遞增和遞減運(yùn)算符

  • 可以采用4種方法給一個(gè)變量增加1:

      i = i + 1;
      i += 1;
      ++i;
      i++;
    
  • 后兩種稱為遞增運(yùn)算符。第一種形式是前綴遞增,這個(gè)操作將變量的值增加1,然后返回增加后的新值,供表達(dá)式的其他部分使用。第二種形式是后綴遞增,返回舊的(沒(méi)有增加的)值,供表達(dá)式其他部分使用。遞減運(yùn)算符的功能類似。

  • operator++和operator--的雙重意義(前綴和后綴)給重載帶來(lái)了問(wèn)題。例如,編寫(xiě)重載的operator++時(shí),怎么表示重載的是前綴版本還是后綴版本?C++引入了一個(gè)方法來(lái)區(qū)分:前綴的operator++和operator--不接受參數(shù),而后綴的版本需要接受一個(gè)不用的int類型參數(shù)。

  • 如果要為SpreadsheetCell類重載這些運(yùn)算符,原型如下所示:

      SpreadsheetCell& operator++();        //前綴(prefix)
      SpreadsheetCell operator++(int);        //后綴(postfix)
      SpreadsheetCell& operator--();        //前綴(prefix)
      SpreadsheetCell operator--(int);        //后綴(postfix)
    
  • 前綴形式的結(jié)果值和操作數(shù)的最終值一致,因此前綴遞增和前綴遞減返回被調(diào)用對(duì)象的引用。然而后綴版本的遞增操作和遞減操作返回的結(jié)果值和操作數(shù)的最終值不同,因此不能返回引用。

  • 下面是operator++運(yùn)算符的實(shí)現(xiàn):

      SpreadsheetCell& SpreadsheetCell::operator++()
      {
          set(mValue + 1);
          return *this;
      }
      SpreadsheetCell SpreadsheetCell::operator++(int)
      {
          SpreadsheetCell oldCell(*this);  //在遞增之前保存當(dāng)前值的值
          set(mValue + 1);                    //把值加1(遞增)
          return oldCell;                  //返回之前保存過(guò)的原來(lái)的值
      }
    
  • operator--的實(shí)現(xiàn)幾乎和遞增的相同,我們通過(guò)遞增和遞減來(lái)操作SpreadsheetCell對(duì)象:

      SpreadsheetCell c1(4);
      SpreadsheetCell c2(4);
      c1++;
      ++c2;
    
  • 遞增和遞減還能應(yīng)用于指針。當(dāng)編寫(xiě)的類是智能指針或迭代器時(shí),可以重載operator++和operator--,以提供指針的遞增和遞減操作。

3. 重載按位運(yùn)算符和二元邏輯運(yùn)算符

  • 按位運(yùn)算符和算術(shù)運(yùn)算符類似,簡(jiǎn)寫(xiě)的按位運(yùn)算符也和簡(jiǎn)寫(xiě)的算術(shù)運(yùn)算符類似。在表1-1中已經(jīng)展示了示例原型。

  • 邏輯運(yùn)算符要困難一些。建議不重載&&和||。這些運(yùn)算符并不應(yīng)用于單個(gè)類型:而是整合布爾表達(dá)式的結(jié)果。此外,重載這些運(yùn)算符會(huì)失去短路求值,原因是在將運(yùn)算符左側(cè)和右側(cè)的值綁定至重載的&&和||運(yùn)算符之前,必須對(duì)運(yùn)算符的左側(cè)和右側(cè)進(jìn)行求值。因此,一般對(duì)特定的類型重載這些運(yùn)算符都沒(méi)有意義。

  • 下面來(lái)講解下短路求值的概念:在C++中短路求值有邏輯與(&&)和邏輯或(||)。

    (1). 邏輯與的短路

  • 首先看如下代碼:

      #include <iostream>
      using namespace std;
      int main()
      {
          int a = 1;
          cout << "a = " << a << endl;
          false && (a=3);
          cout << "a = " << a << endl;
      }
    
  • 運(yùn)行結(jié)果如下:

      a = 1
      a = 1
    
  • 邏輯或的表現(xiàn)形式如下:

      expression1 && exexpression2
    
  • 這里用到了邏輯與,由于邏輯與的短路,expression1為false,則后面的expression2(即:(a=3))不再求值,整個(gè)表達(dá)式的結(jié)果為false,所以a的值仍為1,沒(méi)有改變。

    (2). 邏輯或的短路

  • 首先看如下代碼:

      #include <iostream>
      using namespace std;
      int main()
      {
          int a = 1;
          cout << "a = " << a << endl;
          true || (a=0);
          cout << "a = " << a << endl;
      }
    
  • 運(yùn)行結(jié)果如下:

      a = 1
      a = 1
    
  • 邏輯或的表現(xiàn)形式如下:

      expression1 || exexpression2
    
  • 這里用到了邏輯或,由于邏輯或的短路,expression1為true,則后面的expression2(即:(a=0))不再求值,整個(gè)表達(dá)式的結(jié)果為true,所以a的值仍為1,沒(méi)有改變。

    (3). 應(yīng)用舉例

  • 如何不用if語(yǔ)句,不用匯編使得兩個(gè)數(shù)之積總是小于等于255?

  • 比如可以用簡(jiǎn)單的條件表達(dá)式:

      result = ((a*b) > 255) ? 255 : (a*b);
    
  • 也可以用邏輯或的短路來(lái)解:

      bool btmp = ((result = a*b) < 255) || (result = 255);
    
  • 同時(shí)也可以用邏輯與的短路來(lái)解:

      bool btmp = ((result = a*b) >= 255) && (result = 255);
    

4. 重載插入運(yùn)算符和提取運(yùn)算符

  • 在C++中,不僅算術(shù)操作需要使用運(yùn)算符,從流中讀寫(xiě)數(shù)據(jù)都可以使用運(yùn)算符。例如,向cout寫(xiě)入int和string時(shí)使用插入運(yùn)算符<<:

      int number = 10;
      cout << "The number is " << number << endl;
    
  • 從流中讀取數(shù)據(jù)時(shí),使用提取運(yùn)算符>>:

      int number;
      string str;
      cin >> nubmer >> str;
    
  • 還可以為自己定義的類編寫(xiě)合適的插入和提取運(yùn)算符,從而可按以下方式進(jìn)行讀寫(xiě):

      SpreadsheetCell myCell, anotherCell, aThirdCell;
      cin >> myCell >> anotherCell >> aThirdCell;
      cout << myCell << " " << anotherCell << " " << aThirdCell << endl;
    
  • 在編寫(xiě)插入和提取運(yùn)算符之前,需要決定如何將自定義的類向流輸出,以及如何從流中提取自定義的類。在上面的例子中,SpreadsheetCell將讀取和寫(xiě)入字符串。

  • 插入和提取運(yùn)算符左側(cè)的對(duì)象是istreamostream(例如cincout),而不是SpreadsheetCell對(duì)象。由于不能向istreamostream類添加方法,因此應(yīng)該將插入和提取運(yùn)算符寫(xiě)為SpreadsheetCell類的全局friend函數(shù)。這些函數(shù)在SpreadsheetCell類中的聲明如下所示:

      class SpreadsheetCell
      {
      public:
          //省略……
          friend std::ostream& operator<<(std::ostream& ostr, const SpreadsheetCell& cell);
          friend std::istream& operator>>(std::istream& istr, SpreadsheetCell& cell);
          //省略……
      };
    
  • 將插入運(yùn)算符的第一個(gè)參數(shù)設(shè)置為ostream的引用,這個(gè)運(yùn)算符就能夠應(yīng)用于文件輸出流、字符串輸出流、cout、cerr和clog等。與此同時(shí),將提取運(yùn)算符的第一個(gè)參數(shù)設(shè)置為istream的引用,這個(gè)運(yùn)算符就能應(yīng)用于文件輸入流、字符串輸入流和cin。

  • operator<<和operator>>的第二個(gè)參數(shù)是對(duì)要寫(xiě)入或讀取的SpreadsheetCell對(duì)象的引用。插入運(yùn)算符不會(huì)修改寫(xiě)入的SpreadsheetCell,因此這個(gè)引用可以是const。然而提取運(yùn)算符會(huì)修改SpreadsheetCell對(duì)象,因此要求這個(gè)參數(shù)為非const引用。

  • 兩個(gè)運(yùn)算符返回的都是第一個(gè)參數(shù)傳入的流的引用,所以這個(gè)運(yùn)算符可以嵌套。記住,這個(gè)運(yùn)算符的語(yǔ)法實(shí)際上是顯示調(diào)用全局operator>>函數(shù)或operator<<函數(shù)的簡(jiǎn)寫(xiě)形式。例如下面這一行代碼:

      cin >> myCell >> anotherCell >> aThirdCell;
    
  • 實(shí)際上是這一行的簡(jiǎn)寫(xiě)形式:

      operator>>(operator>>(operator>>(cin, myCell), anotherCell), aThirdCell);
    
  • 從中可以看出,第一次調(diào)用operator>>的返回值作下一次調(diào)用的輸入值。因此必須返回流的引用,結(jié)果才能可以用于下一次嵌套的調(diào)用。否則嵌套調(diào)用無(wú)法編譯。

  • 下面是SpreadsheetCell類的operator<<和operator>>的實(shí)現(xiàn):

      ostream& operator<<(ostream& ostr, const SpreadsheetCell& cell)
      {
          ostr << cell.mString;
          return ostr;
      }
      istream& operator>>(istream& istr, SpreadsheetCell& cell)
      {
          string strTemp;
          istr >> strTemp;
          cell.set(strTemp);
          return istr;
      }
    
  • 這些函數(shù)中最棘手的部分是為了正確配置mValue的值,operator>>必須調(diào)用SpreadsheetCell的set()方法,而不是直接設(shè)置mString。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基本上我們進(jìn)行運(yùn)算符重載時(shí)有兩種形式,類內(nèi)的運(yùn)算符重載和頂層函數(shù)位置的運(yùn)算符重載。 操作符重載指的是將C++提供的...
    飛揚(yáng)code閱讀 1,784評(píng)論 0 4
  • C++語(yǔ)言的一個(gè)很有意思的特性就是除了支持函數(shù)重載外還支持運(yùn)算符重載,原因就是在C++看來(lái)運(yùn)算符也算是一種函數(shù)。比...
    歐陽(yáng)大哥2013閱讀 2,789評(píng)論 0 8
  • 3. 類設(shè)計(jì)者工具 3.1 拷貝控制 五種函數(shù)拷貝構(gòu)造函數(shù)拷貝賦值運(yùn)算符移動(dòng)構(gòu)造函數(shù)移動(dòng)賦值運(yùn)算符析構(gòu)函數(shù)拷貝和移...
    王偵閱讀 2,074評(píng)論 0 1
  • C++基礎(chǔ)2:類與對(duì)象 1. 認(rèn)識(shí)類與對(duì)象 什么是類(class)?類(class)是類型(type),是用戶自定...
    jdzhangxin閱讀 2,460評(píng)論 0 7
  • 送走獨(dú)特的2019,迎來(lái)嶄新的2020。此刻,我只想在新的一年里首先做這件事一一重拾“日更”。 堅(jiān)持是一種習(xí)慣,堅(jiān)...
    青蓮紅魚(yú)閱讀 276評(píng)論 0 1

友情鏈接更多精彩內(nèi)容