?C++重載運(yùn)算符和重載函數(shù)
C++的運(yùn)算符重載在維基百科中的定義如下:
在計(jì)算機(jī)程序設(shè)計(jì)中,運(yùn)算符重載(英語:operator overloading)是多態(tài)的一種。這里,運(yùn)算符(比如+,=或==)被當(dāng)作多態(tài)函數(shù),它們的行為隨著其參數(shù)類型的不同而不同。運(yùn)算符并不一定總是符號。
運(yùn)算符重載通常只是一種語法糖。它可以簡單地通過函數(shù)調(diào)用來模擬:
a + b * c
在一個(gè)支持運(yùn)算符重載的語言里,上面的寫法要比下面的寫法有效而簡練:
add(a, multiply(b, c))
(假設(shè)運(yùn)算符* 的優(yōu)先級高于運(yùn)算符 +)
當(dāng)一種語言允許運(yùn)算符在某種情況下被隱式調(diào)用的時(shí)候,運(yùn)算符重載將不只提供寫法上的方便。例如,Ruby中的to_s運(yùn)算符就是如此,它返回一個(gè)對象的字符串表示。
C++中的函數(shù)重載在維基百科中的定義如下:
函數(shù)重載(英語:function overloading),是Ada、C++、C#、D和Java等編程語言中具有的一項(xiàng)特性,這項(xiàng)特性允許創(chuàng)建數(shù)項(xiàng)名稱相同但輸入輸出類型或個(gè)數(shù)不同的子程序,它可以簡單地稱為一個(gè)單獨(dú)功能可以執(zhí)行多項(xiàng)任務(wù)的能力。
C++允許在同一個(gè)作用域中的某個(gè)函數(shù)或者運(yùn)算符指定多個(gè)定義,這樣的方式分別被稱為函數(shù)重載和運(yùn)算符重載。
重載聲明是指的一個(gè)與之前已經(jīng)在這個(gè)作用域內(nèi)聲明過的函數(shù)或者方法具有相同的名稱的聲明,但是他們的參數(shù)和定義并不完全相同。
當(dāng)調(diào)用一個(gè)重載函數(shù)和重載運(yùn)算發(fā)的時(shí)候,編譯器會通過把使用的參數(shù)類型的定義中的參數(shù)類型進(jìn)行比較,最終選定最合適的定義,選擇最合適的重載函數(shù)或者重載運(yùn)算符的過程,這樣的過程成員為重載決策。
C++中的函數(shù)重載
在同一個(gè)作用域內(nèi),可以聲明幾個(gè)功能類似的同名函數(shù),但是這些同名函數(shù)的參數(shù)(參數(shù)個(gè)數(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 1400000000usingnamespace 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í)行結(jié)果也不相同,這就是C++中的函數(shù)名重載。
C++中的運(yùn)算符重載
在C++中額可以重載或重定義大多數(shù)的內(nèi)置的運(yùn)算符,這樣我們就可以使用自定義類型的運(yùn)算符。
重載運(yùn)算符的函數(shù)通常是帶有特殊函數(shù)名的函數(shù),函數(shù)是由關(guān)鍵字operator和后面的運(yùn)算符符合組成的,和普通函數(shù)相同,重載的運(yùn)算符有一個(gè)返回類型和參數(shù)列表。
形式如下:
Boxoperator+(constBox&);
重載加法運(yùn)算是用于將兩個(gè)Box類型的對象進(jìn)行相加并最終返回Box類型的對象,大多數(shù)的重載運(yùn)算符可以被定義為普通的非成員函數(shù)或者類內(nèi)的成員函數(shù),如果定義的函數(shù)是類的非成員函數(shù),那么我們需要在每次執(zhí)行函數(shù)的時(shí)候向函數(shù)內(nèi)傳遞兩個(gè)參數(shù),形式如下所示:
Boxoperator+(constBox&,constBox&);
下面的代碼通過使用成員函數(shù)進(jìn)行了運(yùn)算符重載,此時(shí),對象作為參數(shù)進(jì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 1400000000usingnamespace 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;}
可重載運(yùn)算符/不可重載運(yùn)算符
下面列出C++中可重載和不可重載的運(yùn)算符。
可重載運(yùn)算符:
| 運(yùn)算符類型 | 運(yùn)算符名稱 |
|---|---|
| 算數(shù)運(yùn)算符 | + - * / % |
| 關(guān)系運(yùn)算符 | == != < > <= >= |
| 邏輯運(yùn)算符 | |
| 單目運(yùn)算符 | + - * & |
| 自增運(yùn)算符 | ++ -- |
| 位運(yùn)算符 | |
| 賦值運(yùn)算符 | = += -= *= /= %= &= |
| 空間申請和釋放 | new delete new[] delete[] |
| 其他 | () -> , [] |
不可重載運(yùn)算符:
| 運(yùn)算符名稱 | 運(yùn)算符符號 |
|---|---|
| 成員訪問運(yùn)算符 | . |
| 成員指針訪問運(yùn)算符 | .* ->* |
| 域運(yùn)算符 | :: |
| 長度運(yùn)算符 | sizeof |
| 條件運(yùn)算符 | ?: |
| 預(yù)處理符號 | # |
下面逐個(gè)對可重載的運(yùn)算符進(jìn)行演示。
一元運(yùn)算符重載
一元運(yùn)算符指的是只對一個(gè)操作數(shù)進(jìn)行操作,下面是一元運(yùn)算符的實(shí)例。
遞增運(yùn)算符(++),遞減運(yùn)算符(--)
負(fù)號(-)
邏輯非運(yùn)算符(!)
通常一元運(yùn)算符出現(xiàn)在操作的對象的左邊,下面演示重載負(fù)號(-)一元運(yù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 1400000000usingnamespace 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è)計(jì)了一段求點(diǎn)關(guān)于原點(diǎn)對稱的點(diǎn)的代碼,重載了-運(yùn)算符來直接對對象進(jìn)行操作。
二元運(yùn)算符重載
二元運(yùn)算符主要需要兩個(gè)參數(shù),我們通常使用的加(+),減(-),乘(*),除(/)都是二元運(yùn)算符。接下來我們嘗試重載 + 運(yù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 1400000000usingnamespace 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;}
接下來我們嘗試將上面的代碼進(jìn)行改寫,將運(yùn)算符重載的函數(shù)以非成員函數(shù)的方式進(jìn)行重載運(yù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 1400000000usingnamespace 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í)候,是將兩個(gè)對象進(jìn)行相加,并且由于函數(shù)是全局函數(shù),那么需要傳入的參數(shù)個(gè)數(shù)為2,同時(shí)當(dāng)重載預(yù)算符的函數(shù)是全局函數(shù)的時(shí)候,需要在類中將這個(gè)函數(shù)聲明為友元函數(shù)。
關(guān)系運(yùn)算符重載
C++支持很多種關(guān)系運(yùn)算符,這些關(guān)系運(yùn)算符用來比較C++中的數(shù)據(jù)類型,我們同時(shí)也可以重載任何一個(gè)關(guān)系運(yùn)算符,接下來我們嘗試重載>,<,==關(guān)系運(yùn)算符,從而實(shí)現(xiàn)一個(gè)成績排名的小程序:
#include#include#include#include#include#include#include#include#include#include#include#include#define R register#define LL longlong#define pi 3.141#define INF 1400000000usingnamespace 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;}
輸入輸出運(yùn)算符重載
C++能夠使用流提取運(yùn)算符>>和流插入運(yùn)算符<<來進(jìn)行輸入和輸出內(nèi)置的數(shù)據(jù)類型,我們也可以重載流提取和插入運(yùn)算符來操作對象或者我們自定義的數(shù)據(jù)類型。
通常我們會將輸入術(shù)后出的函數(shù)聲明為類的友元函數(shù),這樣我們在使用的函數(shù)就不需要創(chuàng)建對象二直接調(diào)用函數(shù)。
下面我們將之前的對學(xué)生成績的代碼進(jìn)行修改,添加了重載流提取運(yùn)算符和流插入運(yù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 1400000000usingnamespace 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;}
上述代碼我們將流插入和提取運(yùn)算符進(jìn)行重載,可以直接對對象中的成員變量進(jìn)行輸入和輸出。
通常我們輸入輸出習(xí)慣上使用cin>>和cout<<,這樣重載的時(shí)候需要使用友元函數(shù)來重載運(yùn)算符,如果使用成員函數(shù)來重載運(yùn)算符的時(shí)候?qū)霈F(xiàn)d1<
++和--運(yùn)算符重載
++和--運(yùn)算符是C++中兩個(gè)重要的一元運(yùn)算符,下面的示例中以時(shí)間變化為例演示重載++運(yù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 1400000000usingnamespace 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),所以對遞增和遞減運(yùn)算符的重載通常為成員函數(shù)。
重載遞增遞減一定要和指針的遞增和遞減進(jìn)行區(qū)分,疑問這里的重載操作的是對象而不是指針(由于指針是內(nèi)置類型,因此指針的遞增和遞減是無法被重載的),所以通常情況下的遞增和遞減操作的對象是對象內(nèi)部的成員變量。
遞增和遞減運(yùn)算符分為前置和后置兩種情況,因?yàn)榉栂嗤虼私o后置的版本增加一個(gè)int類型的形參,這個(gè)形參為0,在函數(shù)體中式用不到的,引入這個(gè)形參只是為了區(qū)分富豪的前置和后置。
賦值運(yùn)算符重載
和其他運(yùn)算符相同,C++中允許我們重載賦值運(yùn)算符,用來創(chuàng)建宇哥對象,比如拷貝構(gòu)造函數(shù)。下面我們演示對賦值運(yù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 1400000000usingnamespace 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í)間進(jìn)行復(fù)制的操作演示了對賦值運(yùn)算符的重載。
淺拷貝和隱式轉(zhuǎn)換:通常情況我們使用隱式轉(zhuǎn)換函數(shù)的時(shí)候十分謹(jǐn)慎,因?yàn)楫?dāng)我們不需要使用轉(zhuǎn)換蛤?qū)俚臅r(shí)候,這個(gè)函數(shù)仍可能被調(diào)用執(zhí)行,這些不正確的程序可能會出現(xiàn)一些意想不到的事情,而我們很難對此做出判斷,同時(shí)淺拷貝存在指針懸空的問題,所以我們通常禁止隱式轉(zhuǎn)換,在構(gòu)造函數(shù)前面添加explicit關(guān)鍵字,通常情況下隱式轉(zhuǎn)換的聲明的意義不大,在實(shí)際情況中通常使用淺拷貝,不會調(diào)用隱式轉(zhuǎn)換。
函數(shù)調(diào)用運(yùn)算符重載
蛤?qū)僬{(diào)用運(yùn)算符可以被重載與類的對象,當(dāng)我們重載()的時(shí)候,我們并不是調(diào)用了一種新的調(diào)用函數(shù)的方式,相反的這是創(chuàng)建了一個(gè)可以傳遞任意數(shù)目參數(shù)的運(yùn)算符函數(shù),下面我們通過求總秒數(shù)的例子來演示重載函數(shù)調(diào)用運(yù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 1400000000usingnamespace 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;}
下標(biāo)運(yùn)算符[]重載
下標(biāo)操作符[]通常用來訪問數(shù)組元素,重載這個(gè)運(yùn)算符可以增強(qiáng)操作數(shù)組的功能,下面的代碼通過重載下標(biāo)運(yùn)算符來更方便得訪問類中數(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 1400000000usingnamespace 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;}
類成員訪問運(yùn)算符( -> )可以被重載,但它較為麻煩。它被定義用于為一個(gè)類賦予"指針"行為。運(yùn)算符 -> 必須是一個(gè)成員函數(shù)。如果使用了 -> 運(yùn)算符,返回類型必須是指針或者是類的對象。
運(yùn)算符 -> 通常與指針引用運(yùn)算符 * 結(jié)合使用,用于實(shí)現(xiàn)"智能指針"的功能。這些指針是行為與正常指針相似的對象,唯一不同的是,當(dāng)通過指針訪問對象時(shí),它們會執(zhí)行其他的任務(wù)。比如,當(dāng)指針銷毀時(shí),或者當(dāng)指針指向另一個(gè)對象時(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 1400000000usingnamespace std;// 假設(shè)一個(gè)實(shí)際的類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;// 為上面的類實(shí)現(xiàn)一個(gè)容器classObjContainer{vector<Obj*> a;public:void add(Obj* obj){a.push_back(obj);// 調(diào)用向量的標(biāo)準(zhǔn)方法}friendclassSmartPointer;};// 實(shí)現(xiàn)智能指針,用于訪問類 Obj 的成員classSmartPointer{ObjContainer oc;int index;public:SmartPointer(ObjContainer& objc){oc = objc;index =0;}// 返回值表示列表結(jié)束booloperator++()// 前綴版本{if(index >= oc.a.size()-1)returnfalse;if(oc.a[++index]==0)returnfalse;returntrue;}booloperator++(int)// 后綴版本{returnoperator++();}// 重載運(yùn)算符 ->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)建一個(gè)迭代器do{sp->f();// 智能指針調(diào)用sp->g();}while(sp++);return0;}
一些注意的要點(diǎn)
運(yùn)算符重載不可以改變語法結(jié)構(gòu);
運(yùn)算符重載不可以改變操作數(shù)的個(gè)數(shù);
運(yùn)算符重載不可以改變運(yùn)算的優(yōu)先級;
運(yùn)算符重載不可以改變結(jié)合性。
類重載、覆蓋、重定義之間的區(qū)別:
重載指的是函數(shù)具有的不同的參數(shù)列表,而函數(shù)名相同的函數(shù)。重載要求參數(shù)列表必須不同,比如參數(shù)的類型不同、參數(shù)的個(gè)數(shù)不同、參數(shù)的順序不同。如果僅僅是函數(shù)的返回值不同是沒辦法重載的,因?yàn)橹剌d要求參數(shù)列表必須不同。(發(fā)生在同一個(gè)類里)
覆蓋是存在類中,子類重寫從基類繼承過來的函數(shù)。被重寫的函數(shù)不能是static的。必須是virtual的。但是函數(shù)名、返回值、參數(shù)列表都必須和基類相同(發(fā)生在基類和子類)
重定義也叫做隱藏,子類重新定義父類中有相同名稱的非虛函數(shù) ( 參數(shù)列表可以不同 ) 。(發(fā)生在基類和子類)
this 指針的作用
this 指針是一個(gè)隱含于每一個(gè)非靜態(tài)成員函數(shù)中的特殊指針。它指向正在被該成員函數(shù)操作的那個(gè)對象。當(dāng)對一個(gè)對象調(diào)用成員函數(shù)時(shí),編譯器先將對象的地址賦給 this 指針,然后調(diào)用成員函數(shù),每次成員函數(shù)存取數(shù)據(jù)成員時(shí)由隱含使用 this 指針。
電子發(fā)燒友App






















評論