運算符重載:
要重載運算符, 需要使用被稱為運算符函數的特殊函數形式, 運算符函數的格式如下:
operatorop(argument list)
例如:operator+()重載+運算符, operator*()重載*運算符, op必須是有效的C++運算符.
例如, 假設有一個Sales類, 并為他定義了一個operator+()成員函數, 以重載+運算符, 以便能夠将兩個Sales對象的銷售額相加, 則如果district2, sid和sara都是Sales類對象, 便可以寫這種等式:
district2 = sid + sara;
編譯器發現, 操作數是Sales類對象, 是以使用相應的運算符函數替換上述運算符:
district2 = sid.operator+(sara);
然後該函數将隐式的使用sid(因為他調用了方法), 而顯示的使用sara對象(因為他被作為參數傳遞), 來計算總和, 并傳回這個值.
來看一個重載運算符的例子:
先看不添加重載運算符函數的代碼:
// 第一個檔案
// mytime0.h
#ifndef MYTIME0_H_
#define MYTIME0_H_
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);
// 最後一個const表明函數不會隐式修改成員變量
Time Sum(const Time & t) const;
void Show() const;
};
#endif
第二個檔案
// 第二個檔案
// mytime0.cpp, Time類的函數的實作檔案
#include <iostream>
#include "mytime0.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::Sum(const Time & t) const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes = sum.minutes % 60;
// 注意這裡, 由于傳回類型是Time, 是以可以直接傳回sum
// 如果傳回類型是Time & , 則不能傳回sum
// 因為sum是臨時變量, 函數執行完畢後就會釋放, 是以不能傳回sum的引用
return sum;
}
void Time::Show() const
{
std::cout << hours << " hours, " << minutes << " minutes" << std::endl;
}
第三個檔案:
// 第三個檔案:
// usetime0.cpp
// compile with mytime0.cpp
#include <iostream>
#include "mytime0.h"
int main()
{
using std::cout;
using std::endl;
// 調用沒有參數的構造函數
Time planning;
// 調用兩個參數的構造函數
Time coding(2, 40);
Time fixing(5, 55);
Time total;
cout << "planning time = ";
planning.Show();
cout << endl;
cout << "Coding time = ";
coding.Show();
cout << endl;
cout << "fixing time = ";
fixing.Show();
cout << endl;
total = coding.Sum(fixing);
cout << "coding.Sum(fixing) = ";
total.Show();
cout << endl;
return 0;
}
程式運作結果為:

