C++ primer 第五版個人筆記 第十四章 重載運算與類型轉換

14.1 基本概念

  1. 除了重載的函數調用運算符operator()外,其他重載運算符不能含有默認實參;
  2. 當一個重載的運算符是成員函數時,this綁定到左側運算對象,成員運算符函數的(顯式)參數數量比運算對象少一個;
  3. 對於一個運算符函數來說,它或者是類的成員或者至少含有一個類類型的參數;不能爲內置類型的運算對象改變運算符的含義;
  4. 有四個符號(+,-,*,&)既是一元運算符也是二元運算符,都能重載,從參數的數量可以推斷到底定義的是那種運算符;
  5. 重載的運算符的優先級和結合律與對應的內置運算符保持一致;
  6. 不能被重載的運算符(::) , (.*), (.), (? :)
  7. 可以使用運算符的直接和間接調用
    data1 + data2 ;
    operator+(data1,data2);  //兩種調用方式等價

     

  8. 調用重載的成員運算符函數

    data1 += data2;
    data1.operator+(data2);   //兩種調用方式等價

     

  9. 有些函數在C++裏有一些特定規定,如|| 和&&的短路屬性,因此不建議重載逗號(,)取地址(&)邏輯與(&&)邏輯或(&&);

  10. 確定運算符定義爲成員函數還是普通的非成員運算符有一下幾個準則;a,賦值(=),下標([]),調用(()),和成員訪問箭頭(->)運算符必須是成員; b, 符合賦值運算符一般來說應該is成員,但並非必須; c, 改變對象狀態的運算符或者與給定類型密切相關的運算符,如遞增、遞減和解引用運算符,通常應該是成員; d, 具有對稱性的運算符可能轉換任一一端的運算對象,如算術、相等性、關係和位運算符等,通常應該是普通的非成員函數; 

 14.2 輸入和輸出運算符

  1. 輸出運算符的第一個形參是一個非常量ostream對象的引用,非常量是因爲向流寫入內容會改變其狀態,引用是因爲不能直接複製一個ostream對象;
  2. 通常輸出運算符應該主要負責打印對象的內容而非控制格式,輸出運算符不應該打印換行符;
  3. 爲類自定義IO運算符的時候,必須將其定義爲非成員函數,並且一般要聲明爲友元;
  4. 輸入運算符必須考慮輸入可能失敗的情況,而輸出運算符不需要,詳見495頁代碼,當讀取操作發生錯誤時,輸入運算應該負責從錯誤中恢復,書例中的辦法是賦予一個默認構造的空sales_data對象;

 

14.3 算術和關係運算符

  1. 如果存在唯一一種邏輯可靠的<定義,則應該考慮爲這個類定義<運算符;如果類同時還包含==,則當且僅當<的定義和==的結果一致時才定義<運算符;簡而言之就是如果存在歧義的<運算符,最好不要定義;

 

14.4 賦值運算符

  1. vector的值初始化實際是重載了 賦值運算符=,參數是一個初始化列表(initializer_list<string> il);
  2. 賦值運算符必須定義爲類的成員(因其要返回左側對象的引用),複合賦值運算符通常情況下也應該這麼做;

 

14.5 下標運算符

  1. 如果一個類包含下標運算符,則它通常會定義兩個版本,一個返回普通引用,另一個是類的常量成員並且返回常量引用;

 

14.6 遞增和遞減運算符

  1. 前置和後置運算符唯一的區別是後置版本接受一個額外的(不被使用)int類型的星璀璨,後置運算符返回的是對象的原值,前置返回遞增或遞減後的對象的引用;
  2. 先定義前置版本運算符,後置版本將調用前置版本來完成實際的工作;
  3. 顯示調用遞增或遞減運算符是需要傳值來區分是否是前置還是後置
    p.operator++(); //調用前置++
    p.operator++(0); //調用後置++

     

14.7 成員訪問運算符

  1. 重載的箭頭運算符必須返回類的指針或者自定義了箭頭運算符的某個類的對象,重載箭頭運算符的工作原理見505頁;

 

14.8 函數調用運算符

  1. 函數調用運算符(()一對括號) 必須是成員函數,一個類可以定義多個不同版本的調用運算符,相互之間應該在參數數量或類型上有所區別;

  2. 如果類定義了調用運算符,則該類的對象稱作函數對象,因爲可以調用這種對象,所以說這些對象的“行爲像函數一樣”

  3. 函數對象常常作爲泛型算法的實參;例如可以使用標準庫for_each算法和自定義的類來組合完成一些功能;

  4. lambda其實可以看作是一個未命名類的未命名對象

  5. 標準庫定義的函數對象列表見510頁,可以將函數對象應用到泛型算法中,產生更多的用途

  6. 關聯容器使用less<T>對元素進行排序,因此不需要直接聲明less;

  7. 標準庫function類型的用法,詳見512頁,從此編寫計算器程序不用if else了;

 

14.9 重載、類型轉換與運算符

  1. 類型轉換運算符是類的一種特殊的成員函數,將類類型的值轉換成奇台類型,一般形式爲  operator type() const; 它不能聲明返回類型,形參列表也必須爲空,通常是const(因爲類型轉換運算符通常不應該改變待轉換對象的內容);
  2. 避免過度使用類型轉換函數,類型轉換函數有可能不能達到設計者的意圖,通過定義一個或多個普通成員函數來滿足設計的需求或獲取有用的信息;
  3. 可以通過顯示的類型轉換運算符來防止一些異常情況的發生,在 operator前面加上 explicit; 加了explicit之後類型轉換就成了顯式的,不能通過隱式轉換其類型,只能通過顯示轉換(static_cast<type>等),有一種情況例外是當表達式用作條件時,編譯器會將顯式的類型轉換自動應用,即顯式的類型轉換將被隱式地執行
    while(std::cin>>value) 
    //istream類其實是顯式的定義了轉換爲bool類型,但是在條件語句中
    //編譯器隱式地執行顯示轉換,如下幾種情況會進行類似轉換
    // if、while 及do 語句的條件部分
    // for語句頭的條件表達式(第一個;到第二個;之間)
    // 邏輯非運算符(!)、邏輯或運算符(||)、邏輯與運算符(&&)的運算對象
    // 條件運算符(? :)的條件表達式(?左邊)

     

  4. 向bool的類型轉換通常用在條件部分,因此operator bool 一般定義成explicit的;
  5. 爲了避免二義性,不要爲類定義相同的類型轉換,也不要在類中定義兩個及兩個以上轉換源或轉換目標是算術類型的轉換,幾個常見用例見518頁;
  6. 當我們使用兩個用戶定義的類型轉換時,如果轉換函數之前或之後存在標準類型轉換,則標準類型轉換將決定最佳匹配是哪個(如實參是short,相比於double會優先匹配int);
  7. 進行運算符重載之後,當通過類類型的對象(或該類對象的指針或引用)進行函數調用時,只考慮該類的成員函數;當在表達式中使用重載的運算符時,無法判斷正在使用的是成員函數還是非成員函數;
  8. 如果我們對同一個類既提供了轉換目標是算術類型的類型轉換,也提供了重載的運算符,則將會遇到重載運算符與內置運算符的二義性問題;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章