#4:C++:運算子的過載;
運算子的過載
所謂過載,就是重新賦予新的定義,也就是一名多用;
除了函式之外,在 C++中 運算子也可以過載!
例:通過函式實現兩個複數的相加:
class Complex { public: Complex() { real = 0; imag = 0; } Complex(double r,double i) : real(r),imag(i) { } Complex Complex_Add(Complex &); void Display(); private: double real; double imag; }; Complex Complex :: Complex_Add(Complex &c2) { Complex temp; temp.real = this->real + c2.real; temp.imag = this->imag + c2.imag; return temp; } void Complex :: Display() { cout << "The complex is : " << real << " + " << imag << "i" << endl; } int main() { Complex c1(3,4),c2(1.2,-4),c3; c3 = c1.Complex_Add(c2); cout << "c1 = " ; c1.Display(); cout << "c2 = " ; c2.Display(); cout << "c1 + c2 = " ; c3.Display(); return 0; }
這種呼叫方式太過繁瑣,所以能不能用 + 運算子來實現兩個複數的相加呢?
運算子過載的方法:
運算子過載的方法是 : 定義一個過載運算子的函式;
在需要執行被過載的運算子時,系統就自動呼叫該函式,以完成運算;
運算子過載,實質上就是函式的過載!!
一般格式:
函式型別 operator 運算子名稱(形參列表)
{
對運算子的過載處理;
}
例:對 + 進行復數加法的運算子過載:
Complex operator + (Complex &c1, Complex &c2);
其中:
1. operator 是關鍵字,專門用於定義過載運算子函式的;
2. 運算子名稱就是 C++ 提供給使用者的預定義運算子;
注:
3. 函式名是由 operator和運算子組成: 即 operator + 為函式名 !!!
4. 兩個形參是 Complex 類物件的引用,要求兩個實參是 Complex 型別!!
在定義了過載運算子之後,即: 函式 operator+ 過載了運算子 + ;
此後,在執行 c1 + c2 的表示式的時候:
系統就會呼叫 operator+ 函式,把 c1,c2 作為實參,與形參進行虛
實結合;
例:
兩個整數相加 :
int operator+ (int a, int b)
{
return (a+b);
}
此時如果有表示式 5+8 ,就呼叫這個函式,將 5和8作為實參,函式的
返回值為13;
例:利用過載運算子實現兩個複數相加:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) { }
Complex operator+ (Complex &);
void Display();
private:
double real;
double imag;
};
Complex Complex :: operator+ (Complex &c2)
{
Complex temp;
temp.real = real + c2.real;
temp.imag = imag + c2.imag;
return temp;
}
void Complex :: Display()
{
cout << "The complex is : " << real << " + " << imag << "i" << endl;
}
int main()
{
Complex c1(3,4),c2(1.2,-4),c3;
c3 = c1 + c2;
cout << "c1 = " ; c1.Display();
cout << "c2 = " ; c2.Display();
cout << "c1 + c2 = " ; c3.Display();
return 0;
}
說明:
C++ 編譯系統將把表示式 c1 + c2 解釋為 :
c1.operator+(c2) // c1 和 c2 都是 Complex 類的物件;
即:
以 c2 為實參呼叫 c1 的運算過載函式 operator+(Complex &c2), 進行
求值;
過載函式的簡便寫法:
Complex Complex :: operator+ (Complex &c2)
{
return Complex(real + c2.real,imag + c2.imag);
}
其中的 Complex(xx,xx) 是建立一個臨時變數,是一個無名物件,在建
立時使用建構函式;
說明:
在運算子被過載之後,其原有的功能還是被保留,同時又增加了別的功能;
編譯系統通過對上下文( 運算子的兩側,單目運算子為一側 ) 資料型別進
行判斷。
C++ 提供的運算子過載使得使用者可以自己編寫對 物件進行的操作;
過載運算子的規則:
1. 只能對 C++ 中已有的運算子進行過載;
2. 五個不能過載的運算子:
. 成員訪問
.* 成員指標訪問
:: 域運算子
sizeof 長度運算子
?: 條件運算子
前兩個不能過載是為了保證訪問成員的功能不被改變;
域運算子和sizeof 運算子的運算物件是型別,而不是變數或一般表示式,不具備過載功能;
3. 過載不能改變運算子運算物件的個數:
即 雙目運算子需要兩個引數,單目運算子需要一個引數。。
4. 過載不能改變運算子的優先級別;
5. 過載不能改變運算子的結合性:
如:賦值運算子是右向左,過載後不變;
6. 過載運算子函式不能帶有預設引數!
7. 過載運算子必須和使用者自定義型別的物件一起使用:
其引數至少有一個類物件(或類物件的引用)!!!
即: 不能全部是 C++ 的標準型別!!
例:
int operator+ (int a, int b)
{
return (a-b);
}
此時在計算時系統 對於加減將會不明確!
如果有兩個引數,則既可以都是類物件,也可以一個是類物件,一個是C++
的標準型別:
例:
Complex operator+ (int a, Complex &c)
{
return Complex(a + c.real, c.imag);
}
即,一個整數和一個複數的加法運算;
8. 用於類物件的運算子一般需要過載,但是有兩個例外:
運算子 = 和 & 可以不用過載,
1): 賦值運算子 = :
可以用於每一個類物件,可以直接用於同類物件之間相互賦值。
由於系統已經為使用者的每一個新宣告的類過載了一個運算子,作用是逐個複製類的成員。
但是有的時候,預設的物件賦值運算子不能滿足程式的要求!!
例:資料成員中包括動態分配記憶體的指標成員時,在複製成員時可能會出現危險,此時要求使用者自己編寫。
2): 地址運算子 & :
返回類物件在記憶體中的起始地址;
9. 一般應當使過載運算子的功能類似於作用於標準型別資料時的功能,
防止可讀性差。
10. 運算子過載函式,可以是普通函式,類的成員函式或者類的友元函式;
運算子過載函式作為類成員函式和友元函式
對於 + 雙目運算子,為何在例子中只有一個引數:
實際上運算子過載函式有兩個引數,但是由於過載函式是 Complex 類中的成員函式;
有一個引數是隱含的,運算子函式是通過 this 指標來訪問的!!!
即:
過載函式訪問了兩個引數 : this->real , c2.real;
且
在將運算子函式過載為成員函式後,如果出現含該運算子的表示式,
c1 + c2 編譯系統將改為 : c1.operator+ (c2)
即通過物件 c1 呼叫運算子過載函式,並以表示式中的第二個引數( c2 ) 作為實參。
過載函式除了作為成員函式外,還可以作為非成員函式:
例:作為 Complex 的友元函式:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) {}
friend Complex operator + (Complex &c1, Complex &c2);
void Display();
private:
double real;
double imag;
};
Complex operator + (Complex &c1, Complex &c2)
{
return Complex(c1.real + c2.real,c1.imag + c2.imag);
}
void Complex :: Display()
{
cout << "The complex is : " << real << " + " << imag << "i" << endl;
}
int main()
{
Complex c1(3,4),c2(1.2,-4),c3;
c3 = c1 + c2;
cout << "c1 = " ; c1.Display();
cout << "c2 = " ; c2.Display();
cout << "c1 + c2 = " ; c3.Display();
return 0;
}
當運算子函式不作為成員函式的時候:
c++ 把程式中的表示式 c1 + c2 解釋為 : operator + (c1, c2);
即執行語句:
Complex operator + (Complex &c1,Complex &c2)
{
....;
}
過載函式作為 友元函式和成員函式 的比較:
1. 作為成員函式:
可以通過 this 指標自由的訪問資料成員,因此可以少寫一個函式的引數;
但必須要求:
運算表示式的第一個引數( 運算子左側的運算元 )是一個類物件!!
並且與運算子函式的型別相同;
且只有返回值型別與該物件同類型,運算結果才有意義。
如:
將一個複數和一個整數相加, c1 + i ,如果將其作為成員函式有:
Complex Complex :: operator + (int &i)
{
return Complex(real + i,imag);
}
注意:
在表示式中的過載運算子 + 左側必須為 Complex 型別的物件:
c3 = c2 + i; // 正確;
如果寫為了:
c3 = i + c2; // 錯誤,左側不是類物件 !!
2. 過載函式作為友元函式:
運算子左側作為 C++ 標準型別或者是非 Complex 型的物件;
此時運算子過載函式不能作為成員函式,只能作為非成員函式;
並且如果此時函式需要訪問類的私有成員,則必須宣告為該類的友元函式!!
例: 在 Complex 中 宣告過載運算子為該類的友元函式:
friend Complex operator + ( int &i, Complex &c );
同時在類外定義友元函式:
Complex operator + (int &i, Complex &c)
{
return Complex(i + c.real, c.imag);
}
注:
1. 將雙目運算子作為友元函式時,函式的形參表列中必須有兩個引數,不可省
略!!!
2. 形參的順序任意,不要求第一個物件為類物件!
3. 但是在使用運算表示式中:
要求運算子左側的運算元與函式的第一個引數對應;
運算子右側的運算元與函式的第二個引數相對應。
如:
c3 = i + c2; // 正確,型別匹配;
c3 = c2 + i; // 錯誤,型別不匹配!!!
即在此處數學上的交換律並不適用,需要再一次過載運算子 + :
Complex operator + ( Complex &c ,int &i )
{
return Complex(i + c.real,c.imag);
}
此後,i + c2 與 c2 + i 都合法;
注: 不可把兩個函式都作為成員函式;
3. C++ 規定,有的運算子必須定義為類的成員函式:
賦值運算子,下標運算子,函式呼叫運算子;
有的運算子不能定義為類的成員函式:
流插入 << , 流提取 >> , 型別轉換運算子;
所以一般將:
單目運算子過載為成員函式,雙目運算子過載為友元函式;
過載雙目運算子:
雙目運算子有兩個運算元( 通常在操作符兩側 ),所以在過載函式中有兩個引數:
例:
定義一個字串類 string,用來存放不定長的字串;
並且過載運算子 == ,> , <, 用於兩個字串的等於,大於,小於的比較運算。
class String
{
public:
String()
{
p = NULL;
}
String(char *str)
{
p = str;
}
void Display();
void Length();
friend bool operator > (String &str1, String &str2);
friend bool operator < (String &str1, String &str2);
friend bool operator == (String &str1, String &str2);
private:
char *p;
int length;
};
void String :: Display()
{
cout << p;
}
void String :: Length()
{
for(length = 0; *p != NULL; p++,length++)
{
;
}
cout << "The length of the string is : " << length << endl;
}
bool operator > (String &str1, String &str2)
{
if(strcmp(str1.p , str2.p) > 0)
{
return true;
}
else
{
return false;
}
}
bool operator < (String &str1, String &str2)
{
if(strcmp(str1.p , str2.p) < 0)
{
return true;
}
else
{
return false;
}
}
bool operator == (String &str1, String &str2)
{
if(strcmp( str1.p , str2.p ) == 0)
{
return true;
}
else
{
return false;
}
}
void Compare(String &str1, String &str2) // 增加一個compare 函式,減輕main函式的負擔;
{
if( ( operator > (str1,str2) ) == 1 )
{
str1.Display();
cout << '>';
str2.Display();
}
else if( ( operator < (str1,str2) ) == 1 )
{
str1.Display();
cout << '<';
str2.Display();
}
else if( ( operator == (str1,str2) ) == 1 )
{
str1.Display();
cout << '=';
str2.Display();
}
}
int main()
{
String str1("BOOK!"),str2("BOOK!");
Compare(str1,str2);
return 0;
}
過載單目運算子:
單目運算子只有一個運算元,與雙目運算子的過載類似:
只不過過載函式只有一個引數,且如果作為成員函式,還可以省略!
例:
自加 ++ 運算子的過載: 時鐘的自加;
class Time
{
public:
Time()
{
minute = 0;
sec = 0;
}
Time(int m,int s) : minute(m),sec(s) {}
Time operator ++ ();
void Display();
private:
int minute;
int sec;
};
Time Time :: operator ++ ()
{
(this->sec)++;
if(sec >= 60)
{
sec = 0;
minute++;
}
return *this; // 返回的是一個指向正在操作的這個 Time型別的物件的指標!
}
void Time :: Display()
{
cout << this->minute << " : " << this->sec << endl;
}
int main()
{
Time time1(34,0);
for(int i = 0; i < 61; i++)
{
++time1; // 注: 此時必須寫為 ++XXX, 即寫在自加運算子後(右結合性)!!
time1.Display();
}
return 0;
}
例: 前置自加運算子 和 後置自加運算子:
class Time
{
public:
Time()
{
minute = 0;
sec = 0;
}
Time(int m,int s) : minute(m),sec(s) {}
Time operator ++();
Time operator ++(int);
void Display();
private:
int minute;
int sec;
};
Time Time :: operator ++ () // 前置自增運算子 ++time : 先自加再返回!
{
if( ++sec >= 60 ) // 此處不可寫為 sec ++ !!!!;
{
sec -= 60;
minute++;
}
return *this;
}
Time Time :: operator ++ (int) // 後置自增運算子 time++ : 返回的是自加前的物件!!
{
Time temp(* this); // 把之前的 this 內容儲存起來;
sec++;
if(sec >= 60)
{
sec -= 60;
++minute;
}
return temp; // 返回的是自加前的物件!
}
void Time :: Display()
{
cout << this->minute << " : " << this->sec << endl;
}
int main()
{
Time time1(34,59),time2;
cout << "time1 : ";
time1.Display();
++time1;
cout << "time1 : ";
time1.Display();
time2 = time1++; // 把自加前的 time1 賦值給 time2 ,然後 time1 自加;
cout << "time1 : ";
time1.Display();
cout << "time2 : ";
time2.Display();
return 0;
}
注:
1. 前置自增運算子 和 後置自增運算子的區別:
1) : 前置自增 ++time :
先自加,返回的是自加後的值;
2) : 後置自增 time++ :
先返回原值,之後再自加一!!
2. 如果不區分前置和後置,則使用operator++( )或operator--( )即可;
否則:要使用operator++( )或operator--( )來過載前置運算子!!
使用operator++(int) 或 operator--(int)來過載後置運算子,呼叫時,引數int被傳遞給值0。
過載流插入和流提取運算子:
C++ 中的流插入 << 和 流提取 >> 功能是在類庫中提供的,即 istream 和 ostream;
在類庫的標頭檔案中已經包括了輸出 標準型別的資料,但是對於使用者自定義的類,需要自行定義:
過載的形式函式:
istream & operator >> ( istream & , 自定義類 & );
ostream & operator << ( ostream & , 自定義類 & );
即:
過載運算子 >> 函式的第一個函式和函式的型別都必須是 istream & 型別;
第二個引數是要進行輸出操作的類。
所以!!! 只能把 << >> 的過載函式作為友元函式或者普通的函式,而不可作為成員函式!
1. 過載流插入運算子 << :
用插入運算子 << 來輸出使用者自己的類物件的資訊。
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) {}
Complex operator + (Complex &c2);
friend ostream& operator << ( ostream & , Complex & );
private:
double real;
double imag;
};
Complex Complex :: operator + (Complex &c2) // 過載 + 運算子!
{
return Complex(real + c2.real, imag + c2.imag);
}
ostream& operator << (ostream &output, Complex &c2) // 過載流插入 << 運算子
{
output << "(" << c2.real << " + " << c2.imag << "i)" << endl;
return output;
}
int main()
{
Complex c1(2,4),c2(6,10),c3;
c3 = c1 + c2;
cout << c3;
return 0;
}
說明:
1. 運算子過載中的 形參 output 是 ostream類物件的引用名, 是使用者可以任意取
名的!
2. 編譯系統把 cout << c3 解釋為 :
operator << ( cout , c3 ); 所以呼叫使用者自定義的過載函式!
3. return output 就是將輸出流 cout 的現狀返回,即保留輸出流的現狀:
例:
cout << c2 << c3;
則先處理 (cout << c2) << c3;
此後相當於 cout(新值) << c3; 此時 << 左側依舊是 ostream型別,右側還是
Complex 型別,還可繼續呼叫;
所以 C++ 規定 << 過載時的第一個引數和函式的型別必須都是 ostream 型別的引
用:
就是為了返回 cout 的當前值以便於連續輸出!
2. 過載流提取運算子 >> :
c++ 預定義的 >> 的作用是從一個輸入流中提取資料;
而過載的目的是為了提取使用者需要的自定義型別的物件的資訊;
例:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i){ }
friend ostream & operator << ( ostream & , Complex & );
friend istream & operator >> ( istream & , Complex & );
private:
double real;
double imag;
};
ostream& operator << (ostream &output, Complex &c)
{
output << "(" << c.real << " + " << c.imag << "i)";
return output;
}
istream& operator >> (istream& input, Complex &c)
{
cout << "Input the part & the imaginary part of the complex number :";
input >> c.real >> c.imag;
return input;
}
int main()
{
Complex c1,c2;
cin >> c1 >> c2;
cout << " c1 = " << c1 << endl;
cout << " c2 = " << c2 << endl;
return 0;
}
說明:
1. 在執行 cin >> c1時,同樣的呼叫, operator >> 函式,將 cin 和 c1 傳遞給形
參;
2. 函式返回新的 cin 值,即用 cin 可以連續的從輸入流提取資料給 Complex 對
象!
注:3. 每遇到一個 >> 就呼叫一次函式!
例改:
當出現負數時,將出現 3 + -10i 的錯誤;需要改:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i){ }
friend ostream & operator << ( ostream & , Complex & );
friend istream & operator >> ( istream & , Complex & );
private:
double real;
double imag;
};
ostream& operator << (ostream &output, Complex &c)
{
output << "(" << c.real;
if(c.imag >= 0)
{
output << " + ";
}
output << c.imag << "i)" << endl;
return output;
}
istream& operator >> (istream& input, Complex &c)
{
cout << "Input the part & the imaginary part of the complex number :";
input >> c.real >> c.imag;
return input;
}
int main()
{
Complex c1,c2;
cin >> c1 >> c2;
cout << " c1 = " << c1 << endl;
cout << " c2 = " << c2 << endl;
return 0;
}
注:
利用引用型別作為函式的形參可以在呼叫函式的時候:
1. 不是用值傳遞的方式進行虛實結合,而是通過傳地址的方式使形參成為別名;
因此不生成臨時變數,減少了時間和空間上的開銷!
2. 過載函式的引用物件如果是物件的引用時,返回的不是常量,而是引用所代表的
物件;
所以可以出現在賦值號的左側,成為左值,可以被賦值或者進行其他操作 (連續
輸入,輸出等)
不同資料型別間的轉換:
1. 標準型別資料間的轉換:
1. 隱式型別轉換:
C++ 根據運算式中的資料型別自動完成;
2. 顯式型別轉換:
資料型別名(資料) ;
例: int(89.4); 即把 89.4 轉換為 89;
注: 在 C語言中為: (型別名)資料 ;
對於使用者自己宣告的類,需要定義專門的函式來處理資料型別轉變!
1. 轉換建構函式:
將一個其他型別的資料轉化為一個指定的類的物件。
原來學過的: 1) : 初始化建構函式;Complex(double r,double i);
2) : 複製建構函式: Complex(Complex &c)
說明:
1. 轉換建構函式只有一個引數:
例:
Complex(double r)
{
real = r;
imag = 0;
}
作用為 : 將 double 型別的引數 r 轉化為 Complex 類的物件;(即 r為實部,虛部
為零);
2. 在類體中,可以有轉換建構函式,也可以沒有轉換建構函式!!
3. 以上學過的幾類建構函式可以同時出現在同一個類體之中,他們都是建構函式的
過載。
( 但是形式不同,編譯系統會自動根據建立物件時所給實參來匹配! )
例:
構造了轉換建構函式後:
1. Complex c1(3.5); // 建立了一個將 double 3.5 轉化為 3.5 + 0i的虛數;
2. Complex c(3.6); // 建立了一個無名物件;
3. Complex c1(3.6);
c = c1 + 2.5; // 若未定義一個 Complex 和 一個 double 相加的過載則錯誤;
c = c1 + Complex(2.5); // 若定義了兩個複數相加,正確!
4. 轉換建構函式也是一種建構函式,一般都作為型別轉換。
若不作為型別轉換:
Complex(double r)
{
cout << r;
}
理論上是可行的,但是不具有實際意義!
注:5. 轉換建構函式只能有一個引數!!!!
2. 型別轉換函式:
利用構造轉換函式可以將一個指定型別的資料轉化為類的物件,但是不能反過來;
所以只能通過 c++ 提供的型別轉換函式來解決:
即、將一個類的物件轉換成另一型別的函式。
例:
在 Complex 類中宣告:
operator double()
{
return real; // 函式返回虛數的實部;
}
注: 函式名為 operator double 。
一般形式為: operator 型別名() { ... }
1. 在函式名前不能指定函式型別,函式也沒有引數!!
2. 返回值的型別是由函式名指定的型別名決定的;
3. 型別轉換函式必須作為成員函式,因為轉換的本體是本類的物件;不可作為普通或友元函式;
說明:
1. double 型別經過過載之後,除了原有的含義之外,還獲得了新的含義( 把Complex 轉化為 double型 )
對於編譯系統,不僅能識別原有的 double 型別,還會把 Complex 類物件作為 double 型別處理!!
即: Complex 相當於具有了雙重國籍,在不同的場合,以不同的面貌出現;
2. 轉換建構函式 和 型別轉換函式有一個共同的功能:
當需要的時候,編譯系統可以自動呼叫這些函式,自動建立臨時變數!
例:
若定義了 double型 : d1,d2 ; Complex 型: c1,c2;
則對於程式中的表示式:
d1 = d2 + c1;
編譯系統發現 + 左端為 double ,右側為 Complex ;
1): 首先:檢查有無對 + 的運算子過載;
2): 若沒有:檢查有無型別轉換函式:
發現 double 的過載函式後,呼叫該函式把 Complex 轉化為 double 型別(建立一
個 double temp),
在和 c2 相加之和,把一個 double 型別的資料賦值給 d1;
對於表示式( 若定義了轉換建構函式和 + 的過載,但是沒有定義 double 的型別轉換函式 ):
c2 = c1 + d2;
1):首先:檢查發現 有 operator+ 函式,但是是 Complex 的友元函式( 要求兩個 Complex )
在類中沒有對 double 過載,所以不能把 c1 轉化為 double 然後相加;
此時: 呼叫轉換建構函式:Complex(&d2) ,產生temp 的 Complex 物件;
在呼叫 operator+ 函式,將兩個複數相加 再賦值給 c2;
即: c2 = c1 + Complex(d2)
例:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i){}
Complex(double r) // 定義轉換建構函式,double 轉化為 Complex;
{
real = r;
imag = 0;
}
operator double() // 定義型別轉換函式,Complex 轉化為 double;
{
return real;
}
private:
double real;
double imag;
};
int main()
{
Complex c1(3,4),c2(5,-10),c3;
double d;
d = 2.5 + c1; // double + Complex;
cout << d << endl;
return 0;
}
說明:
1. 由於已經定義了成員函式 operator double,就可以利用它將Complex物件轉
化為double型別;
注: 程式中不必顯示的呼叫型別函式,是自動被呼叫的(隱式呼叫);
即編譯系統在處理表達式 2.5 + c1 的時候,發現運算子 + 左側為 double 型別,
右側為 Complex 型別,
而又無運算子 + 過載函式,不能直接相加;
但是有對 double 的過載函式,因此呼叫該函式,並返回double;
2. 如果改為:
d = c1 + c2; // d 為 double 型別;
將 c1 和 c2 兩個類物件相加,得到一個臨時的 Complex 物件,而又有對double型別的過載函式,
於是呼叫此函式,把臨時類物件轉換為 double 資料,然後複製給 d;
使用型別轉換函式的好處:
例: 程式需要對一個 double 和 Complex類物件進行運算,如果不用型別轉換
函式, 就要對多種運算子進行過載,以便能進行各種運算!
但是如果對 double 型別進行過載(使 Complex 類物件轉化為 double 型別
數據),就不必對各種運算子進行過載了;
例:包含轉換建構函式、運算子重構函式和型別轉換函式的程式:
1. 只包含轉換建構函式和運算子重構函式:
class Complex
{
public:
Complex() // 預設建構函式;
{
real = 0;
imag = 0;
}
Complex(double r) // 轉換建構函式;
{
real = r;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i) {} // 初始化建構函式;
friend Complex operator + (Complex c1, Complex c2); // 過載運算子 + 的友元函式;
void Display();
private:
double real;
double imag;
};
Complex operator + (Complex c1, Complex c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
void Complex :: Display()
{
cout << "(" << real << " + " << imag << "i)" << endl;
}
int main()
{
Complex c1(3,4),c2(5,-10),c3;
c3 = c1 + 2.5;
c3.Display();
return 0;
}
在 Complex 中定義了轉換建構函式,並規定了怎樣構成一個複數,所以在處理 c1 +
2.5時:
編譯系統解釋為: operator + ( c1, 2.5 );
即:
operator + (c1, Complex(2.5)) 把 2.5 轉化為臨時變數;
注: 如果改為 2.5 + c1 程式也是可以執行的!!!!
所以在已經定義了轉換建構函式的情況下,將運算子 + 過載為友元函式,在兩個複數相加時,可用交換律;
注: 如果運算子 + 過載函式不作為 Complex 類的友元函式,而作為 Complex 類
的成員函式:
則不滿足交換律:
對於表示式 2.5 + c1 ,
c++編譯系統把它解釋為 : (2.5).operator + (c1) 錯誤!
所以:
運算子函式過載為成員函式,他的第一個引數必須是本類的物件!!
當第一個運算元不是類物件時,不能將運算子函式過載為成員函式 !
否則交換律不適用!
即: 一般情況下,將:
雙目運算子函式過載為友元函式。而單目運算子則過載為成員函式;
3. 如果一定要將運算子函式過載為成員函式,而第一個運算元又不是本類物件:
只能再過載一個運算子 + 函式,其第一個引數為 double 型別,且只能是友元函式。
函式原型: friend operator + (double , Complex &)
例:
2. 增加型別轉換函式:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r)
{
real = r;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i) {}
operator double() // 型別轉換函式;
{
return real;
}
friend Complex operator + (Complex c1, Complex c2);
void Display();
private:
double real;
double imag;
};
Complex operator + (Complex c1, Complex c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
void Complex :: Display()
{
cout << "(" << real << " + " << imag << "i)" << endl;
}
int main()
{
Complex c1(3,4),c2(5,-10),c3;
c3 = c1 + 2.5;
c3.Display();
return 0;
}
此時程式在編譯時出現錯誤,因為出現了二義性:
1. 呼叫建構函式把 2.5 變為 Complex 類物件,然後呼叫運算子 + 相加;
2. 呼叫型別轉換函式,把 c1 轉換為 double 型別,然後與 2.5 相加;
如果要使用型別在轉換函式,就必須要刪除運算子 + 過載函式;