1. 程式人生 > >話運算子過載和友元函式

話運算子過載和友元函式

運算子過載的意義是:將常見的運算子重載出其他的含義:比如將*重載出指標的含義,將<<與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)

但問題在於:真的一定要使用友元函式嗎,可以用合理的成員函式替代嗎???