【C++】運算符重載 三 (總結)

前接文章:

【C++】運算符重載 一

【C++】運算符重載 二 (實例)


c++的一大特性就是重載(overload),通過重載可以把功能相似的幾個函數合爲一個,使得程序更加簡潔、高效。在c++中不止函數可以重載,運算符也可以重載。由於一般數據類型間的運算符沒有重載的必要,所以運算符重載主要是面向對象之間的。

1.一般運算符重載

在進行對象之間的運算時,程序會調用與運算符相對應的函數進行處理,所以運算符重載有兩種方式:成員函數和友元函數。成員函數的形式比較簡單,就是在類裏面定義了一個與操作符相關的函數。友元函數因爲沒有this指針,所以形參會多一個。


# include<iostream>
class A
{
public:
    A(int d):data(d){}
    A operator+(A&);//成員函數
    A operator-(A&);
    A operator*(A&);
    A operator/(A&);
    A operator%(A&);
    friend A operator+(A&,A&);//友元函數
    friend A operator-(A&,A&);
    friend A operator*(A&,A&);
    friend A operator/(A&,A&);
    friend A operator%(A&,A&);
private:
    int data;
};

//成員函數的形式
A A::operator+(A &a)
{
 return A(data+a.data);
}

A A::operator-(A &a)
{
 return A(data-a.data);
}

A A::operator*(A &a)
{
 return A(data*a.data);
}

A A::operator/(A &a)
{
 return A(data/a.data);
}

A A::operator%(A &a)
{
 return A(data%a.data);
}

//友元函數的形式
A operator+(A &a1,A &a2)
{
 return A(a1.data+a2.data);
}

A operator-(A &a1,A &a2)
{
 return A(a1.data-a2.data);
}

A operator*(A &a1,A &a2)
{
 return A(a1.data*a2.data);
}

A operator/(A &a1,A &a2)
{
 return A(a1.data/a2.data);
}

A operator%(A &a1,A &a2)
{
 return A(a1.data%a2.data);
}

//然後我們就可以對類的對象進行+、-、*、/了。
void main(void)
{
 A a1(1),a2(2),a3(3);
 a1=a2+a3;
 //或者
 a1=a2.operator+(a3);
}

注意:在進行a2+a3的時候會出錯,因爲我們在上面對+定義了兩種方法,去掉一種即可。

2.關係運算符重載

因爲函數體比較簡單,後面我就只給出成員函數形式的函數聲明瞭,關係運算符有==,!=,<,>,<=,>=。

bool operator == (const A& ); 
bool operator != (const A& );
bool operator < (const A& );
bool operator <= (const A& );
bool operator > (const A& );
bool operator >= (const A& );

3.邏輯運算符重載

bool operator || (const A& );
bool operator && (const A& );
bool operator ! ();

4.單目運算符重載

這裏的+、-是正負的意思,放在對象前面

A& operator + ();
A& operator - ();
A* operator & ();
A& operator * ();

5.自增減運算符重載

++和–根據位置的不同有四種情況,都可以重載。

A& operator ++ ();//前置++
A operator ++ (int);//後置++
A& operator --();//前置--
A operator -- (int);//後置--

6.位運算符重載

A operator | (const A& );
A operator & (const A& );
A operator ^ (const A& );
A operator << (int i);
A operator >> (int i);
A operator ~ ();

7.賦值運算符重載

沒有=哦。

A& operator += (const A& );
A& operator -= (const A& ); 
A& operator *= (const A& );
A& operator /= (const A& );
A& operator %= (const A& );
A& operator &= (const A& );
A& operator |= (const A& );
A& operator ^= (const A& );
A& operator <<= (int i);
A& operator >>= (int i);

8.內存運算符重載

void *operator new(size_t size);
void *operator new(size_t size, int i);
void *operator new[](size_t size);
void operator delete(void*p);
void operator delete(void*p, int i, int j);
void operator delete [](void* p);

9.重載箭頭操作符

箭頭操作符可能看起來是二元操作符:接受一個對象和一個成員名,對對象解引用以獲取成員。其實箭頭操作符是一元操作符,沒有顯示形參(而且是類成員,唯一隱式形參是this或*this)。->的右操作數不是表達式,而是對應類成員的一個標識符,由編譯器處理獲取成員工作(編譯器對重載箭頭操作符所做的事情,比其它重載操作符要多,這裏也正是複雜的地方)。

箭頭操作符與衆不同。它可能表現得像二元操作符一樣,不管外表如何,箭頭操作符不接受顯式形參。