對Time類添加重載+操作符的代碼:
// 第一個檔案
// mytime1.h
#ifndef MYTIME0_H_
#define MYTIME0_H_
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);
// 最後一個const表明函數不會隐式修改成員變量
Time Sum(const Time & t) const;
// 重載+運算符
Time operator+(const Time & t) const;
void Show() const;
};
#endif
第二個檔案
// mytime1.cpp, Time類的函數的實作檔案
#include <iostream>
#include "mytime1.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::Sum(const Time & t) const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes = sum.minutes % 60;
// 注意這裡, 由于傳回類型是Time, 是以可以直接傳回sum
// 如果傳回類型是Time & , 則不能傳回sum
// 因為sum是臨時變量, 函數執行完畢後就會釋放, 是以不能傳回sum的引用
return sum;
}
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;
// 注意這裡, 由于傳回類型是Time, 是以可以直接傳回sum
// 如果傳回類型是Time & , 則不能傳回sum
// 因為sum是臨時變量, 函數執行完畢後就會釋放, 是以不能傳回sum的引用
return sum;
}
void Time::Show() const
{
std::cout << hours << " hours, " << minutes << " minutes" << std::endl;
}
第三個檔案
// 第三個檔案:
// usetime0.cpp
// compile with mytime1.cpp
#include <iostream>
#include "mytime1.h"
int main()
{
using std::cout;
using std::endl;
// 調用沒有參數的構造函數
Time planning;
// 調用兩個參數的構造函數
Time coding(2, 40);
Time fixing(5, 55);
Time total;
cout << "planning time = ";
planning.Show();
cout << endl;
cout << "Coding time = ";
coding.Show();
cout << endl;
cout << "fixing time = ";
fixing.Show();
cout << endl;
total = coding.Sum(fixing);
cout << "coding.Sum(fixing) = ";
total.Show();
cout << endl;
total = coding + fixing;
cout << "coding + fixing = ";
total.Show();
cout << endl;
Time morefixing(3, 28);
cout << "more fixing time = ";
morefixing.Show();
cout << endl;
total = morefixing.operator+(total);
cout << " morefixing.operator+(total) = ";
total.Show();
cout << endl;
return 0;
}
程式運作結果:
C++運算符重載的限制:
1.重載後的運算符必須至少有一個操作數是使用者定義的類型, 這能防止使用者重載标準類型運算符.
2.使用運算符時不能違反運算符原來的文法規則. 例如:不能将球磨運算符(%)重載成為一個操作數:
int x;
Time t1;
// 這個不符合文法
% x;
// 非法的重載
% t1;
3.不能修改運算符的優先級.
4.不能建立新的運算符.
5.不能重載下面這些運算符:
sizeof
. 成員運算符
.* 成員指針運算符
:: 作用域解析運算符
?: 條件運算符
typeid 一個RTTI運算符
const_cast, dynamic_cast, reinterpret_cast, static_cast 強制類型轉換運算符
6.大多數運算符都能通過成員或非成員函數進行重載, 但下面的運算符隻能通過成員函數進行重載:
=, (), [], ->
友元:
C++提供了另一種形式的通路權限: 友元, 友元有三種:
友元函數
友元類
友元成員函數
通過讓函數稱為類的友元, 可以賦予該函數與類的成員函數相同的通路權限.
在前文的Time類示例中, 重載的乘法運算符與其他兩種重載運算符(+, -)的差别在于, 它使用了兩種不同的類型, 也就是說, 加法和減法運算符都結合兩個Time值, 而乘法運算符将一個Time值和一個double值結合在一起, 這限制了該運算符的使用方式. 例如:
A = B * 2.3;
将被轉換為:
A = B.operator*(2.3);
但下面的語句就不行了:
A = 2.3 * B;
這條語句就不能正确執行了
從概念上說, 2.3*B應和B*2.3是一樣的, 但第一個表達式不對應于成員函數, 因為2.3不是Time類型對象.
C++提供了一種解決辦法: 非成員函數(大多數運算符都可以通過成員或非成員函數來重載). 非成員函數不是由對象調用的, 它使用所有的值(包括對象)都是顯示參數. 這樣, 編譯器能夠将下面的表達式:
A = 2.3 * B;
與下面的非成員函數調用比對:
A = operator*(2.3, B);
該函數的原型如下:
Time operator*(double m, const Time & t);
建立友元:
建立友元函數第一步是将其原型放在類聲明中, 并在原型聲明前加上關鍵字friend:
// 需要放在類聲明中
friend Time operator*(double m, const Time & t);
該原型意味着:
1.雖然operator*()函數時在類聲明中聲明的, 但它不是成員函數, 是以不能使用成員運算符(.)來調用;
2.雖然operator*()函數不是成員函數, 但它與成員函數的通路權限相同.
第二步是編寫函數定義, 因為不是成員函數, 是以不要使用Time::限定符, 并且不要在定義中使用關鍵字friend:
// 注意不要用Time::限定符, 并且不要使用frined
Time operator*(double mult, const Time & t)
{
Time result;
long totalminutes = t.hours * mult * 60 + t.minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
有了上述聲明和定義後:
A = 2.3 * B;
将被轉換為下面語句, 進而調用剛才定義的非成員友元函數:
A = operator*(2.3, B);
重載<<運算符
還是上面的Time類, 使用友元函數, 重載<<運算符
void operator<<(ostream & os, const Time & t)
{
// 由于直接通路了Time類的私有變量hours和minutes, 是以必須是Time的友元函數
os << t.hours << " hours, " << t.minutes << " minutes";
}
這樣就可以使用下面的語句:
cout << time1;
但這種實作不允許像通常那樣将重新定義的<<運算符與cout一起使用
cout << "Trip time: " << trip << " (Tuesday) \n";
要了解原因, 先看一個關于cout操作的知識點:
int x = 3;
int y = 6;
cout << x << y;
C++從左至右讀取輸出語句, 意味着它等同于:
(cout << x) << y;
而ostream類将operator<<()函數實作為傳回一個指向ostream對象的引用.
是以可以對友元函數采用相同的方法, 隻要修改operator<<()函數, 讓它傳回ostream對象的引用即可:
ostream & operator<<(ostream & os, const Time & t)
{
os << t.hours << " hours, " << t.minutes << " minutes";
return os;
}
我們重新看
cout << "Trip time: " << trip << " (Tuesday) \n";
這個語句的工作過程:
首先,
cout << "Trip time: "
這個将調用的是ostream中的<<定義, 它顯示字元串并傳回cout對象
cout << trip << " (Tuesday) \n";
将調用Time聲明顯示trip值, 并再次傳回cout對象,
cout << " (Tuesday) \n";
又再次調用ostream的<<定義, 來顯示最後一個字元串.
改進之後的版本:
第一個檔案
// 第一個檔案
// mytime3.h
#ifndef MYTIME3_H_
#define MYTIME3_H_
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;
// 友元函數
// 直接寫了方法的定義, 則實際是個内聯函數
// 方法體裡實際調用的是operator*(double n) 這個成員函數
friend Time operator*(double m, const Time & t)
{return t * m;}
// 友元函數重載了<< 運算符
friend std::ostream & operator<<(std::ostream & os, const Time & t);
};
#endif
第二個檔案:
// 第二個檔案
// mytime3.cpp, Time類的函數的實作檔案
#include <iostream>
#include "mytime3.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;
// 注意這裡, 由于傳回類型是Time, 是以可以直接傳回sum
// 如果傳回類型是Time & , 則不能傳回sum
// 因為sum是臨時變量, 函數執行完畢後就會釋放, 是以不能傳回sum的引用
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.minutes = (tot2 - tot1) % 60;
diff.hours = (tot2 - tot1) / 60;
return diff;
}
Time Time::operator*(double mult) const
{
Time result;
long totalminutes = hours * mult * 60 + minutes * mult;
result.minutes = totalminutes % 60;
result.hours = totalminutes / 60;
return result;
}
// 這個不是成員函數不需要Time::
// 友元函數的實作, 不需要寫friend
std::ostream & operator<<(std::ostream & os, const Time & t)
{
os << t.hours << " hours, " << t.minutes << " minutes";
return os;
}
第三個檔案:
// 第三個檔案:
// usetime0.cpp
// compile with mytime3.cpp
#include <iostream>
#include "mytime3.h"
int main()
{
using std::cout;
using std::endl;
// 調用兩個參數的構造函數
Time coding(2, 40);
Time fixing(5, 55);
Time temp;
cout << "coding and fixing : " << endl;
// 使用的是友元非成員函數 重載<<
cout << coding << "; " << fixing << endl;
// 使用的是 operator+()
temp = coding + fixing;
// 使用的友元非成員函數 重載<<
cout << "coding + fixing : " << temp << endl;
// 使用的是 operator*()
temp = coding * 1.17;
cout << "coding * 1.17 : " << temp << endl;
// 使用的是友元非成員函數 重載<<
// 還使用了友元非成員函數operator*
cout << "10.0 * fixing : " << 10.0 * fixing << endl;
return 0;
}
程式運作結果為: