C++操作符重載手冊

原文鏈接:http://www.adintr.com/myarticle/operator.html


一、重載規則

I.可以重載的操作符

+ - * / %
^ & | ~ !
= > < += -=
*= /= %= ^= &=
|= >> << >>= <<=
== != >= <= &&
|| ++ -- ->* ,
-> [] () operator new operator new[]
operator delete operator delete []

II.不能重載的操作符

:: . .* ? :
sizeof typeid new delete
static_cast dynamic_cast const_cast reinterpret_cast

III.基本規則

  1. 一元操作符可以是不帶參數的成員函數[1] 或帶一個參數的非成員函數[1]
  2. 二元操作符可以是帶一個參數的成員函數[1]或帶兩個參數的 非成員函數[1]
  3. operator=、operator[]、operator()、operator->只能定義爲成員函數[1]
  4. operator->的返回值必須是一個指針或能使用->的對象。
  5. 重載 operator++ 和 operator-- 時帶一個 int 參數表示後綴,不帶參數表示前綴。
  6. 除 operator new 和 operator delete 外,重載的操作符參數中至少要有一個非內建數據類型。
  7. x@y 搜索範圍爲:x 成員函數--> 全局函數/X所在名字空間中的函數/Y所在名字空間中的函數/X的友元函數/Y的友元函數。
  8. 重載的的操作符應儘量模擬操作符對內建類型的行爲。

二、使用重載

I.操作符重載的一些建議

  1. 只將會改變第一個參數的值的操作符(如: +=)定義爲成員函數,而將返回一個新對象的操作符(如: +)定義爲非成員函數(並使用 += 來實現)。
  2. 只有非成員函數才能在左參數上實施性別轉換,如果需要進行轉換則應將操作符定義爲非成員函數。
  3. 對一元操作符, 爲避免隱式轉換最好將其重載爲成員函數。
  4. 對二元操作符, 爲能在左操作數上能進行和右操作數一樣的隱式轉換, 最好將其重載爲非成員函數。
  5. 爲了遵照使用習慣,operator>>、operator<< 應定義爲非成員函數。
  6. 重載 operator[] 之類的操作符, 應儘量提供 const 版本和非 const 版本。
  7. 關於將操作符定義爲成員或非成員可參考以下建議:
    操作符 建議
    所有一元操作符   成員
    = () [] ->   必須爲成員
    += -= /= *= ^= &= != %= >>= <<=   成員
    其它二元操作符   非成員
  8. 如果默認操作符已經可以施用於你的型別上, 則應儘量避免重載此操作符. 如 operator, 、operator&(取地址) 等等.

