operator是C++的關鍵字,它和運算符一起使用,表示一個運算符函數,理解時應將operator=整體上視為一個函數名。
這是C+ +擴展運算符功能的方法,雖然樣子古怪,但也可以理解:一方面要使運算符的使用方法與其原來一致,另一方面擴展其功能只能通過函數的方式(c++中,“功能”都是由函數實現的)。
一、為什么使用操作符重載?
對於系統的所有操作符,一般情況下,只支持基本數據類型和標准庫中提供的class,對於用戶自己定義的class,如果想支持基本操作,比如比較大小,判斷是否相等,等等,則需要用戶自己來定義關於這個操作符的具體實現。
比如,判斷兩個人是否一樣大,我們默認的規則是按照其年齡來比較,所以,在設計person 這個class的時候,我們需要考慮操作符==,而且,根據剛才的分析,比較的依據應該是age。那么為什么叫重載呢?這是因為,在編譯器實現的時候,已經為我們提供了這個操作符的基本數據類型實現版本,但是現在他的操作數變成了用戶定義的數據類型class,所以,需要用戶自己來提供該參數版本的實現。
二、如何聲明一個重載的操作符?
A: 操作符重載實現為類成員函數
重載的操作符在類體中被聲明,聲明方式如同普通成員函數一樣,只不過他的名字包含關鍵字operator,以及緊跟其后的一個c++預定義的操作符。
可以用如下的方式來聲明一個預定義的==操作符:
class person{
private:
int age;
public:
person(int a){
this->age=a;
}
inline bool operator == (const person &ps) const;
};
// 實現方式
inline bool person::operator==(const person &ps) const
{
if (this->age==ps.age)
return true;
return false;
}
// 調用方式
#include
using namespace std;
int main()
{
person p1(10);
person p2(20);
if(p1==p2){
cout<<”the age is equal!”<<endl;
}
return 0;
}
這里,因為operator == 是class person的一個成員函數,所以對象p1,p2都可以調用該函數,上面的if語句中,相當於p1調用函數 ==,把p2作為該函數的一個參數傳遞給該函數,從而實現了兩個對象的比較。
B:操作符重載實現為非類成員函數(全局函數)
對於全局重載操作符,代表左操作數的參數必須被顯式指定。例如:
#include
#include
using namespace std;
class person
{
public:
int age;
public:
};
bool operator==(person const &p1 ,person const & p2)
//滿足要求,做操作數的類型被顯示指定
{
if(p1.age==p2.age)
return true;
return false;
}
int main()
{
person rose;
person jack;
rose.age=18;
jack.age=23;
if(rose==jack){
cout<<"ok"<<endl;
}
return 0;
}
C:如何決定把一個操作符重載為類成員函數還是全局名字空間的成員呢?
①如果一個重載操作符是類成員,那么只有當與他一起使用的左操作數是該類的對象時,該操作符才會被調用。如果該操作符的左操作數必須是其他的類型,則操作符必須被重載為全局名字空間的成員。
②C++要求賦值=,下標[],調用(), 和成員指向-> 操作符必須被定義為類成員操作符。任何把這些操作符定義為名字空間成員的定義都會被標記為編譯時刻錯誤。
③如果有一個操作數是類類型如string類的情形那么對於對稱操作符比如等於操作符最好定義為全局名字空間成員。
D:重載操作符具有以下限制:
(1) 只有C++預定義的操作符集中的操作符才可以被重載;

