操作符重載手冊(轉)

一、重載規則

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. operator 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 的重載實現應大致如下:
void * ... operator new(size_t size ... )
{
    if(size == 0)
         size = 1;

    while(1)
    {
        ... // allocate memery
        if(allocate sucessfull)
            return ... // return the pointer.

        new_handler curhandler = set_new_handler(0);
        set_new_handler(curhandler); // get current new handler

        if(curhandler == 0)
            (*curhandler)();
        else
            throw std::bad_alloc();
    }
}
    重載 operator delete 簡單許多, 只需注意 C++ Standard 要求刪除一個 NULL 是安全的即可.

    4.重載 operator new

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

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

    通過使用不同的參數類型, 可以重載 operator new, 例如 :
void * operator new(size_t size, int x, int y, int z)
{
    ...
}

X * pX = new (1, 2, 3) X;
    你還可以爲 operator new 的重載使用默認值, 其原則和普通函數重載一樣, 只要不造成和已存在的形式發生衝突即可. 可能你已經想到了, 你甚至還可以在 operator new 中使用不定參數, 如果你真的需要的話.
void * operator new(size_t size, int x, int y = 0, int z = 0)
{
    ...
}

X * pX = new (10) X;
Y * pY = new (10, 10) Y;
Z * pZ = new (10, 10, 10) Z;

...
void * operator new(size_t size, ...)
...
    在全局空間中也可直接重載 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 也是可以繼承的.
class X{
...
static void * operator new(size_t size); // ... (1)
static void * operator new(size_t size, int); // ... (2)
};

class Y : public X{
...
};

class Z : public X{
...
static void * operator new(size_t size); // ... (3)
};

X * pX1 = new X; // call (1)
X * pX2 = ::new X; // call default operator new
X * pX3 = new (0) X; // call (2)

Y * pY1 = new Y; // call (1)

Z * pZ1 = new Z; // call (3)
Z * pZ2 = ::new Z; // call default operator new
Z * pZ3 = X::new Z; // error, no way to call (1)
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)
{
    Carrot result = lhs;
    return result += rhs;
}

【注】1. 如果可能, 應考慮使用 operator+= 來實現 operator+ .
         2. operator+ 不能返回引用, 應返回值類型.
         3. 爲了阻止形如 (a + b) = c 的調用, 應將返回值聲明爲 const .

· const Carrot operator-(const Carrot& lhs, const Carrot& rhs)
const Carrot operator*(const Carrot& lhs, const Carrot& rhs)
{
    ...
}

【注】1. operator* 還可以重載爲提領操作符.

· 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)
{
    ...
}

【注】1. operator& 還可以重載爲取地址操作符.

· const Carrot operator|(const Carrot& lhs, const Carrot& rhs)
const Carrot Carrot::operator-() const
{
    Carrot result = (*this);
    ... // chang the value to negative
    return result;
}

【注】1. 一元操作符, 取負.

· 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)
{
    if(this == &rhs)
        return *this; // may be (*this) == rhs if needs.

    Barley::operator=(rhs); // if Carrot derived from Barley

     ... // assignments every memeber of Carrot.

    return *this;
}

【注】1. 爲了實現形如 x=y=z=0 的串聯賦值操作, operator= 必須傳回 *this 的非常量引用.
         2. 在賦值時應注意檢查是否爲自賦值 ( a = a ).

Carrot& Carrot::operator+=(const Carrot& rhs)
{
     ...
    return *this;
}

【注】1. C++ 允許 (x += 1) = 0 形式的賦值操作, operator+= 必須傳回 *this 的非常量引用.

· 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)
{
    ...
    return _IStr;
}

【注】1. 爲了遵照使用習慣(cin>>x 而不是 x>>cin), 對流操作的 operator>> 應爲非成員函數.

ostream& operator<<(ostream& _OStr, const Carrot& rhs)
{
    ...
    return _OStr;
}

const Carrot operator>>(const Carrot& lhs, int rhs)
{
    Carrot result = lhs;
    ...
    return result;
}

【注】1. 移位操作的重載方式.

· const Carrot operator<<(const Carrot& lhs, int rhs)
Carrot& Carrot::operator>>=(int rhs)
{
     ...
    return *this;
}

【注】1. 移位操作.

· 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
{
    ...
}

【注】1. 基於以下原因, 你應該避免重載 operator&& 和 operator|| :
               (1). && 和 || 是一個邏輯操作符, 只對 bool 型別有才有明確的語意.
               (2). 重載的 operator&& 和 operator|| 無法模擬操作符默認的驟死式語義[5].
               (3). 你無法保證重載後操作符的參數求值次序(C++ Stand 保證了默認的 && 和 || 按從左到右求值).
         2.自定義型別可以考慮提供到 bool 的轉型操作來支持此操作符.