II. 重載 operator new

  1. 爲什麼要重載 operator new ?
    [效率問題] 通常系統默認提供的分配器速度極慢, 而且分配小型對象時空間浪費嚴重.
    [改變行爲] 默認的分配器失敗時會拋出異常, 或許你想改變這種行爲.
  2. perator new 的行爲
    [區分三個不同的 new]

    new 操作符(new 表達式, new operator, new expression): 通常我們調用 X * pX = new X 時使用的就是這個操作符, 它由語言內建, 不能重載, 不能改變其行爲. 它包括分配內存的 operator new 和調用構造函數的 placement new.

    operator new :opeator new 是一個函數, void * operator new(size_t size) . 它分配指定大小的內存, 可以被重載, 可以添加額外的參數, 但第一個參數必須爲 size_t. operator new 除了被 new operator 調用外也可以直接被調用: void * rawMem = operator new(sizeof(X)).

    placement new : placement new 在一塊指定的內存上使用構造函數, 包含頭文件 <new> 之後也可以直接使用 placement new: X * pX = new (rawMem) X. [2]

    與 new operator 類似, 對於 delete operator, 也存在 operator delete: void operator delete(void *), 析構方法 pX->~X().

    [operator new 的錯誤處理]

    默認的 operator new 在內存分配失敗時將會拋出 std::bad_alloc 異常; nothrow new[3] 
    (X * pX = new (nothrow) X) 在內存分配失敗時將會返回 0 . 這種行爲可以通過設置 new-handler 來改變. new-handler 是一個回調函數指針, typedef void(*new_handler)(void). 通過 set_new_handler(new_handler) 函數設置回調句柄後, 如果分配內存失敗, operator new 將會不斷的調用 new-handler 函數, 直到找到足夠的內存爲止. 爲了避免死循環, new-handler 函數必須具備以下行爲之一:

    1. 找到可用的內存.
    2. 安裝其它的 new-handler 函數.
    3. 卸除 new-handler, 即 set_new_hanlder(0), 這樣下此循環將恢復默認行爲拋出異常或返回 0.
    4. 拋出異常.
    5. 保存錯誤日誌, 退出程序.

  3. 準備重載 operator new

    重載 operator new 時需要兼容默認的 operator new 錯誤處理方式. 另外, C++ Standard 規定當要求的內存爲 0 byte 時也應該返回有效的內存地址. 所以 operator new 的重載實現應大致如下:

    1. void * ... operator  new(size_t  size ... )  
    2. {  
    3.     if (size == 0)  
    4.              size = 1;   
    5.   
    6.     while (1)  
    7.     {   
    8.         ...  // allocate memery  
    9.         if (allocate sucessfull)  
    10.             return  ... // return the pointer.  
    11.   
    12.         new_handler curhandler = set_new_handler(0);  
    13.         set_new_handler(curhandler);     // get current new handler  
    14.   
    15.         if (curhandler != 0)  
    16.             (*curhandler)();  
    17.         else   
    18.              throw std::bad_alloc();  
    19.     }  
    20. }  

    重載 operator delete 簡單許多, 只需注意 C++ Standard 要求刪除一個 NULL 是安全的即可.

  4. 重載 operator new

    opeator new 的重載和其它操作符大不相同. 首先, 即使你不重載, 默認的 operator new 也可施用於你的自定義型別上(operator, 也具有此特性), 而其它操作符如果不進行重載就無法使用. 其次, 其它重載其它操作符時參數個數都是固定的, 而 operator new 的參數個數是可以任意的, 只需要保證第一個參數爲 size_t, 返回類型爲 void * 即可, 而且其重載的參數類型也不必包含自定義類型. 更一般的說, operator new 的重載更像是一個函數的重載, 而不是一個操作符的重載.

    [★ 用不同的參數重載 operator new]

    通過使用不同的參數類型, 可以重載 operator new, 例如 :

    1. void * operator  new(size_t  size, int x,  int y, int  z)  
    2. {  
    3.     ...  
    4. }  
    5.   
    6. X * pX = new  (1, 2, 3) X;  

    你還可以爲 operator new 的重載使用默認值, 其原則和普通函數重載一樣, 只要不造成和已存在的形式發生衝突即可. 可能你已經想到了, 你甚至還可以在 operator new 中使用不定參數, 如果你真的需要的話.

    1. void  * operator newsize_t size, int  x, int y = 0, int z = 0)  
    2. {  
    3.     ...  
    4. }  
    5.   
    6. X * pX = new (10) X;  
    7. Y * pY = new  (10, 10) Y;  
    8. Z * pZ = new (10, 10, 10) Z;  
    9.   
    10. ...  
    11. void * operator  new(size_t size, ...)  
    12. ...  

    在全局空間中也可直接重載 void * operator new(size_t size) 函數, 這將改變所有默認的 new 操作符的行爲, 不建議使用.

    [★ 重載 class 專屬的 operator new]

    爲某個 class 重載 operator new 時必須定義爲類的靜態函數[4], 因爲 operator new 會在類的對象被構建出來之前調用. 即是說調用 operator new 的時候還不存在 this 指針, 因此重載的 operator new 必須爲靜態的. 當然在類中重載 operator new 也可以添加額外的參數, 並可以使用默認值.另外, 和普通函數一樣, operator new 也是可以繼承的.

    1. class X{  
    2. ...  
    3. static  void * operator  new(size_t  size);    // ... (1)   
    4. static  void * operator new (size_t size, int);   // ... (2)  
    5. };  
    6.   
    7. class Y :  public X{  
    8. ...  
    9. };  
    10.   
    11. class Z :  public X{  
    12. ...  
    13. static void  * operator new(size_t  size);    // ... (3)  
    14. };  
    15.   
    16. X * pX1 = new X;      // call (1)  
    17. X * pX2 = ::new  X;  // call default operator new   
    18. X * pX3 = new  (0) X;    // call (2)  
    19.   
    20. Y * pY1 =  new Y;     // call (1)   
    21.   
    22. Z * pZ1 =  new Z;     // call (3)   
    23. Z * pZ2 = ::new  Z; // call default operator new  
    24. Z * pZ3 = X::new Z;  // error, no way to call (1)  
    25. Z * pZ4 = new (0) Z;  // error, no way to call (2)  
  5. 重載 operator delete

    如果你重載了一個 operator new, 記得一定要在相同的範圍內重載 operator delete. 因爲你分配出來的內存只有你自己才知道如何釋放. 如果你忘記了, 編譯器不會給你任何提示, 它將會使用默認的 operator delete 來釋放內存. 這種忘記的代價是慘重的, 你得時刻在寫下 operator new 的同時寫下 operator delete.

    如果在類中使用 operator delete, 也必須將其聲明爲靜態函數. 因爲調用 operator delete 時對象已經被析構掉了. operator delete 的重載可以有兩種形式:

    1. void operator delete(void * mem)
    2. void operator delete(void * mem, size_t size)

    並且這兩種形式的 operator delete 可以同時存在, 當調用 delete px 時, 如果 (1) 式存在的話將調用 (1) 式. 只有在 (1) 式不存在時纔會調用 (2) 式. 對第 (2) 種形式的 operator delete, 如果用基類指針刪除派生類對象, 而基類的析構函數沒有虛擬的時候, size 的值可能是錯誤的.

