話運算子過載和友元函式
運算子過載的意義是:將常見的運算子重載出其他的含義:比如將*重載出指標的含義,將<<與cout聯合使用重載出輸出的含義,但需要認識到的問題是:運算子的過載:本質仍然是成員函式,即你可以認為:發生運算子過載時,實際上,是呼叫了成員函式,只不過重新換了個名字,叫運算子過載。
友元函式的意義:使得我們能夠像成員函式一樣訪問私有成員變數,不會受到“許可權”。下面通過一個函式來認識這一點:
1 # ifndef MYTIME3_H_H 2 # define MYTIME3_H_H 3 # include "iostream" 4 class Time 5 { 6 private: 7 int hours; 8 int minutes; 9 public: 10 Time(); 11 Time(int h, int m = 0); 12 void Addmin(int m); 13 void AddHr(int h); 14 void Reset(int h = 0, int m = 0); 15 Time operator+(const Time & t) const;//const放在後面表示不能修改成員函式 16 Time operator-(const Time & t) const; 17 Time operator*(double n) const; 18 friend Time operator*(double m, const Time & t) 19 { 20 return t*m; 21 }//類宣告中定義的函式都是行內函數??? 22 friend std::ostream & operator<< (std::ostream & os, const Time & t); 23 }; 24 #endif
上述是一個關於類宣告的標頭檔案,在此,再次強調這麼幾個東西:
1 10,11行描述了建構函式(運用了函式過載),建構函式的存在意義是完成對類物件的初始化,在定義自己的類物件的時候,一定要保證了自己的類物件經過了初始化(實際上,在穿件類的時候,首先就會呼叫建構函式,完成初始化,即使沒定義初始化函式,系統會自定生成一個初始化函式)
2 對於15-22行的程式碼,實際上都運用了運算子的過載,+,-,*,<<都被過載,至少從這裡可以看出,運算子的過載,本質上,仍然是函式的呼叫。
3 18,22行的函式宣告為友元函式,可以發現,friend 為關鍵字。同時注意:17行,18行程式碼,構成了函式過載!!!因為他們使用了同一個函式名,雖然這其中使用了運算子過載。這兩個函式都描述了*運算,這也可以看出,過載函式,往往描述了同一種功能。
4 一個有趣的做法是:22行 的函式返回了一個ostream &,在之後的呼叫中,我們可以體會到這種妙用。
繼續看成員函式定義:
1 # include "mytime3.h" 2 3 Time::Time() 4 { 5 hours = minutes = 0; 6 } 7 8 Time::Time(int h, int m) 9 { 10 hours = h; 11 minutes = m; 12 } 13 14 void Time::Addmin(int m) 15 { 16 minutes = minutes + m; 17 hours = minutes / 60; 18 minutes = minutes % 60; 19 } 20 21 void Time::AddHr(int h) 22 { 23 hours = hours + h; 24 } 25 26 void Time::Reset(int h, int m) 27 { 28 hours = h; 29 minutes = m; 30 } 31 Time Time::operator+(const Time& t) const 32 { 33 Time sum; 34 sum.minutes = minutes + t.minutes; 35 sum.hours = hours + t.hours + sum.minutes / 60; 36 sum.minutes = sum.minutes % 60; 37 return sum;//思考這裡有引入Time物件的必要嗎 38 } 39 40 Time Time::operator-(const Time& t) const 41 { 42 Time diff; 43 int tot1,tot2; 44 tot1 = t.minutes + 60 * t.hours; 45 tot2 = minutes + 60 * hours; 46 diff.hours = (tot2 - tot1) / 60; 47 diff.minutes = (tot2 - tot1) % 60; 48 return diff; 49 } 50 51 Time Time::operator*(double mult) const 52 { 53 Time result; 54 long totalminutes = hours*mult * 60 + minutes*mult; 55 result.hours = totalminutes / 60; 56 result.minutes = totalminutes % 60; 57 return result; 58 } 59 60 std::ostream & operator<<(std::ostream& os, const Time & t) 61 { 62 os << t.hours << "hours," << t.minutes << "minutes"; 63 return os; 64 }
需要注意的是:51行的*並不是描述友元函式那個,而是另外一個。同時注意到63行,返回了一個類物件的引用,返回引用,本質上其實就是返回了傳遞到引用的引數!!!
我們注意到:62中,我們直接訪問了t.hour和t.minutes,這是因為我們定義的是友元函式,同時注意到,前面並沒有Time::類作用域的限定。這說明了友元函式並不是成員函式。注意:函式定義中並沒有使用friend關鍵字。
最後看實際上,呼叫了這個類的程式碼:
1 # include <iostream> 2 # include "mytime3.h" 3 4 int main() 5 { 6 using std::cout; 7 using std::endl; 8 Time aida(3, 35); 9 Time tosca(2, 48); 10 Time temp; 11 12 cout << "Aida and Tosca:" << endl; 13 cout << aida << ";" << tosca << endl; 14 temp = aida + tosca; 15 cout << "Aida + Tosca:" << temp << endl; 16 temp = aida*1.17; 17 cout << "Aida *1.17:" << temp << endl; 18 cout << "10.0*Tosca::" << 10.0*tosca << endl; 19 system("pause"); 20 return 0; 21 }
注意:第十行的 物件,被“隱“初始化
這裡尤其要注意的是16行的程式碼,和18行的區別,當執行16行的程式碼時,系統會呼叫第一個*函式,當執行18行的程式碼時,執行的卻是二個*函式。同時,我們注意:cout可以用來列印類物件,根本原因是std::ostream & operator<<(std::ostream& os, const Time & t)導致的,同時注意,該函式中返回了一個類物件引用,返回物件引用的目的是:返回cout本身,這就導致了比如定義了兩個物件:Time A ,Time B ,當執行 cout<<A<<B時不會出錯,因為(cout<<A)執行完畢返回了一個cout繼續作用於B。你可以嘗試刪除return 。但是要運用這個的前提是:你認識到運算子過載本質上,仍然是函式的呼叫!!!只有認識到這點,才會將cout<<A<<B看成((cout<<A)<<B)
但問題在於:真的一定要使用友元函式嗎,可以用合理的成員函式替代嗎???