標籤:add 作用 range 個數 string 算術運算子 調用 counter 編寫程式
C++中預定義的運算子的操作對象只能是基礎資料型別 (Elementary Data Type),實際上,對於很多使用者自訂類型,也需要有類似的運算操作。例如:
class complex
{
public:
complex(double r=0.0,double I=0.0){real=r;imag=I;}
void display();
private:
double real;
double imag;
};
complex a(10,20),b(5,8);
“a+b”運算如何??這時候我們需要自己編寫程式來說明“+”在作用於complex類對象時,該實現什麼樣的功能,這就是運算子多載。運算子多載是對已有的運算子賦予多重含義,使同一個運算子作用於不同類型的資料導致不同類型的行為。
運算子多載的實質是函數重載。在實現過程中,首先把指定的運算運算式轉化為對運算子函數的調用,運算對象轉化為運算子函數的實參,然後根據實參的類型來確定需要調用達標函數,這個過程愛編譯過程中完成。
一、 運算子多載的規則
運算子多載規則如下:
①、 C++中的運算子除了少數幾個之外,全部可以重載,而且只能重載C++中已有的運算子。
②、 重載之後運算子的優先順序和結合性都不會改變。
③、 運算子多載是針對新類型資料的實際需要,對原有運算子進行適當的改造。一般來說,重載的功能應當與原有功能相類似,不能改變原運算子的操作對象個數,同時至少要有一個操作對象是自訂類型。
不能重載的運算子只有五個,它們是:成員運算子“.”、指標運算子“*”、範圍運算子“::”、“sizeof”、條件運算子“?:”。
運算子多載形式有兩種,重載為類的成員函數和重載為類的友元函數。
運算子多載為類的成員函數的一般文法形式為:
函數類型 operator 運算子(形參表)
{
函數體;
}
運算子多載為類的友元函數的一般文法形式為:
friend 函數類型 operator 運算子(形參表)
{
函數體;
}
其中,函數類型就是運算結果類型;operator是定義運算子多載函數的關鍵字;運算子是重載的運算子名稱。
當運算子多載為類的成員函數時,函數的參數個數比原來的操作個數要少一個;當重載為類的友元函數時,參數個數與原運算元個數相同。原因是重載為類的成員函數時,如果某個對象使用重載了的成員函數,自身的資料可以直接存取,就不需要再放在參數表中進行傳遞,少了的運算元就是該對象本身。而重載為友元函數時,友元函數對某個對象的資料進行操作,就必須通過該對象的名稱來進行,因此使用到的參數都要進行傳遞,運算元的個數就不會有變化。
運算子多載的主要優點就是允許改變使用於系統內部的運算子的操作方式,以適應使用者自訂類型的類似運算。
二、 運算子多載為成員函數
對於雙目運算子B,如果要重載B為類的成員函數,使之能夠實現運算式oprd1 B oprd2,其中oprd1為類A的對象,則應當把B重載為A類的成員函數,該函數只有一個形參,形參的類型是oprd2所屬的類型。經過重載後,運算式oprd1 B oprd2 就相當於函數調用oprd1.operator B(oprd2).
對於前置單目運算子U,如“-”(負號)等,如果要重載U為類的成員函數,用來實現運算式U oprd,其中oprd為A類的對象,則U應當重載為A類的成員函數,函數沒有形參。經過重載之後,運算式U oprd相當於函數調用oprd.operator U().
對於後置運算子“++”和“- -”,如果要將它們重載為類的成員函數,用來實現運算式oprd++或oprd--,其中oprd為A類的對象,那麼運算子就應當重載為A類的成員函數,這時函數要帶有一個整型形參。重載之後,運算式oprd++和oprd—就想當於函數調用oprd.operator++(0)和oprd.operator—(0);
運算子多載就是賦予已有的運算子多重含義。通過重新定義運算子,使它能夠用於特定類的對象執行特定的功能,這便增強了C++語言的擴充能力。
1. 運算子多載的作用:
運算子多載允許C/C++的運算子在使用者定義型別(類)上擁有一個使用者定義的意義。重載的運算子是函數調用的文法修飾:
class Fred
{
public:
// ...
};
#if 0
// 沒有算符重載:
Fred add(Fred, Fred);
Fred mul(Fred, Fred);
Fred f(Fred a, Fred b, Fred c)
{
return add(add(mul(a,b), mul(b,c)), mul(c,a)); // 哈哈,多可笑...
}
#else
// 有算符重載:
Fred operator+ (Fred, Fred);
Fred operator* (Fred, Fred);
Fred f(Fred a, Fred b, Fred c)
{
return a*b + b*c + c*a;
}
#endif
2. 可以用作重載的運算子:
算術運算子:+,-,*,/,%,++,--;
位操作運算子:&,|,~,^,<<,>>
邏輯運算子:!,&&,||;
比較子:<,>,>=,<=,==,!=;
賦值運算子:=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=;
其他運算子:[],(),->,,(逗號運算子),new,delete,new[],delete[],->*。
下列運算子不允許重載:
.,.*,::,?:
3. 運算子多載後,優先順序和結合性:
使用者重載新定義運算子,不改變原運算子的優先順序和結合性。這就是說,對運算子多載不改變運算子的優先順序和結合性,並且運算子多載後,也不改變運算子的文法結構,即單目運算子只能重載為單目運算子,雙目運算子只能重載雙目運算子。
4. 編譯器如何選用哪一個運算子函數:
運算子多載實際是一個函數,所以運算子的重載實際上是函數的重載。編譯器對運算子多載的選擇,遵循著函數重載的選擇原則。當遇到不很明顯的運算時,編譯器將去尋找參數相匹配的運算子函數。
5. 重載運算子有哪些限制:
(1) 不可臆造新的運算子。必須把重載運算子限制在C++語言中已有的運算子範圍內的允許重載的運算子之中。
(2) 重載運算子堅持4個“不能改變”。
·不能改變運算子運算元的個數;
·不能改變運算子原有的優先順序;
·不能改變運算子原有的結合性;
·不能改變運算子原有的文法結構。
6. 運算子多載時必須遵循哪些原則:
運算子多載可以使程式更加簡潔,使運算式更加直觀,增加可讀性。但是,運算子多載使用不宜過多,否則會帶來一定的麻煩。
(1) 重載運算子含義必須清楚。
(2) 重載運算子不能有二義性。
運算子多載函數的兩種形式
運算子多載的函數一般地採用如下兩種形式:成員函數形式和友元函數形式。這兩種形式都可訪問類中的私人成員。
1. 重載為類的成員函數
這裡先舉一個關於給複數運算重載複數的四則運算子的例子。複數由實部和虛部構造,可以定義一個複數類,然後再在類中重載複數四則運算的運算子。先看以下原始碼:
#include <iostream.h>
class complex
{
public:
complex() { real=imag=0; }
complex(double r, double i)
{
real = r, imag = i;
}
complex operator +(const complex &c);
complex operator -(const complex &c);
complex operator *(const complex &c);
complex operator /(const complex &c);
friend void print(const complex &c);
private:
double real, imag;
};
inline complex complex::operator +(const complex &c)
{
return complex(real + c.real, imag + c.imag);
}
inline complex complex::operator -(const complex &c)
{
return complex(real - c.real, imag - c.imag);
}
inline complex complex::operator *(const complex &c)
{
return complex(real * c.real - imag * c.imag, real * c.imag + imag * c.real);
}
inline complex complex::operator /(const complex &c)
{
return complex((real * c.real + imag + c.imag) / (c.real * c.real + c.imag * c.imag),
(imag * c.real - real * c.imag) / (c.real * c.real + c.imag * c.imag));
}
void print(const complex &c)
{
if(c.imag<0)
cout<<c.real<<c.imag<<‘i‘;
else
cout<<c.real<<‘+‘<<c.imag<<‘i‘;
}
void main()
{
complex c1(2.0, 3.0), c2(4.0, -2.0), c3;
c3 = c1 + c2;
cout<<"/nc1+c2=";
print(c3);
c3 = c1 - c2;
cout<<"/nc1-c2=";
print(c3);
c3 = c1 * c2;
cout<<"/nc1*c2=";
print(c3);
c3 = c1 / c2;
cout<<"/nc1/c2=";
print(c3);
c3 = (c1+c2) * (c1-c2) * c2/c1;
cout<<"/n(c1+c2)*(c1-c2)*c2/c1=";
print(c3);
cout<<endl;
}
該程式的運行結果為:
c1+c2=6+1i
c1-c2=-2+5i
c1*c2=14+8i
c1/c2=0.45+0.8i
(c1+c2)*(c1-c2)*c2/c1=9.61538+25.2308i
在程式中,類complex定義了4個成員函數作為運算子多載函數。將運算子多載函數說明為類的成員函數格式如下:
<類名> operator <運算子>(<參數表>)
其中,operator是定義運算子多載函數的關鍵字。
程式中出現的運算式:
c1+c2
編譯器將給解釋為:
c1.operator+(c2)
其中,c1和c2是complex類的對象。operator+()是運算+的重載函數。
該運算子多載函數僅有一個參數c2。可見,當重載為成員函數時,雙目運算子僅有一個參數。對單目運算子,重載為成員函數時,不能再顯式說明參數。重載為成員函數時,總時隱含了一個參數,該參數是this指標。this指標是指向調用該成員函數對象的指標。
2. 重載為友元函數:
運算子多載函數還可以為友元函數。當重載友元函數時,將沒有隱含的參數this指標。這樣,對雙目運算子,友元函數有2個參數,對單目運算子,友元函數有一個參數。但是,有些運行符不能重載為友元函數,它們是:=,(),[]和->。
重載為友元函數的運算子多載函數的定義格式如下:
friend <類型說明符> operator <運算子>(<參數表>)
{……}
下面用友元函數代碼成員函數,重載編寫上述的例子,程式如下:
#include <iostream.h>
class complex
{
public:
complex() { real=imag=0; }
complex(double r, double i)
{
real = r, imag = i;
}
friend complex operator +(const complex &c1, const complex &c2);
friend complex operator -(const complex &c1, const complex &c2);
friend complex operator *(const complex &c1, const complex &c2);
friend complex operator /(const complex &c1, const complex &c2);
friend
void print(const complex &c);
private:
double real, imag;
};
complex operator +(const complex &c1, const complex &c2)
{
return complex(c1.real + c2.real, c1.imag + c2.imag);
}
complex operator -(const complex &c1, const complex &c2)
{
return complex(c1.real - c2.real, c1.imag - c2.imag);
}
complex operator *(const complex &c1, const complex &c2)
{
return complex(c1.real * c2.real - c1.imag * c2.imag, c1.real * c2.imag + c1.imag * c2.real);
}
complex operator /(const complex &c1, const complex &c2)
{
return complex((c1.real * c2.real + c1.imag * c2.imag) / (c2.real * c2.real + c2.imag * c2.imag),
(c1.imag * c2.real - c1.real * c2.imag) / (c2.real * c2.real + c2.imag * c2.imag));
}
void print(const complex &c)
{
if(c.imag<0)
cout<<c.real<<c.imag<<‘i‘;
else
cout<<c.real<<‘+‘<<c.imag<<‘i‘;
}
void main()
{
complex c1(2.0, 3.0), c2(4.0, -2.0), c3;
c3 = c1 + c2;
cout<<"/nc1+c2=";
print(c3);
c3 = c1 - c2;
cout<<"/nc1-c2=";
print(c3);
c3 = c1 * c2;
cout<<"/nc1*c2=";
print(c3);
c3 = c1 / c2;
cout<<"/nc1/c2=";
print(c3);
c3 = (c1+c2) * (c1-c2) * c2/c1;
cout<<"/n(c1+c2)*(c1-c2)*c2/c1=";
print(c3);
cout<<endl;
}
該程式的運行結果與上例相同。前面已講過,對又目運算子,重載為成員函數時,僅一個參數,另一個被隱含;重載為友元函數時,有兩個參數,沒有隱含參數。因此,程式中出現的 c1+c2
編譯器解釋為:
operator+(c1, c2)
調用如下函數,進行求值,
complex operator +(const coplex &c1, const complex &c2)
3. 兩種重載形式的比較
一般說來,單目運算子最好被重載為成員;對雙目運算子最好被重載為友元函數,雙目運算子多載為友元函數比重載為成員函數更方便此,但是,有的雙目運算子還是重載為成員函數為好,例如,賦值運算子。因為,它如果被重載為友元函數,將會出現與賦值語義不一致的地方。 其他運算子的重載舉例
1).下標運算子多載
由於C語言的數組中並沒有儲存其大小,因此,不能對數組元素進行存取範圍的檢查,無法保證給數組動態賦值不會越界。利用C++的類可以定義一種更安全、功能強的數群組類型。為此,為該類定義重載運算子[]。
下面一個例子:
#include <iostream.h>
class CharArray
{
public:
CharArray(int l)
{
Length = l;
Buff = new char[Length];
}
~CharArray() { delete Buff; }
int GetLength() { return Length; }
char & operator [](int i);
private:
int Length;
char * Buff;
};
char & CharArray::operator [](int i)
{
static char ch = 0;
if(i<Length&&i>=0)
return Buff[i];
else
{
cout<<"/nIndex out of range.";
return ch;
}
}
void main()
{
int cnt;
CharArray string1(6);
char * string2 = "string";
for(cnt=0; cnt<8; cnt++)
string1[cnt] = string2[cnt];
cout<<"/n";
for(cnt=0; cnt<8; cnt++)
cout<<string1[cnt];
cout<<"/n";
cout<<string1.GetLength()<<endl;
}
該數組類的優點如下:
(1) 其大小不一定是一個常量。
(2) 運行時動態指定大小可以不用運算子new和delete。
(3) 當使用該類數組作函數參數時,不心分別傳遞陣列變數本身及其大小,因為該對象中已經儲存大小。
在重載下標運算子函數時應該注意:
(1) 該函數只能帶一個參數,不可帶多個參數。
(2) 不得重載為友元函數,必須是非static類的成員函數。 2). 重載增1減1運算子.
增1減1運算子是單目運算子。它們又有首碼和尾碼運算兩種。為了區分這兩種運算,將尾碼運算視為又目運算子。運算式
obj++或obj--
被看作為:
obj++0或obj--0
下面舉一例子說明重載增1減1運算子的應用。
#include <iostream.h>
class counter
{
public:
counter() { v=0; }
counter operator ++();
counter operator ++(int );
void print() { cout<<v<<endl; }
private:
unsigned v;
};
counter counter::operator ++()
{
v++;
return *this;
}
counter counter::operator ++(int)
{
counter t;
t.v = v++;
return t;
}
void main()
{
counter c;
for(int i=0; i<8; i++)
c++;
c.print();
for(i=0; i<8; i++)
++c;
c.print();
}
3). 重載函數調用運算子
可以將函數調用運算子()看成是下標運算[]的擴充。函數調用運算子可以帶0個至多個參數。下面通過一個執行個體來熟悉函數調用運算子的重載。
#include <iostream.h>
class F
{
public:
double operator ()(double x, double y) const;
};
double F::operator ()(double x, double y) const
{
return (x+5)*y;
}
void main()
{
F f;
cout<<f(1.5, 2.2)<<endl;
}
C++主要操作符重載的定義和總結