· bool operator||(const Carrot& lhs, const Carrot& rhs) X
Carrot& Carrot::operator++()
{
    (*this) += 1; // or other implement
    return *this;
}

【注】1. 前置操作(prefix): ++carrot

const Carrot Carrot::operator++(int)
{
    Carrot oldValue = *this;
    ++(*this);
    return oldValue;
}

【注】1. 後置操作(postfix): carrot++ 調用時編譯器自動生成一個 0 作爲參數.
         2. 爲了禁止 carrot++++ 操作, 返回值應爲 const.
         3. 從實現和參數可以看出,後置操作的效率遠遠低於前置操作, 所以如非必要儘量使用前置操作.
         4. 爲了保證遞增行爲一致, 並便於維護後置操作最好使用前置操作來完成遞增行爲.

· Carrot& Carrot::operator--()
· const Carrot Carrot::operator--(int)
const Carrot operator,(const Carrot& lhs, const Carrot& rhs) X
{
    ...
}

【注】1. 基於以下原因, 你應該避免重載 operator, :
               (1). 即使沒有重載, 默認,號操作符也可以施用於自定義型別上.
               (2). C++ 保證 , 操作符的求值是從左到右的, 而重載後無法保證此行爲.

const PMFC Carrot::operator->*(ReturnType (T::*pmf)()) const
{
    ...
}

【注】1. operator->* 重載比較少見, 有興趣可參考Implementing operator->* for Smart Pointers.

const Carrot* Carrot::operator&() const X
{
    ...
}

【注】1. 你應該儘量避免重載取地址操作符.

Coca& Carrot::operator*()
{
    ...
}

【注】1. 重載提領操作符應提供 const 版和非 const 版.

· const Coca& Carrot::operator*() const
Coca* Carrot::operator->()
{
    ...
}

【注】1. 重載 operator-> 應提供 const 版和非 const 版.
         2. operator-> 的返回值必須爲一個指針或可以應用 -> 操作的類型.
         3. 如果 operator-> 的返回的不是一個指針, C++ 會繼續在返回類型上應用 -> 直到得到一個指針爲止.

· const Coca* Carrot::operator->() const
Coca& Carrot::operator[](KeyType index)
{
    ...
}

【注】1. operator[] 應提供 const 版本和非 const 版.
         2. KeyType 可以爲任意類型, 但通常爲 int 型.

· const Coca& Carrot::operator[](KeyType index) const
AnyType Carrot::operator()(...)
{
    ...
}

【注】1. operator () 重載的是函數運算符, 改變表達式優先級的 () 是不能重載的.
         2. 重載了 operator() 的對象被成爲仿函數, 使用方法和函數指針類似.

static void* Carrot::operator new(size_t size, ...)
{
    if(size == 0)
         size = 1;

    while(1)
    {
        ... // allocate memery
        if(allocate sucessfull)
            return ... // return the pointer.

        new_handler curhandler = set_new_handler(0);
        set_new_handler(curhandler); // get current new handler

        if(curhandler == 0)
            (*curhandler)();
        else
            throw std::bad_alloc();
    }

【注】1. 參考重載 operator new.

· static void* Carrot::operator new[](size_t size, ...)
static void Carrot::operator delete(void * memory, size_t size)
{
    if(memorty == NULL)
        return;
    ...
}

【注】1. 參考重載 operator new.

· 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 中定義如下:
#define __PLACEMENT_NEW_INLINE
inline void *__cdecl operator new(size_t, void *_Where) _THROW0()
{ // construct array with placement at _Where
return (_Where);
}
3.和 placement new 一樣, nothrow new 也是 operator new 的一種重載形式, nothrow 是 中聲明的一個對象. 該重載在 VC7.1 中的聲明爲:
...
struct nothrow_t
    { // placement new tag type to suppress exceptions
    };

extern const nothrow_t nothrow; // constant for placement new tag
...
void *__cdecl operator new(size_t, const std::nothrow_t&)
    _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|| 是作爲一個函數, 要在所有的參數求值完成後纔會調用此操作符.

▲參考資料:

1. Bjarne Stroustrup.The C++ Programming Language(Special Edition)
2. Scott Meyers.Effective C++(2nd Edition)
3. Andrei Alexandrescu.Modern C++ Design
4. Robert B.Murray.C++ Strategies and Tactics
5. Scott Meyers.More Effective C++
6. John Lakos.Large-Scale C++ Software Design
7. Scott Meyers.Implementing operator->* for Smart Pointers
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章