C++運算符重載函數作爲類成員函數和友元函數

全文轉自:http://c.biancheng.net/cpp/biancheng/view/217.html

例10.2中對運算符“+”進行了重載,使之能用於兩個複數的相加。在該例中運算符重載函數operator+作爲Complex類中的成員函數。

可能有的讀者會提出這樣的問題:”+“是雙目運算符,爲什麼在例10.2程序中的重載函數中只有一個參數呢?實際上,運算符重載函數有兩個參數,由於重載函數是Complex類中的成員函數,有一個參數是隱含的,運算符函數是用this指針隱式地訪問類對象的成員。可以看到,重載函數operator+訪問了兩個對象中的成員,一個是this指針指向的對象中的成員,一個是形參對象中的成員。如this->real+c2.real,this->real就是c1.real。上節中已說明,在將運算符函數重載爲成員函數後,如果出現含該運算符的表達式,如c1+c2,編譯系統把它解釋爲:
    c1.operator+(c2)
即通過對象c1調用運算符重載函數,並以表達式中第二個參數(運算符右側的類對象c2)作爲函數實參。運算符重載函數的返回值是Complex類型,返回值是複數c1和c2之和(Complex(c1.real + c2.real, c1.imag+c2.imag))。

運算符重載函數除了可以作爲類的成員函數外,還可以是非成員函數。可以將例10.2改寫爲例10.3。

[例10.3] 將運算符“+”重載爲適用於複數加法,重載函數不作爲成員函數,而放在類外,作爲Complex類的友元函數。


 
  1. #include <iostream>
  2. using namespace std;
  3. // 注意,該程序在VC 6.0中編譯出錯,將以上兩行替換爲 #include <iostream.h> 即可順利通過
  4. class Complex
  5. {
  6. public:
  7. Complex( ){real=0;imag=0;}
  8. Complex(double r,double i){real=r;imag=i;}
  9. friend Complex operator + (Complex &c1,Complex &c2); //重載函數作爲友元函數
  10. void display( );
  11. private:
  12. double real;
  13. double imag;
  14. };
  15.  
  16. Complex operator + (Complex &c1,Complex &c2) //定義作爲友元函數的重載函數
  17. {
  18. return Complex(c1.real+c2.real, c1.imag+c2.imag);
  19. }
  20.  
  21. void Complex::display( )
  22. {
  23. cout<<"("<<real<<","<<imag<<"i)"<<endl;
  24. }
  25. int main( )
  26. {
  27. Complex c1(3,4),c2(5,-10),c3;
  28. c3=c1+c2;
  29. cout<<"c1="; c1.display( );
  30. cout<<"c2="; c2.display( );
  31. cout<<"c1+c2 ="; c3.display( );
  32. }


與例10.2相比較,只作了一處改動,將運算符函數不作爲成員函數,而把它放在類外,在Complex類中聲明它爲友元函數。同時將運算符函數改爲有兩個參數。在將運算符“+”重載爲非成員函數後,C++編譯系統將程序中的表達式c1+c2解釋爲
    operator+(c1, c2)
即執行c1+c2相當於調用以下函數:
    Complex operator + (Complex &c1,Complex &c2)
    {
        return Complex(c1.real+c2.real, c1.imag+c2.imag);
    }
求出兩個複數之和。運行結果同例10.2。

爲什麼把運算符函數作爲友元函數呢?因爲運算符函數要訪問Complex類對象中的成員。如果運算符函數不是Complex類的友元函數,而是一個普通的函數,它是沒有權利訪問Complex類的私有成員的。

在上節中曾提到過:運算符重載函數可以是類的成員函數,也可以是類的友元函數,還可以是既非類的成員函數也不是友元函數的普通函數。現在分別討論這3種情況。

首先,只有在極少的情況下才使用既不是類的成員函數也不是友元函數的普通函數,原因是上面提到的,普通函數不能直接訪問類的私有成員。

