C++運算符重載三種形式(成員函數,友元函數,普通函數)詳解

三種重載方式

首先,介紹三種重載方式:ios

 1 //做爲成員函數重載(常見)
 2 class Person{
 3 Private:
 4      string name;
 5      int age;
 6 public:
 7      Person(const char* name, int age):name(name),age(age){}
 8       bool operator<(const Person& b);
 9   
10 };
11 bool Person::operator<(const Person& b)
12 {
13 //做爲成員函數時,*this即爲左操做數a
14 ...
15 }

 

 

 1 //做爲友元函數重載
 2 class Person{
 3 private:
 4     string name;
 5     int age;
 6 public:
 7     Person(const char* name, int age):name(name),age(age){}
 8      friend bool operator<(const Person& a,const Person& b);
 9  
10 };
11 bool operator<(const Person& a,const Person& b)
12 {
13 ...
14 }

 

 

 1 //做爲普通函數重載(不推薦)
 2 class Person{
 3 public://注意,重載運算符爲普通函數時,使用到的類成員必須爲public
 4     string name;
 5     int age;
 6 public:
 7     Person(const char* name, int age):name(name),age(age){}
 8  
 9 };
10 bool operator<(const Person& a,const Person& b)
11 {
12 ...
13 }

 

做爲成員函數重載

先介紹第一種:函數

bool Person::operator<(const Person& b),bool是函數返回類型,Person::只是指定了成員函數所屬類名。this

 

在做爲函數成員重載中,先看下這句話:單目運算符做爲類成員函數重載時沒有型參(除了後置自增(自減)有一個整型參數:詳細點擊),雙目運算符做爲類成員函數重載時只有一個型參,做爲運算符的右操做數,其左操做數就是本對象本身,也就是this。spa

 

單目運算符通常重載爲成員函數。.net

 

所以在做爲成員函數進行重載時,是以指針

 

 1 #include <iostream>
 2 #include <cstdlib>
 3 
 4 using namespace std;
 5 
 6 class A {
 7 private:
 8     int a;
 9     int b;
10 public:
11     A(int x = 0, int y = 0):a(x), b(y){}
12     A operator+ (A &C);
13 };
14 
15 A A::operator+ (A &C) {
16     A G;
17     G.a = this->a + C.b;
18 
19     return G;
20 }
21 
22 int main()
23 {
24     A G(5, 6);
25     A J(7, 8);
26     A K;
27     K = G + J;
28 
29     return 0;
30 }

 

 

 

而計算機對於K = G + j;進行重載後形式是code

K = G.operator+(J);

G爲對象,J爲參數。htm

 

做爲友元函數重載

而對於第二種形式的重載: 友元函數(友元函數則是指某些雖然不是類成員卻可以訪問類的全部成員的函數)進行重載,那麼它就不存在this指針了,因此須要定義兩個參數來運算(對於雙目運算符),而友元函數的實現能夠在外面定義,但必須在類內部聲明對象

 

 1 #include <iostream>
 2 #include <cstdlib>
 3 
 4 using namespace std;
 5 
 6 class A {
 7 private:
 8     int a;
 9     int b;
10 public:
11     A(int x = 0, int y = 0):a(x), b(y){}
12     friend A operator+ (A &C, A &D);
13 };
14 
15 A operator+ (A &C, A &D) {
16     A G;
17     G.a = D.a + C.b;
18 
19     return G;
20 }
21 
22 int main()
23 {
24     A G(5, 6);
25     A J(7, 8);
26     A K;
27     K = G + J;
28 
29     return 0;
30 }

推薦類內聲明,外部定義,這樣不會顯得類臃腫。blog

對於K = G + J;計算機將重載爲:

K = operator+(G, J);

 

聲明爲友元函數的好處:

1.和普通函數重載相比,它可以訪問非公有成員。

2.將雙目運算符重載爲友元函數,這樣就可使用交換律。

弊端:

 友元能夠像類成員同樣訪問類的成員和函數,可是使用不慎會形成破壞類的封裝性。

 

第一條沒解釋的必要,跳過,哈哈哈哈嗝。        =。=

第二條:交換律也能夠理解成對操做數對稱處理。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 //複數類
 5 class Complex{
 6 public:
 7     Complex(): m_real(0.0), m_imag(0.0){ }
 8     Complex(double real, double imag): m_real(real), m_imag(imag){ }
 9     Complex(double real): m_real(real), m_imag(0.0){ }  //轉換構造函數
10 public:
11     friend Complex operator+(const Complex &c1, const Complex &c2);
12 public:
13     double real() const{ return m_real; }
14     double imag() const{ return m_imag; }
15 private:
16     double m_real;  //實部
17     double m_imag;  //虛部
18 };
19 
20 //重載+運算符
21 Complex operator+(const Complex &c1, const Complex &c2){
22     Complex c;
23     c.m_real = c1.m_real + c2.m_real;
24     c.m_imag = c1.m_imag + c2.m_imag;
25     return c;
26 }
27 
28 int main(){
29     Complex c1(25, 35);
30     Complex c2 = c1 + 15.6;
31     Complex c3 = 28.23 + c1;
32     cout<<c2.real()<<" + "<<c2.imag()<<"i"<<endl;
33     cout<<c3.real()<<" + "<<c3.imag()<<"i"<<endl;
34    
35     return 0;
36 }

若是將 operator+ 定義爲成員函數,根據「+」 運算符具備左結合性」這條原則,Complex c2 = c1 + 15.6;會被轉換爲下面的形式:

Complex c2 = c1.operator+(Complex(15.6));

 

這就是經過對象調用成員函數,是正確的。而對於Complex c3 = 28.23 + c1;,編譯器會嘗試轉換爲不一樣的形式:

Complex c3 = (28.23).operator+(c1);

 

很顯然這是錯誤的,由於 double 類型並無以成員函數的形式重載 +。

也就是說,以成員函數的形式重載 +,只能計算 c1 + 15.6,不能計算 28.23 + c1,這是不對稱的

 

將22-25行代碼可替換爲

return (c1.real + c2.real, c1.image + c2.image);//調用構造函數

將會建立一個匿名對象返回。

 

使用&的好處:

將重載的返回類型定義爲引用類型,可以實現連續輸入(輸出)。

 1 #include <iostream>
 2 #include <cstdlib>
 3 
 4 using namespace std;
 5 
 6 class A {
 7 private:
 8     int a;
 9     int b;
10 public:
11     A(int x = 0, int y = 0):a(x), b(y){}
12     friend A operator+ (A &C, A &D);
13     friend ostream& operator<<(ostream & out, A &W);
14 };
15 
16 A operator+ (A &C, A &D) {
17     A G;
18     G.a = D.a + C.b;
19 
20     return G;
21 }
22 
23 ostream& operator<<(ostream & out, A &W) {
24     out << W.a << " " << W.b;
25 
26     return out;
27 }
28 
29 int main()
30 {
31     A G(5, 6);
32     A J(7, 8);
33     A K;
34     K = G + J;
35 
36     cout << K << " " << J;
37 
38     return 0;
39 }

將流提取運算符 >> 或流插入運算符 << 聲明爲友元函數,可以訪問非公有成員。

 

 做爲普通函數重載

對於第三種普通函數重載:

由於不屬於類了,天然也就沒比較加Person::

 

要注意的是這種形式沒法訪問非公有成員。

 

 

其餘知識點:

爲何咱們要使用兩個參數(非成員函數形式)來重載流提取運算符 >> 和流插入運算符 << 呢?

 

若是咱們要用成員函數,則會有cout.operator<<(const A& W),但重載雙目操做符(即爲類的成員函數),就只要設置一個參數做爲右側運算量,而左側運算量就是對象自己,而cin和cout並非對象自己(你若是聲明爲對象,下面豈不是this->a + W.b,注意this是類對象的)

 

若是必定要聲明爲成員函數,只能成爲以下的形式:

#include <iostream>
#include <string>

using namespace std;

class A {
public:
    A(const string &s = "hello"):str(s){}
    ostream& operator<<(ostream &ou) {
        ou << this->str;

        return ou;
    }
private:
    string str;
};

int main()
{
    A a;

    a << cout;//輸出"hello"

    system("PAUSE");
    return 0;
}

 

 

因此在運用這個<<運算符時就變爲這種形式了:

t<<cout;

而不是

 cout<<t

並且也沒法鏈式使用了 

cout<<t<<t<<t<<endl;

 

對於t << cout 咱們能夠反向理解cout << t,cout >> t會被重載爲cout.operator(t),錯誤。

 

這段借用網上的:

不能重載的根本緣由在於, 大部份的標準庫實現中,對ostream,istream類體系採用了構造函數保護繼承的方式。。。導致即便以繼承的方式來擴展流類,也會在對象實例化時遭遇阻礙。。。 另外一方面,標準庫中的流類,其插入符函數沒有聲明爲虛函數,所以子類不能對其實現進行覆蓋,因此也使成員函數重載遭遇到實質的困難。。。 總的來講,C++標準I/O庫很是繁雜且難,其實現思想不少都與常規的OOP有所出入。。。在使用的時候要謹慎,並最好聽從慣例。。。

 

至於單目運算符的聲明定義就很少作介紹了,要看的點擊此處,前面腳本之家的連接也有單目運算符重載的介紹。

 

附:今天早上上機時發現VC++6.0不支持流提取運算符和流插入運算符的友元函數形式重載。

 

歡迎你們一塊兒交流,成長。

相關文章
相關標籤/搜索