?C++重載運算符和重載函數(shù)
C++的運算符重載在維基百科中的定義如下:
在計算機程序設計中,運算符重載(英語:operator overloading)是多態(tài)的一種。這里,運算符(比如+,=或==)被當作多態(tài)函數(shù),它們的行為隨著其參數(shù)類型的不同而不同。運算符并不一定總是符號。
運算符重載通常只是一種語法糖。它可以簡單地通過函數(shù)調(diào)用來模擬:
a + b * c
在一個支持運算符重載的語言里,上面的寫法要比下面的寫法有效而簡練:
add(a, multiply(b, c))
(假設運算符* 的優(yōu)先級高于運算符 +)
當一種語言允許運算符在某種情況下被隱式調(diào)用的時候,運算符重載將不只提供寫法上的方便。例如,Ruby中的to_s運算符就是如此,它返回一個對象的字符串表示。
C++中的函數(shù)重載在維基百科中的定義如下:
函數(shù)重載(英語:function overloading),是Ada、C++、C#、D和Java等編程語言中具有的一項特性,這項特性允許創(chuàng)建數(shù)項名稱相同但輸入輸出類型或個數(shù)不同的子程序,它可以簡單地稱為一個單獨功能可以執(zhí)行多項任務的能力。
C++允許在同一個作用域中的某個函數(shù)或者運算符指定多個定義,這樣的方式分別被稱為函數(shù)重載和運算符重載。
重載聲明是指的一個與之前已經(jīng)在這個作用域內(nèi)聲明過的函數(shù)或者方法具有相同的名稱的聲明,但是他們的參數(shù)和定義并不完全相同。
當調(diào)用一個重載函數(shù)和重載運算發(fā)的時候,編譯器會通過把使用的參數(shù)類型的定義中的參數(shù)類型進行比較,最終選定最合適的定義,選擇最合適的重載函數(shù)或者重載運算符的過程,這樣的過程成員為重載決策。
C++中的函數(shù)重載
在同一個作用域內(nèi),可以聲明幾個功能類似的同名函數(shù),但是這些同名函數(shù)的參數(shù)(參數(shù)個數(shù),參數(shù)類型,參數(shù)順序)必須不同,同時不能僅僅通過返回類型的不同來重載函數(shù)。
接下來我們看一段函數(shù)重載的代碼:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
class printData {
public:
void print(int number){
cout << number <<"n";
}
void print(double number){
cout << number <<"n";
}
void print(string s){
cout << s <<"n";
}
};
int main(){
printData test;
test.print(1);
test.print(1.1);
test.print("abcde");
return0;
}
通過上面的代碼,即使函數(shù)名相同,單數(shù)傳入的參數(shù)類型不同,那么程序調(diào)用的函數(shù)也不相同,程序的執(zhí)行結果也不相同,這就是C++中的函數(shù)名重載。
C++中的運算符重載
在C++中額可以重載或重定義大多數(shù)的內(nèi)置的運算符,這樣我們就可以使用自定義類型的運算符。
重載運算符的函數(shù)通常是帶有特殊函數(shù)名的函數(shù),函數(shù)是由關鍵字operator和后面的運算符符合組成的,和普通函數(shù)相同,重載的運算符有一個返回類型和參數(shù)列表。
形式如下:
Boxoperator+(constBox&);
重載加法運算是用于將兩個Box類型的對象進行相加并最終返回Box類型的對象,大多數(shù)的重載運算符可以被定義為普通的非成員函數(shù)或者類內(nèi)的成員函數(shù),如果定義的函數(shù)是類的非成員函數(shù),那么我們需要在每次執(zhí)行函數(shù)的時候向函數(shù)內(nèi)傳遞兩個參數(shù),形式如下所示:
Boxoperator+(constBox&,constBox&);
下面的代碼通過使用成員函數(shù)進行了運算符重載,此時,對象作為參數(shù)進行傳遞,代碼如下所示:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classBox{
private:
int length, breadth, height;
public:
int get_volume(){
return length * breadth * height;
}
void set_length(int l){
length = l;
}
void set_breadth(int b){
breadth = b;
}
void set_height(int h){
height = h;
}
Boxoperator+(Box& add_box){
Box box;
box.length = add_box.length + length, box.breadth = add_box.breadth + breadth, box.height = add_box.height + height;
return box;
}
};
int main(){
Box box1, box2, box3;
box1.set_length(1), box1.set_breadth(2), box1.set_height(3);
box2.set_length(4), box2.set_breadth(5), box2.set_height(6);
box3 = box1 + box2;
cout <<"The volume of box1 is: "<< box1.get_volume()<<"n";
cout <<"The volume of box2 is: "<< box2.get_volume()<<"n";
cout <<"The volume of box3 is: "<< box3.get_volume()<<"n";
return0;
}
可重載運算符/不可重載運算符
下面列出C++中可重載和不可重載的運算符。
可重載運算符:
運算符類型 | 運算符名稱 |
---|---|
算數(shù)運算符 | + - * / % |
關系運算符 | == != < > <= >= |
邏輯運算符 | |
單目運算符 | + - * & |
自增運算符 | ++ -- |
位運算符 | |
賦值運算符 | = += -= *= /= %= &= |
空間申請和釋放 | new delete new[] delete[] |
其他 | () -> , [] |
不可重載運算符:
運算符名稱 | 運算符符號 |
---|---|
成員訪問運算符 | . |
成員指針訪問運算符 | .* ->* |
域運算符 | :: |
長度運算符 | sizeof |
條件運算符 | ?: |
預處理符號 | # |
下面逐個對可重載的運算符進行演示。
一元運算符重載
一元運算符指的是只對一個操作數(shù)進行操作,下面是一元運算符的實例。
遞增運算符(++),遞減運算符(--)
負號(-)
邏輯非運算符(!)
通常一元運算符出現(xiàn)在操作的對象的左邊,下面演示重載負號(-)一元運算符:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classPoint{
private:
int x, y;
public:
Point(int x_x =0,int y_y =0){
x = x_x, y = y_y;
}
Pointoperator-(){
x =-x, y =-y;
return*this;
}
voidShow_Point(){
cout <<"("<< x <<","<< y <<")n";
}
};
int main(){
int x, y;
cin >> x >> y;
Point p(x, y);
-p;
p.Show_Point();
return0;
}
上面設計了一段求點關于原點對稱的點的代碼,重載了-運算符來直接對對象進行操作。
二元運算符重載
二元運算符主要需要兩個參數(shù),我們通常使用的加(+),減(-),乘(*),除(/)都是二元運算符。接下來我們嘗試重載 + 運算符:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classBox{
private:
int length, breadth, height;
public:
int get_volume(){
return length * breadth * height;
}
void set_length(int l){
length = l;
}
void set_breadth(int b){
breadth = b;
}
void set_height(int h){
height = h;
}
Boxoperator+(Box& add_box){
Box box;
box.length = add_box.length + length, box.breadth = add_box.breadth + breadth, box.height = add_box.height + height;
return box;
}
};
int main(){
Box box1, box2, box3;
box1.set_length(1), box1.set_breadth(2), box1.set_height(3);
box2.set_length(4), box2.set_breadth(5), box2.set_height(6);
box3 = box1 + box2;
cout <<"The volume of box1 is: "<< box1.get_volume()<<"n";
cout <<"The volume of box2 is: "<< box2.get_volume()<<"n";
cout <<"The volume of box3 is: "<< box3.get_volume()<<"n";
return0;
}
接下來我們嘗試將上面的代碼進行改寫,將運算符重載的函數(shù)以非成員函數(shù)的方式進行重載運算符:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classBox{
private:
int length, breadth, height;
public:
int get_volume(){
return length * breadth * height;
}
void set_length(int l){
length = l;
}
void set_breadth(int b){
breadth = b;
}
void set_height(int h){
height = h;
}
friendBoxoperator+(Box& add_box1,Box& add_box2);
};
Boxoperator+(Box& add_box1,Box&add_box2){
Box box;
box.length = add_box1.length + add_box2.length, box.breadth = add_box1.breadth + add_box2.breadth, box.height = add_box1.height + add_box2.height;
return box;
}
int main(){
Box box1, box2, box3;
box1.set_length(1), box1.set_breadth(2), box1.set_height(3);
box2.set_length(4), box2.set_breadth(5), box2.set_height(6);
box3 = box1 + box2;
cout <<"The volume of box1 is: "<< box1.get_volume()<<"n";
cout <<"The volume of box2 is: "<< box2.get_volume()<<"n";
cout <<"The volume of box3 is: "<< box3.get_volume()<<"n";
return0;
}
將重載函數(shù)作為非成員函數(shù)的時候,是將兩個對象進行相加,并且由于函數(shù)是全局函數(shù),那么需要傳入的參數(shù)個數(shù)為2,同時當重載預算符的函數(shù)是全局函數(shù)的時候,需要在類中將這個函數(shù)聲明為友元函數(shù)。
關系運算符重載
C++支持很多種關系運算符,這些關系運算符用來比較C++中的數(shù)據(jù)類型,我們同時也可以重載任何一個關系運算符,接下來我們嘗試重載>,<,==關系運算符,從而實現(xiàn)一個成績排名的小程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classStudent{
private:
string name, ID;
int score1, score2;
public:
Student(string n, string id,int number1,int number2){
name = n, ID = id, score1 = number1, score2 = number2;
}
Student(){
}
booloperator>(Student& s){
if(score1 + score2 > s.score1 + s.score2){
returntrue;
}
else{
returnfalse;
}
}
booloperator<(Student& s){
if(score1 + score2 < s.score1 + s.score2){
returntrue;
}
else{
returnfalse;
}
}
booloperator==(Student& s){
if(score1 + score2 == s.score1 + s.score2){
returntrue;
}
else{
returnfalse;
}
}
};
int main(){
string name, ID;
int score1, score2;
cin >> name >> ID >> score1 >> score2;
StudentStudent1(name, ID, score1, score2);
cin >> name >> ID >> score1 >> score2;
StudentStudent2(name, ID, score1, score2);
if(Student1>Student2){
cout <<"Student1 is better than Student2";
}
elseif(Student1==Student2){
cout <<"Student1 is equal to Student2";
}
elseif(Student1<Student2){
cout <<"Student1 is worse than Student2";
}
return0;
}
輸入輸出運算符重載
C++能夠使用流提取運算符>>和流插入運算符<<來進行輸入和輸出內(nèi)置的數(shù)據(jù)類型,我們也可以重載流提取和插入運算符來操作對象或者我們自定義的數(shù)據(jù)類型。
通常我們會將輸入術后出的函數(shù)聲明為類的友元函數(shù),這樣我們在使用的函數(shù)就不需要創(chuàng)建對象二直接調(diào)用函數(shù)。
下面我們將之前的對學生成績的代碼進行修改,添加了重載流提取運算符和流插入運算符的部分:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classStudent{
private:
string name, ID;
int score1, score2;
public:
Student(string n, string id,int number1,int number2){
name = n, ID = id, score1 = number1, score2 = number2;
}
Student(){
}
booloperator>(Student& s){
if(score1 + score2 > s.score1 + s.score2){
returntrue;
}
else{
returnfalse;
}
}
booloperator<(Student& s){
if(score1 + score2 < s.score1 + s.score2){
returntrue;
}
else{
returnfalse;
}
}
booloperator==(Student& s){
if(score1 + score2 == s.score1 + s.score2){
returntrue;
}
else{
returnfalse;
}
}
friend istream &operator>>(istream& input,Student&student){
input >> student.name >> student.ID >> student.score1 >> student.score2;
return input;
}
friend ostream &operator<<(ostream& output,Student&student){
output <<"Student:"<< student.name <<" ID:"<< student.ID <<" total score:"<< student.score1 + student.score2 <<"n";
return output;
}
};
int main(){
string name, ID;
int score1, score2;
cin >> name >> ID >> score1 >> score2;
StudentStudent1(name, ID, score1, score2);
cin >> name >> ID >> score1 >> score2;
StudentStudent2(name, ID, score1, score2);
if(Student1>Student2){
cout <<"Student1 is better than Student2n";
}
elseif(Student1==Student2){
cout <<"Student1 is equal to Student2n";
}
elseif(Student1<Student2){
cout <<"Student1 is worse than Student2n";
}
cout <<Student1<<Student2;
return0;
}
上述代碼我們將流插入和提取運算符進行重載,可以直接對對象中的成員變量進行輸入和輸出。
通常我們輸入輸出習慣上使用cin>>和cout<<,這樣重載的時候需要使用友元函數(shù)來重載運算符,如果使用成員函數(shù)來重載運算符的時候將會出現(xiàn)d1<
++和--運算符重載
++和--運算符是C++中兩個重要的一元運算符,下面的示例中以時間變化為例演示重載++運算符的前綴和后綴的兩種用法:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classTime{
private:
int hour, minute, second;
public:
Time(){
hour =0, minute =0, second =0;
}
Time(int h,int m,int s){
hour = h, minute = m, second = s;
}
friend ostream&operator<<(ostream& output,Time T){
output <<"The time is "<< T.hour <<":"<< T.minute <<":"<< T.second <<"n";
return output;
}
Timeoperator++(){
++second;
if(second >=60){
++minute;
minute -=60;
}
if(minute >=60){
++hour;
minute -=60;
}
if(hour >=24){
hour =0;
}
return*this;
}
Timeoperator++(int){
++second;
if(second >=60){
++minute;
minute -=60;
}
if(minute >=60){
++hour;
minute -=60;
}
if(hour >=24){
hour =0;
}
return*this;
}
};
int main(){
int hour, minute, second;
cin >> hour >> minute >> second;
Time T(hour, minute, second);
++T;
cout << T;
T++;
cout << T;
return0;
}
遞增和遞減通常是改變對象的狀態(tài),所以對遞增和遞減運算符的重載通常為成員函數(shù)。
重載遞增遞減一定要和指針的遞增和遞減進行區(qū)分,疑問這里的重載操作的是對象而不是指針(由于指針是內(nèi)置類型,因此指針的遞增和遞減是無法被重載的),所以通常情況下的遞增和遞減操作的對象是對象內(nèi)部的成員變量。
遞增和遞減運算符分為前置和后置兩種情況,因為符號相同,因此給后置的版本增加一個int類型的形參,這個形參為0,在函數(shù)體中式用不到的,引入這個形參只是為了區(qū)分富豪的前置和后置。
賦值運算符重載
和其他運算符相同,C++中允許我們重載賦值運算符,用來創(chuàng)建宇哥對象,比如拷貝構造函數(shù)。下面我們演示對賦值運算符的重載:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classTime{
private:
int hour, minute, second;
public:
Time(){
hour =0, minute =0, second =0;
}
Time(int h,int m,int s){
hour = h, minute = m, second = s;
}
friend ostream&operator<<(ostream& output,Time T){
output <<"The time is "<< T.hour <<":"<< T.minute <<":"<< T.second <<"n";
return output;
}
Timeoperator=(Time& T){
hour = T.hour, minute = T.minute, second = T.second;
return*this;
}
};
int main(){
int hour, minute, second;
cin >> hour >> minute >> second;
Time T1(hour, minute, second);
Time T2;
T2 = T1;
cout << T1 << T2;
return0;
}
上述代碼通過將時間進行復制的操作演示了對賦值運算符的重載。
淺拷貝和隱式轉換:通常情況我們使用隱式轉換函數(shù)的時候十分謹慎,因為當我們不需要使用轉換蛤屬的時候,這個函數(shù)仍可能被調(diào)用執(zhí)行,這些不正確的程序可能會出現(xiàn)一些意想不到的事情,而我們很難對此做出判斷,同時淺拷貝存在指針懸空的問題,所以我們通常禁止隱式轉換,在構造函數(shù)前面添加explicit關鍵字,通常情況下隱式轉換的聲明的意義不大,在實際情況中通常使用淺拷貝,不會調(diào)用隱式轉換。
函數(shù)調(diào)用運算符重載
蛤屬調(diào)用運算符可以被重載與類的對象,當我們重載()的時候,我們并不是調(diào)用了一種新的調(diào)用函數(shù)的方式,相反的這是創(chuàng)建了一個可以傳遞任意數(shù)目參數(shù)的運算符函數(shù),下面我們通過求總秒數(shù)的例子來演示重載函數(shù)調(diào)用運算符:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classTime{
private:
int hour, minute, second, total_second;
public:
Time(){
hour =0, minute =0, second =0, total_second =0;
}
Time(int h,int m,int s){
hour = h, minute = m, second = s, total_second =3600*24* hour +3600* minute + second;
}
void show_total_second(){
cout << total_second;
}
Timeoperator()(int h,int m,int s){
Time T;
T.total_second =24*60*60* h +60*60* m + s;
return T;
}
Timeoperator=(Time& T){
this->total_second = T.total_second;
return*this;
}
};
int main(){
int hour, minute, second;
cin >> hour >> minute >> second;
Time T1;
Time T2 = T1(hour, minute, second);
T2.show_total_second();
return0;
}
下標運算符[]重載
下標操作符[]通常用來訪問數(shù)組元素,重載這個運算符可以增強操作數(shù)組的功能,下面的代碼通過重載下標運算符來更方便得訪問類中數(shù)組指定的元素:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classArray{
private:
int number[10], posion;
public:
int&operator[](int i){
return number[i];
}
Array(){
posion =0;
}
void set_value(int num){
number[posion]= num,++ posion;
}
};
int main(){
Array number;
for(R int i =0; i <10;++i){
int n;
cin >> n;
number.set_value(n);
}
for(R int i =0; i <10;++i){
cout << number[i]<<" ";
}
return0;
}
類成員訪問運算符( -> )可以被重載,但它較為麻煩。它被定義用于為一個類賦予"指針"行為。運算符 -> 必須是一個成員函數(shù)。如果使用了 -> 運算符,返回類型必須是指針或者是類的對象。
運算符 -> 通常與指針引用運算符 * 結合使用,用于實現(xiàn)"智能指針"的功能。這些指針是行為與正常指針相似的對象,唯一不同的是,當通過指針訪問對象時,它們會執(zhí)行其他的任務。比如,當指針銷毀時,或者當指針指向另一個對象時,會自動刪除對象。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
// 假設一個實際的類
classObj{
staticint i, j;
public:
void f()const{ cout << i++<< endl;}
void g()const{ cout << j++<< endl;}
};
// 靜態(tài)成員定義
intObj::i =10;
intObj::j =12;
// 為上面的類實現(xiàn)一個容器
classObjContainer{
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj);// 調(diào)用向量的標準方法
}
friendclassSmartPointer;
};
// 實現(xiàn)智能指針,用于訪問類 Obj 的成員
classSmartPointer{
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index =0;
}
// 返回值表示列表結束
booloperator++()// 前綴版本
{
if(index >= oc.a.size()-1)returnfalse;
if(oc.a[++index]==0)returnfalse;
returntrue;
}
booloperator++(int)// 后綴版本
{
returnoperator++();
}
// 重載運算符 ->
Obj*operator->()const
{
if(!oc.a[index])
{
cout <<"Zero value";
return(Obj*)0;
}
return oc.a[index];
}
};
int main(){
constint sz =10;
Obj o[sz];
ObjContainer oc;
for(int i =0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc);// 創(chuàng)建一個迭代器
do{
sp->f();// 智能指針調(diào)用
sp->g();
}while(sp++);
return0;
}
一些注意的要點
運算符重載不可以改變語法結構;
運算符重載不可以改變操作數(shù)的個數(shù);
運算符重載不可以改變運算的優(yōu)先級;
運算符重載不可以改變結合性。
類重載、覆蓋、重定義之間的區(qū)別:
重載指的是函數(shù)具有的不同的參數(shù)列表,而函數(shù)名相同的函數(shù)。重載要求參數(shù)列表必須不同,比如參數(shù)的類型不同、參數(shù)的個數(shù)不同、參數(shù)的順序不同。如果僅僅是函數(shù)的返回值不同是沒辦法重載的,因為重載要求參數(shù)列表必須不同。(發(fā)生在同一個類里)
覆蓋是存在類中,子類重寫從基類繼承過來的函數(shù)。被重寫的函數(shù)不能是static的。必須是virtual的。但是函數(shù)名、返回值、參數(shù)列表都必須和基類相同(發(fā)生在基類和子類)
重定義也叫做隱藏,子類重新定義父類中有相同名稱的非虛函數(shù) ( 參數(shù)列表可以不同 ) 。(發(fā)生在基類和子類)
this 指針的作用
this 指針是一個隱含于每一個非靜態(tài)成員函數(shù)中的特殊指針。它指向正在被該成員函數(shù)操作的那個對象。當對一個對象調(diào)用成員函數(shù)時,編譯器先將對象的地址賦給 this 指針,然后調(diào)用成員函數(shù),每次成員函數(shù)存取數(shù)據(jù)成員時由隱含使用 this 指針。
評論