C++進階學習一去不復返系列:重載運算符和重載函數

C++ 允許在同一作用域中的某個函數運算符指定多個聲明定義,分別稱爲函數重載運算符重載

重載聲明是指一個與之前已經在該作用域內聲明過的函數或方法具有相同名稱的聲明,但是它們的參數列表和定義(實現)不相同。重載決策是指調用一個重載函數重載運算符時,編譯器通過把所使用的參數類型與聲明定義中的參數類型進行比較,決定選用最合適的聲明定義的過程。

函數重載

同一作用域內,允許聲明功能類似但是形式參數(指參數的個數、類型或者順序)必須不同的同名函數,重載函數不能只是返回類型不同。比如同名函數 print ( ) 輸出不同類型的數據:

class printData
{
   public:
      void print(int i) {
        cout << "整數爲: " << i << endl;
      }
 
      void print(double  f) {
        cout << "浮點數爲: " << f << endl;
      }
 
      void print(char c[]) {
        cout << "字符串爲: " << c << endl;
      }
};

同名函數 print() 會根據傳入參數類型來判斷採用哪一個重載函數。

運算符重載

C++ 允許重定義或重載大部分內置的運算符。重載的運算符是帶有特殊名稱的函數,且函數名是由關鍵字 operator 其後要重載的運算符符號構成的。

重載運算符有一個返回類型和一個參數列表:

returnType operator+(params lists);

大多數的重載運算符可被定義爲普通的非成員函數或者被定義爲類成員函數。對於運算符重載函數爲類的非成員函數時,就需要將需要操作的對象都作爲形式參數聲明。

可重載運算符/不可重載運算符

可重載運算符