(2)對於內置類型的操作符,它的預定義不能被改變,應不能為內置類型重載操作符,如,不能改變int型的操作符+的含義;
(3) 也不能為內置的數據類型定義其它的操作符;
(4) 只能重載類類型或枚舉類型的操作符;
(5) 重載操作符不能改變它們的操作符優先級;
(6) 重載操作符不能改變操作數的個數;
(7) 除了對( )操作符外,對其他重載操作符提供缺省實參都是非法的;
E: 注意點:
(1)后果載操操作符首先要確定它的返回值是左值,還是右值,如果是左值最返回引用,如果是右值那就直接返回值;
(2) +號等這樣的操作符沒有對象可以容納改變后值,對於這樣的情況最好返回數值,否則只能要操作符體內創建臨時對象用於容納改變后的值,如果在堆中創建臨時對象返回指針或者引用,在操作符函數體外還需要釋放它,如果返回的對象而不是引用或者指針,那么效率是比較低的。如果返回的是數值,最好在該類的構造函數中增加對該類型數值的轉換函數,如:返回值是int類型,那么最好有一個int類型作為參數的構造函數。
(3)在增量運算符中,放上一個整數形參,就是后增量運行符,它是值返回,對於前增量沒有形參,而且是引用返回,示例:
class Test
{
public:
Test(x=3){ m_value = x}
Test &operator ++(); //前增量
Test &operator ++(int);//后增量
private:
Int m_value:
};
Test &Test::operator ++()
{
m_value ++; //先增量
return *this; //返回當前對象
}
Test Test::operator ++(int)
{
Test tmp(*this); //創建臨時對象
m_value ++; //再增量
return temp; //返回臨時對象
}
(4)因為強制轉換是針對基本數據類型的,所以對類類型的轉換需自定義;
(5) 轉換運行符重載聲明形式:operator 類型名();它沒有返回類型,因為類型名就代表了它的返回類型,所以返回類型顯得多余。
(6)一般來說,轉換運算符與轉換構造函數(即帶一個參數的構造函數)是互逆的,如有了構造函數Test(int),那么最好有一個轉換運算符int()。這樣就不必提供對象參數重載運算符了,如Test a1(1);Test a2(2); Test a3; a3 = a1+a2;就不需要重載+號操作符了,因為對於a1+a2的運算,系統可能會先找有沒有定義針對Test的+號操作符,如果沒有,它就會找有沒有針對Test類轉換函數參數類型的+號操作符(因為可以將+號運行結果的類型通過轉換函數轉換為Test對象),因為Test類有個int類型的參數,對於int類型有+操作符,所以a1+a2真正執行的是Test(int(a1) + int(a2));即Test(3);
(7)對於轉換運算符,還有一個需要注意的地方就是,如果A類中有以B為參數的轉換函數(構造函數),那B中不能有A的轉換運算符,不然就存在轉換的二義性,如:
class A{A(B&){…}}; class B{ operator A(){…}};
那么以下語句就會有問題:
B b; A(b);//A(b)有就可能是A的構造函數,也可以是B的轉換運算符
重載的意義
在面向對象編程時,常常會建立一個類,例如建立一個矩形類,想判斷其中兩個對象(我聲明的兩個矩形)相等,則必須有長相等、寬相等;如果要寫一個函數來進行比較,會不如我們常用的“==”運算符直觀簡單:
class rectangle{
private:
int length, width;
public:
rectangle(int l, int w){
length = l;
width = w;
}
bool IsSame(const rectangle&); //比較函數
bool operator==(const rectangle&); //重載"=="運算符
};
bool rectangle::IsSame(const rectangle& a){
if(length==a.length&&width==a.width){
return true;
}
else return false;
}
bool rectangle::operator==(const rectangle& a){
if(length==a.length&&width==a.width){
return true;
}
else return false;
}
int main(){
rectangle A(5,5);
rectangle B(5,5);
if(A.IsSame(B)){
cout<<"Same"<<endl;
}
if(A==B){ //符合語言習慣 更為直觀
cout<<"Same~"<<endl;
}
return 0;
}
所以,這個使得“==”運算符能被用戶定義的類使用的過程就是“重載”。
重載的要求
1.不能改變運算符的初始意義。
2.不能改變運算符的參數數目。如重載運算符+時只用一個操作數是錯誤的。
3.運算符函數不能包括缺省的參數。
4.絕大部分C++運算符都可以重載,以下的例外: . :: .* ?
5.除賦值運算符外,其它運算符函數都可以由派生類繼承。
6.運算符重載不改變運算符的優先級和結合性,也不改變運算符的語法結構,即單目、雙目運算符只能重載為單目、雙目運算符。
7.運算符的重載實際上是函數的重載。編譯程序對運算符重載的選擇,遵循函數重載的選擇原則。當遇到不很明顯的運算符時,編譯程序將去尋找參數匹配的運算符函數。
8.運算符重載可使程序更簡潔,使表達式更直觀,增強可讀性。但使用不宜過多。
9.重載運算符含義必須清楚
重載的形式
可以將操作符重載為 成員函數形式 和 友元函數形式。
(1) 一般情況下,單目運算符最好重載為類的成員函數;雙目運算符則最好重載為類的友元函數。
(2) 以下雙目運算符不能重載為類的友元函數:=、()、[]、->。
(3) 類型轉換函數只能定義為一個類的成員函數而不能定義為類的友元函數。
(4) 若一個運算符的操作需要修改對象的狀態,選擇重載為成員函數較好。
(5) 若運算符所需的操作數(尤其是第一個操作數)希望有隱式類型轉換,則只能選用友元函數。
(6) 當運算符函數是一個成員函數時,最左邊的操作數(或者只有最左邊的操作數)必須是運算符類的一 個類對象(或者是對該類對象的引用)。如果左邊的操作數必須是一個不同類的對象,或者是一個內部 類型的對象,該運算符函數必須作為一個友元函數來實現。
(7) 當需要重載運算符具有可交換性時,選擇重載為友元函數。
對此,我個人有一種自己的理解:
考慮操作符重載為哪種形式時,可以從該操作符的“使用者”層面上來思考,
比如常見的“=”、“+=”、“=”、“--”、“++”等,使用者都是“對象”,由“對象”來“使用”,所以定義為類的成員函數。(如上“==”的重載)
其他操作符如“+”、“-”、“”、“/”、“%”的“使用者”應該是其兩邊的內容,所以定義為友元函數,賦予其訪問私有成員的權利即可。
舉個栗子 Int 是一個模擬整形的類:
bool operator ==(const Int&a) {
if (i == a.i) return true;
else return false;
}
void operator +=(const Int&a) {
i += a.i;
}
/*類成員*/
/*友元函數*/
friend double operator*(const double a, const Int& e) {
double c;
c = a * e.i;
return c;
}
friend double operator*(const Int& e, const double a) {
double c;
c = e.i * a;
return c;
}
friend int operator*(const int a, const Int& e) {
int c;
c = a * e.i;
return c;
}
friend int operator*(const Int& e, const int a) {
int c;
c = e.i*a;
return c;
}
friend int operator*(const Int& e, const Int& a) {
int c;
c = e.i*a.i;
return c;
}
輸入輸出流重載
繼續以此段代碼為例,重載輸入輸出 ">>" "<<" ,詳見代碼:
#include<iostream>
using namespace std;
class rectangle{
private:
int length, width;
public:
rectangle(int l, int w){
length = l;
width = w;
}
friend istream &operator>>(istream &in, rectangle &a);//重載輸入流
friend ostream &operator<<(ostream &os, rectangle &a);//重載輸出流
};
istream &operator>>(istream &in, rectangle &a){
in >> a.length >> a.width;
return in;
}
ostream &operator<<(ostream &os, rectangle &a){
os << a.length << endl << a.width << endl;
return os;
}
int main(){
rectangle A(5,5);
rectangle B(5,5);
cin >> A;
cout << A;
cout << B;
return 0;
}
類型轉換運算符重載函數
這一點對於operator關鍵字的運用,除非查詢時就輸入這“生僻”的名稱:“類型轉換運算符重載函數“ 或者 ”類型轉換函數“,否則並不容易查找到相關的資料…
詳見 http://en.cppreference.com/w/cpp/language/cast_operator
簡單地說,即是在類的內部聲明
operator 類型名( )
{
實現轉換的語句
}
如代碼所示:
#include<iostream>
using namespace std;
class rectangle{
private:
int length, width;
public:
rectangle(int l, int w){
length = l;
width = w;
}
operator int() const{
return length*width;
}
};
istream &operator>>(istream &in, rectangle &a){
in >> a.length >> a.width;
return in;
}
ostream &operator<<(ostream &os, rectangle &a){
os << a.length << " " << a.width << endl;
return os;
}
int main(){
rectangle A(5,5);
rectangle B(5,5);
int area = A;
cout << area << endl;
int area2;
area2 = area + B;
cout << area2 << endl;
return 0;
}
operator重載的例子:
#include <iostream>
using namespace std;
class A
{
public:
A(double _data = 0.0):data(_data){}
A& operator = (const A& rhs)
{
data = rhs.data;
return *this;
}
friend A operator + (const A& lhs,const A& rhs);
friend A operator - (const A& lhs,const A& rhs);
friend A operator * (const A& lhs,const A& rhs);
friend A operator + (const A& lhs,double rhs);
friend A operator + (double lhs,const A& rhs);
friend A operator * (const A& lhs,double rhs);
friend A operator * (double lhs,const A& rhs);
friend A operator - (const A& lhs,double rhs);
friend A operator - (double lhs,const A& rhs);
friend ostream& operator << (ostream& fout,A& a);
// A& operator += (const A& rhs);
// A& operator -= (const A& rhs);
// A& operator *= (const A& rhs);
private:
double data;
};
A operator + (const A& lhs,const A& rhs)
{
A res(0);
res.data = lhs.data + rhs.data;
return res;
}
A operator - (const A& lhs,const A& rhs)
{
A res(0);
res.data = lhs.data - rhs.data;
return res;
}
A operator * (const A& lhs,const A& rhs)
{
A res(0);
res.data = lhs.data * rhs.data;
return res;
}
A operator + (const A& lhs,double rhs)
{
A res(0);
res.data = lhs.data + rhs;
return res;
}
A operator + (double lhs,const A& rhs)
{
A res(0);
res.data = lhs + rhs.data;
return res;
}
A operator * (const A& lhs,double rhs)
{
A res(0);
res.data = lhs.data * rhs;
return res;
}
A operator * (double lhs,const A& rhs)
{
A res(0);
res.data = lhs * rhs.data;
return res;
}
A operator - (const A& lhs,double rhs)
{
A res(0);
res.data = lhs.data - rhs;
return res;
}
A operator - (double lhs,const A& rhs)
{
A res(0);
res.data = lhs - rhs.data;
return res;
}
ostream& operator << (ostream& fout,A& a)
{
fout << a.data ;
return fout;
}
int main(int argc, char* argv[])
{
A a(2.3);
A b(1.2);
A d(3.4);
A c;
c = a + b + d;
c=a+b;
c=a+1.0;
c=a-b;
c=a-1.0;
c=a*b;
c=a*1.0;
cout << c << endl;
c=1.0+2.0*a*a-3.0*a*b;
cout << c << endl;
return 0;
}
輸出結果:

