三種重載方式
首先,介紹三種重載方式:
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。
單目運算符一般重載為成員函數。
因此在作為成員函數進行重載時,是以
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;進行重載后形式是
K = G.operator+(J);
G為對象,J為參數。
作為友元函數重載
而對於第二種形式的重載: 友元函數(友元函數則是指某些雖然不是類成員卻能夠訪問類的所有成員的函數)進行重載,那么它就不存在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 }
推薦類內聲明,外部定義,這樣不會顯得類臃腫。
對於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不支持流提取運算符和流插入運算符的友元函數形式重載。
