過載運算子與友元函式
阿新 • • 發佈:2019-02-16
本次部落格 主要學習運算子過載和友元函式。其中
運算子過載能夠將平時用於內建型別的變數的操作符(如+,-,*,/等)用於類物件;
友元這種C++機制使得非成員函式可以訪問私有資料。
一、過載運算子
過載運算子,將普通運算子過載擴充套件到使用者自定義型別,編譯器根據具體環境來選用。(有點像過載函式—多型:函式名相同,但函式列表不同)這樣做使程式碼看起來更加自然,同時隱藏了內部機理,強調了OOP的實質。
過載運算子,必須需要使用運算子函式的特殊函式格式,如下
operatorop(argument-list)
其中,op必須是有效的C++運算子。
過載運算子是怎樣工作的?
理解過載運算子是怎樣工作的?假定Time類,併為它定義一個operator+()成員函式,以過載+運算子,以便能夠將兩個Time類物件相加。等式:
T = t1 + t2 ;
編譯器發現,運算元都是Time類物件,因此呼叫相應的運算子函式替換上述運算子:
T= t1. operator+( t2);
該函式隱式地使用了t1(因為它呼叫了方法),而顯式地使用t2物件(因為它被作為引數傳遞)。
因此,在運算子表示法中,運算子左側的物件(這裡為t1)是呼叫物件,運算子右邊的物件(這裡為t2)是作為引數被傳遞的物件。
擴充套件
當兩個以上的物件相加時,如何轉化為函式呼叫
T = t1 + t2 +t3;
由於+是從左向右的運算子,因此,
T= t1. operator+( t2+t3); //第一步
T= t1. operator+( t2. operator+(t3) );//第二步
二、友元函式的引入
在前面過載運算子的學習中,我們已經知道了,運算子左側的運算元是呼叫物件,運算子右側的運算元是引數。比如:
A = B * 2.75;
將被轉換為下面的成員函式呼叫:
A = B.operator*(2.75);
可是下面的語句如何辦呢?
A = 2.75 * B;
函式左側是操作物件,而2.75不能作為操作物件,因此編譯器不能使用成員函式呼叫來替換該表示式。
解決方案一:
告訴每個人只能用B*2.75這種格式寫。顯然這是一種對伺服器友好,對客戶需要當心的一種解決方案。
解決方案二:
對該過載運算子函式進行多型實現。
Time operator*(double m,const Time & t)
{
Return t * m; //use t.operator(m)
}
解決方案三:
引入友元函式的思想來解決。
1)首先將該函式修改非成員函式。
對於非成員過載運算子函式來說,運算子表示式左邊的運算元對應於運算子函式的第一個引數,運算子表示式右邊的運算元對應於運算子函式的第二個引數。
2)引入友元函式
因此,此時使用非成員函式按所需的順序獲得運算元,2.75作為函式第一個引數,B作為第二個引數。但引發一個新問題:普通非成員函式不能直接訪問類的私有成員,然而,有一類特殊的非成員函式可以訪問類的私有成員,它們被稱為友元函式。
格式:
friend Time operator*(double m,const Time & t)
該原型意味著:
- 雖然operator*()函式是在類宣告中宣告的,但它不是成員函式,因此不能使用成員運算子來呼叫。
- 雖然operator*()函式不是成員函式,但它與成員函式的訪問許可權相同。
總之,類的友元函式是非成員函式,其訪問許可權與成員函式相同。
注意,編寫函式定義時,不能使用Time::限定符,不能加關鍵字friend。
三、程式碼部分
類的宣告:
Time類提供了用於調整和重新設定時間、顯示時間、將兩個時間相加/減的方法; 建構函式; 友元函式的宣告。
//mytime.h
#ifndef MYTIME_H_
#define MYTIME_H_
#include <iostream>
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h,int m = 0); //建構函式
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0,int m = 0);
Time operator+(const Time & t) const;
Time operator-(const Time & t) const;
Time operator*(double n) const;
friend Time operator*(double m,const Time & t)
{ return t * m ;} //行內函數定義
friend std::ostream & operator<<(std::ostream &os, const Time & t);
};
#endif
類的定義
//mytime.cpp
#include "mytime.h"
Time::Time()
{
hours = minutes = 0;
}
Time::Time(int h,int m)
{
hours = h;
minutes = m;
}
void Time::AddMin(int m)
{
minutes += m;
hours += minutes / 60;
minutes %= 60;
}
void Time::AddHr(int h)
{
hours += h;
}
void Time::Reset(int h,int m)
{
hours = h;
minutes = m;
}
Time Time::operator+(const Time & t) const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes /60;
sum.minutes = sum.minutes % 60;
return sum;
}
Time Time::operator-(const Time & t) const
{
Time diff;
int tot1,tot2;
tot1 = t.minutes + t.hours * 60;
tot2 =minutes + hours * 60;
diff.hours = (tot2 -tot1) /60; //小時
diff.minutes = (tot2 -tot1) % 60;//分鐘
return diff;
}
Time Time::operator*(double mult) const
{
Time result;
long totalminutes = hours * mult * 60 +minutes *mult; //總分鐘
result.hours = totalminutes /60;
result.minutes = totalminutes % 60;
return result;
}
std::ostream & operator<<(std::ostream & os,const Time & t)
{
os << t.hours << " hours," << t.minutes <<" minutes";
return os; //物件本身返回還是物件,方便級聯使用
}
類的使用
//usetime.cpp
#include "mytime.h"
#include <iostream>
int main()
{
using std::cout;
using std::endl;
Time t1(3,35);
Time t2(2,48);
Time temp;
cout << "t1 and t2:\n";
cout << t1 <<"; " <<t2 <<endl;
temp = t1 + t2; //利用過載符 operator+()
cout << "t1+t2: "<<temp <<endl;
temp = t1 - t2; //利用過載符 operator-()
cout << "t1-t2: "<<temp <<endl;
temp = t1 * 1.17; //利用過載符 operator*() 順序
cout << "t1*1.17: "<<temp <<endl;
temp = 1.17 * t1; //利用過載符 operator*() 逆序
cout << "1.17*t1: "<<temp <<endl;
//利用過載符 operator<<()級聯
cout << "10.0*t1: " <<10.0*t1<<"10.0*t2: " <<10.0*t2<<endl;
return 0;
}