雙目算術運算符 + (加),-(減),*(乘),/(除),% (取模)
關係運算符 ==(等於),!= (不等於),< (小於),> (大於>,<=(小於等於),>=(大於等於)
邏輯運算符 ||(邏輯或),&&(邏輯與),!(邏輯非)
單目運算符 + (正),-(負),*(指針),&(取地址)
自增自減運算符 ++(自增),--(自減)
位運算符 | (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移)
賦值運算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空間申請與釋放 new, delete, new[ ] , delete[]
其他運算符 ()(函數調用),->(成員訪問),,(逗號),[](下標)

 不可重載運算符

成員訪問運算符  .
成員指針訪問運算符  .* ->*
域運算符  :: 
長度運算符  sizeof
條件運算符  ? :
預處理符號  #

運算符重載實例

二元運算符重載

以+運算符爲例(把兩個 Box 對象相加):

// 重載 + 運算符,用於把兩個 Box 對象相加
Box operator+(const Box& b)
{
    Box box;
    box.length = this->length + b.length;
    box.breadth = this->breadth + b.breadth;
    box.height = this->height + b.height;
    return box;
}

一元運算符重載

遞增和遞減運算符重載

  1. 遞增和遞減一般是改變對象的狀態,所以一般是重載爲成員函數。
  2. 重載遞增遞減,一定要和指針的遞增遞減區分開。因爲這裏的重載操作的是對象,而不是指針(由於指針是內置類型,指針的遞增遞減是無法重載的),所以一般情況的遞增遞減是操作對象內部的成員變量。

以遞增運算符++ 爲例:

// 重載前綴遞增運算符( ++ )
Time operator++ ()  
{
    ++minutes;          // 對象加 1
    if(minutes >= 60)  
    {
        ++hours;
        minutes -= 60;
    }
    return Time(hours, minutes);
}
// 重載後綴遞增運算符( ++ )
Time operator++( int )         
{
    // 保存原始值
    Time T(hours, minutes);
    // 對象加 1
    minutes++;                    
    if(minutes >= 60)
    {
        hours++;
        minutes -= 60;
    }
    // 返回舊的原始值
    return T; 
}

前綴運算符先自增,後計算表達式,後綴運算符則相反。前綴形式重載調用 operator ++ () ,後綴形式重載調用 operator ++ (int)。int 在 括號內是爲了向編譯器說明這是一個後綴形式,而不是表示整數。

關係運算符重載

以 < 爲例:

// 重載小於運算符( < )
bool operator <(const Distance& d)
{
     if(feet < d.feet)return true;
     if(feet == d.feet && inches < d.inches)return true;
     return false;
}

輸入/輸出運算符<<和 >>重載

可以重載流提取運算符和流插入運算符來操作對象等用戶自定義的數據類型。在這裏,需要把運算符重載函數聲明爲類的友元函數這樣我們就能不用創建對象而直接調用函數以流插入運算符 << 爲例:

//重載插入運算符 <<
friend ostream &operator<<( ostream &output, const Distance &D )
{ 
    output << "F : " << D.feet << " I : " << D.inches;
    return output;            
}
//重載提取運算符 >>
friend istream &operator>>( istream  &input, Distance &D )
{ 
    input >> D.feet >> D.inches;
    return input;            
}

賦值運算符重載

重載賦值運算符( = ),用於創建一個對象,比如拷貝構造函數。

// 所需的構造函數
Distance(){
   feet = 0;
   inches = 0;
}
Distance(int f, int i){
    feet = f;
    inches = i;
}
//重載賦值運算符 =
void operator=(const Distance &D )
{ 
    feet = D.feet;
    inches = D.inches;
}

當使用 用戶自定義類型變量內置類型變量賦值時,可以使用自定義類型的隱式轉換:

#include<iostream>
using namespace std;

class Int{
  private:
    int n;
  public:
    Int(int i){
        n = i;
    {
    operator int() // 這裏就是隱式轉換聲明,應注意到它與運算符重載的不同之處
    {
       return n;
    }
};

int main()
{
  Int a(5);
  int c=a; // 隱式調用轉換函數
  cout<<c<<endl;
  cout<<a<<endl; // 由於未重載Int的<<操作符,將隱式調用轉換函數
}

上述代碼的輸出將爲:

5
5

函數調用運算符()重載

函數調用運算符 () 可以被重載用於類的對象。當重載 () 時,您不是創造了一種新的調用函數的方式,相反地,這是創建一個可以傳遞任意數目參數的運算符函數:

// 重載函數調用運算符
Distance operator()(int a, int b, int c)
{
    Distance D;
    // 進行隨機計算
    D.feet = a + c + 10;
    D.inches = b + c + 100 ;
    return D;
}
// 調用 operator()
D2 = D1(10, 10, 10); 

下標運算符[ ] 重載

下標操作符 [] 通常用於訪問數組元素。重載該運算符用於增強操作 C++ 數組的功能:

//下標運算運算符[] 重載
int& operator[](int i)
{
    if( i > SIZE ){
        cout << "索引超過最大值" <<endl; 
        // 返回第一個元素
        return arr[0];
    }
    return arr[i];
}

類成員訪問運算符 -> 重載

類成員訪問運算符( -> )可以被重載,但它較爲麻煩。它被定義用於爲一個類賦予"指針"行爲。類成員訪問運算符 -> 必須是一個成員函數。如果使用了 -> 運算符,返回類型必須是指針或者是類的對象類成員訪問運算符 -> 通常與指針引用運算符 * 結合使用,用於實現"智能指針"的功能。這些指針是行爲與正常指針相似的對象,唯一不同的是,當通過指針訪問對象時,它們會執行其他的任務。比如,當指針銷燬時,或者當指針指向另一個對象時,會自動刪除對象。

間接引用運算符 -> 可被定義爲一個一元后綴運算符。也就是說,給出一個類 Ptr,類 Ptr 的對象可用於訪問類 X 的成員,使用方式與指針的用法十分相似。例如:d

class Ptr{
   // -> 運算符重載
   X * operator->();
};

void f(Ptr p )
{
   p->m = 10 ;
}

 例如,重載類成員訪問運算符 ->:

#include <iostream>
#include <vector>
using namespace std;
 
// 假設一個實際的類
class Obj {
   static int i, j;
public:
   void f() const { cout << i++ << endl; }
   void g() const { cout << j++ << endl; }
};
 
// 靜態成員定義
int Obj::i = 10;
int Obj::j = 12;
 
// 爲上面的類實現一個容器
class ObjContainer {
   vector<Obj*> a;
public:
   void add(Obj* obj)
   { 
      a.push_back(obj);  // 調用向量的標準方法
   }
   friend class SmartPointer;
};
 
// 實現智能指針,用於訪問類 Obj 的成員
class SmartPointer {
   ObjContainer oc;
   int index;
public:
   SmartPointer(ObjContainer& objc)
   { 
       oc = objc;
       index = 0;
   }
   // 返回值表示列表結束
   bool operator++() // 前綴版本
   { 
     if(index >= oc.a.size() - 1) return false;
     if(oc.a[++index] == 0) return false;
     return true;
   }
   bool operator++(int) // 後綴版本
   { 
      return operator++();
   }
   // 重載運算符 ->
   Obj* operator->() const 
   {
     if(!oc.a[index])
     {
        cout << "Zero value";
        return (Obj*)0;
     }
     return oc.a[index];
   }
};
 
int main() {
   const int sz = 10;
   Obj o[sz];
   ObjContainer oc;
   for(int i = 0; i < sz; i++)
   {
       oc.add(&o[i]);
   }
   SmartPointer sp(oc); // 創建一個迭代器
   do {
      sp->f(); // 智能指針調用
      sp->g();
   } while(sp++);
   return 0;
}

 

發佈了178 篇原創文章 · 獲贊 95 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章