三、重載參考

展開全部

 const Carrot operator+(const Carrot& lhs, const Carrot& rhs) 
· const Carrot operator-(const Carrot& lhs, const Carrot& rhs)
 const Carrot operator*(const Carrot& lhs, const Carrot& rhs) 
· const Carrot operator/(const Carrot& lhs, const Carrot& rhs)
· const Carrot operator%(const Carrot& lhs, const Carrot& rhs)
· const Carrot operator^(const Carrot& lhs, const Carrot& rhs)
 const Carrot operator&(const Carrot& lhs, const Carrot& rhs) 
· const Carrot operator|(const Carrot& lhs, const Carrot& rhs)
 const Carrot Carrot::operator-() const 
· const Carrot Carrot::operator~() const
· bool Carrot::operator!() const
· bool operator>(const Carrot& lhs, const Carrot& rhs)
· bool operator<(const Carrot& lhs, const Carrot& rhs)
 Carrot& Carrot::operator=(const Carrot& rhs) 
 Carrot& Carrot::operator+=(const Carrot& rhs) 
· Carrot& Carrot::operator-=(const Carrot& rhs)
· Carrot& Carrot::operator*=(const Carrot& rhs)
· Carrot& Carrot::operator/=(const Carrot& rhs)
· Carrot& Carrot::operator%=(const Carrot& rhs)
· Carrot& Carrot::operator^=(const Carrot& rhs)
· Carrot& Carrot::operator&=(const Carrot& rhs)
· Carrot& Carrot::operator|=(const Carrot& rhs)
 istream& operator>>(istream& _IStr, Carrot& rhs) 
 ostream& operator<<(ostream& _OStr, const Carrot& rhs) 
 const Carrot operator>>(const Carrot& lhs, int rhs) 