實際上這裏沒有第二個形參,因爲 -> 的右操作數不是表達式,相反,是對應着類成員的一個標識符。沒有明顯可行的途徑將一個標識符作爲形參傳遞給函數,相反,由編譯器處理獲取成員的工作。

當這樣編寫時:

 point->action();

由於優先級規則,它實際等價於編寫:

 (point->action)();

換句話說,我們想要調用的是對 point->action 求值的結果。編譯器這樣對該代碼進行求值:

  1. 如果 point 是一個指針 (該指針指向具有名爲 action 的成員的類對象),則編譯器將代碼編譯爲調用該對象的 action 成員。

  2. 否則,如果 point是定義了(重載了) operator-> 操作符的類的一個對象(不是指針),則

point->action 與 point.operator->()->action 相同。

即,執行 point 的 operator->(),然後使用該結果重複這三步。

  1. 否則,代碼出錯。

對重載箭頭的返回值的約束

重載箭頭操作符必須返回指向類類型(對象)的指針,或者返回定義了自己的箭頭操作符的類類型對象。

如果返回類型是指針,則內置箭頭操作符可用於該指針,編譯器對該指針解引用並從結果對象獲取指定成員。如果被指向的類型沒有定義那個成員,則編譯器產生一個錯誤。

如果返回類型是類類型的其他對象(或是這種對象的引用),則將遞歸應用該操作符。編譯器檢查返回對象所屬類型是否具有成員箭頭,如果有,就應用那個操作符;否則,編譯器產生一個錯誤。這個過程繼續下去,直到返回一個指向帶有指定成員的的對象的指針,或者返回某些其他值,在後一種情況下,代碼出錯。

直到調用內部指針操作才停止。

10.特殊運算符重載

上面的運算符重載都有兩種方式,而下面的運算符只能用一種,特殊吧。 這些運算符的重載只能是成員函數。

A& operator = (const A& );
char operator [] (int i);//返回值不能作爲左值
const char* operator () ();
T operator -> ();
//類型轉換符
operator char* () const;
operator int ();
operator const char () const;
operator short int () const;
operator long long () const;
//還有很多就不寫了

而這些只能以友元函數的形式重載。

friend inline ostream &operator << (ostream&, A&);//輸出流
friend inline istream &operator >> (istream&, A&);//輸入流

11.總結

兩種重載方式的比較: 
一般情況下,單目運算符最好重載爲類的成員函數;雙目運算符則最好重載爲類的友元函數。 
以下一些雙目運算符不能重載爲類的友元函數:=、()、[]、->。 
類型轉換函數只能定義爲一個類的成員函數而不能定義爲類的友元函數。 C++提供4個類型轉換函數:reinterpret_cast(在編譯期間實現轉換)、const_cast(在編譯期間實現轉換)、stactic_cast(在編譯期間實現轉換)、dynamic_cast(在運行期間實現轉換,並可以返回轉換成功與否的標誌)。 
若一個運算符的操作需要修改對象的狀態,選擇重載爲成員函數較好。 
若運算符所需的操作數(尤其是第一個操作數)希望有隱式類型轉換,則只能選用友元函數。 
當運算符函數是一個成員函數時,最左邊的操作數(或者只有最左邊的操作數)必須是運算符類的一個類對象(或者是對該類對象的引用)。如果左邊的操作數必須是一個不同類的對象,或者是一個內部 類型的對象,該運算符函數必須作爲一個友元函數來實現。 
當需要重載運算符具有可交換性時,選擇重載爲友元函數。 
注意事項: 
除了類屬關係運算符”.“、成員指針運算符”.*“、作用域運算符”::“、sizeof運算符和三目運算符”?:“以外,C++中的所有運算符都可以重載。 
重載運算符限制在C++語言中已有的運算符範圍內的允許重載的運算符之中,不能創建新的運算符。 
運算符重載實質上是函數重載,因此編譯程序對運算符重載的選擇,遵循函數重載的選擇原則。 
重載之後的運算符不能改變運算符的優先級和結合性,也不能改變運算符操作數的個數及語法結構。 
運算符重載不能改變該運算符用於內部類型對象的含義。它只能和用戶自定義類型的對象一起使用,或者用於用戶自定義類型的對象和內部類型的對象混合使用時。 
運算符重載是針對新類型數據的實際需要對原有運算符進行的適當的改造,重載的功能應當與原有功能相類似,避免沒有目的地使用重載運算符。

參考文獻 
[1] 本文來自Wuyuan’s Blog 轉載, 文章地址: http://wuyuans.com/2012/09/cpp-operator-overload 
[2]《C++ Primer》

 


參考引用來源: c++運算符重載總結

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