在剩下的兩種方式中,什麼時候應該用成員函數方式,什麼時候應該用友元函數方式?二者有何區別呢?如果將運算符重載函數作爲成員函數,它可以通過this指針自由地訪問本類的數據成員,因此可以少寫一個函數的參數。但必須要求運算表達式第一個參數(即運算符左側的操作數)是一個類對象,而且與運算符函數的類型相同。因爲必須通過類的對象去調用該類的成員函數,而且只有運算符重載函數返回值與該對象同類型,運算結果纔有意義。在例10.2中,表達式c1+c2中第一個參數c1是Complex類對象,運算符函數返回值的類型也是Complex,這是正確的。如果c1不是Complex類,它就無法通過隱式this指針訪問Complex類的成員了。如果函數返回值不是Complex類複數,顯然這種運算是沒有實際意義的。

如想將一個複數和一個整數相加,如c1+i,可以將運算符重載函數作爲成員函數,如下面的形式:
    Complex Complex∷operator+(int &i)  //運算符重載函數作爲Complex類的成員函數
    {
        return Complex(real+i,imag);
    }
注意在表達式中重載的運算符“+”左側應爲Complex類的對象,如:
    c3=c2+i;
不能寫成
    c3=i+c2;  //運算符“+”的左側不是類對象,編譯出錯
如果出於某種考慮,要求在使用重載運算符時運算符左側的操作數是整型量(如表達式i+c2,運算符左側的操作數i是整數),這時是無法利用前面定義的重載運算符的,因爲無法調用i.operator+函數。可想而知,如果運算符左側的操作數屬於C++標準類型(如int)或是一個其他類的對象,則運算符重載函數不能作爲成員函數,只能作爲非成員函數。如果函數需要訪問類的私有成員,則必須聲明爲友元函數。可以在Complex類中聲明:
    friend Complex operator+(int &i,Complex &c); //第一個參數可以不是類對象
在類外定義友元函數:
    Complex operator+(int &i, Complex &c) //運算符重載函數不是成員函數
    {
        return Complex(i+c.real, c.imag);
    }
將雙目運算符重載爲友元函數時,在函數的形參表列中必須有兩個參數,不能省略,形參的順序任意,不要求第一個參數必須爲類對象。但在使用運算符的表達式中,要求運算符左側的操作數與函數第一個參數對應,運算符右側的操作數與函數的第二個參數對應。如:
    c3=i+c2;  //正確,類型匹配
    c3=c2+i;  //錯誤,類型不匹配

請注意,數學上的交換律在此不適用。如果希望適用交換律,則應再重載一次運算符“+”。如
    Complex operator+(Complex &c, int &i) //此時第一個參數爲類對象
    {
        return Complex(i+c.real, c.imag);
    }
這樣,使用表達式i+c2和c2+i都合法,編譯系統會根據表達式的形式選擇調用與之匹配的運算符重載函數。可以將以上兩個運算符重載函數都作爲友元函數,也可以將一個運算符重載函數(運算符左側爲對象名的) 作爲成員函數,另一個(運算符左側不是對象名的)作爲友元函數。但不可能將兩個都作爲成員函數,原因是顯然的。

C++規定,有的運算符(如賦值運算符、下標運算符、函數調用運算符)必須定義爲類的成員函數,有的運算符則不能定義爲類的成員函數(如流插入“<<”和流提取運算符“>>”、類型轉換運算符)。

由於友元的使用會破壞類的封裝,因此從原則上說,要儘量將運算符函數作爲成員函數。但考慮到各方面的因素,一般將單目運算符重載爲成員函數,將雙目運算符重載爲友元函數。在學習了本章第10.7節例10.9的討論後,讀者對此會有更深入的認識。

說明:有的C++編譯系統(如Visual C++ 6.0)沒有完全實現C++標準,它所提供不帶後綴.h的頭文件不支持把成員函數重載爲友元函數。上面例10.3程序在GCC中能正常運行,而在Visual C++ 6.0中會編譯出錯。但是Visual C++所提供的老形式的帶後綴.h的頭文件可以支持此項功能,因此可以將程序頭兩行修改如下,即可順利運行:
    #include <iostream.h>
以後如遇到類似情況,亦可照此辦理。

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