· const Carrot operator<<(const Carrot& lhs, int rhs)
 Carrot& Carrot::operator>>=(int rhs) 
· Carrot& Carrot::operator<<=(int rhs)
 bool operator==(const Carrot& lhs, const Carrot& rhs) 
· bool operator!=(const Carrot& lhs, const Carrot& rhs)
· bool operator>=(const Carrot& lhs, const Carrot& rhs)
· bool operator<=(const Carrot& lhs, const Carrot& rhs)
 bool operator&&(const Carrot& lhs, const Carrot& rhs) X 
· bool operator||(const Carrot& lhs, const Carrot& rhs) X
 Carrot& Carrot::operator++() 
 const Carrot Carrot::operator++(int) 
· Carrot& Carrot::operator--()
· const Carrot Carrot::operator--(int)
 const Carrot operator,(const Carrot& lhs, const Carrot& rhs) X 
 const PMFC<ReturnType> Carrot::operator->*(ReturnType (T::*pmf)()) const 
 const Carrot* Carrot::operator&() const X 
 Coca& Carrot::operator*() 
· const Coca& Carrot::operator*() const
 Coca* Carrot::operator->() 
· const Coca* Carrot::operator->() const
 Coca& Carrot::operator[](KeyType index) 
· const Coca& Carrot::operator[](KeyType index) const 
 AnyType Carrot::operator()(...) 
 static void* Carrot::operator new(size_t size, ...) 
· static void* Carrot::operator new[](size_t size, ...)
 static void Carrot::operator delete(void * memory, size_t size) 
· static void Carrot::operator delete[](void * momery, size_t size)

▲注:

  1. 本文中成員函數僅指非靜態成員函數;非成員函數包括自由函數,靜態成員函數,友元函數。 
  2. 這種調用方法使用了 operator new 的一種重載形式: void * operator new(size_t, void * buff) . 這種重載方式直接返回了傳入的內存地址, 所以這種調用方式中 operator new 其實什麼也沒做, 於是這個 new operator 調用只使用了其中調用構造函數的部分, 相當於是在調用 placement new. 該重載在 VC7.1 中定義如下:
    1. #define  __PLACEMENT_NEW_INLINE  
    2. inline  void *__cdecl operator  new(size_tvoid *_Where) _THROW0()  
    3. {    // construct array with placement at _Where   
    4. return  (_Where);  
    5. }  
  3. 和 placement new 一樣, nothrow new 也是 operator new 的一種重載形式, nothrow 是 中聲明的一個對象. 該重載在 VC7.1 中的聲明爲:
    1. ...  
    2. struct nothrow_t  
    3.     {    // placement new tag type to suppress exceptions   
    4.     };  
    5.   
    6. extern  const nothrow_t  nothrow// constant for placement new tag  
    7. ...  
    8. void  *__cdecl operator newsize_tconst  std::nothrow_t&)  
    9.     _THROW0();  
  4. 在 VC7.1 中, 不將 operator new 聲明爲靜態函數仍然可以通過編譯, 但是如果在函數裏面使用了非靜態成員則會產生編譯錯誤. 可見, 即使不將 operator new 聲明爲靜態函數編譯器也會將其作爲靜態函數來處理的. 使用是即使編譯器默認把 operator new 當作靜態函數也應該顯式的將其聲明爲 static 函數. ←
  5. 即對 && 操作符如果左操作數爲 false, 則直接返回 false 而不會去檢測右操作數; 對 || 操作符如果左操作數爲 true, 則直接返回 true 而不會檢測右操作數的值. 於是類似這樣的代碼可以是安全的:
        if( (p != NULL) && strlen(p) < 10 )... 
    如果 p == NULL, strlen 就不會被調用. 但是重載的 operator&& 和 operator|| 是作爲一個函數, 要在所有的參數求值完成後纔會調用此操作符. ←
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章