C++从0到1的入门级教学(十二)——运算符重载
文章目錄
- 12 運(yùn)算符重載
- 12.1 加法運(yùn)算符重載
- 12.2 左移運(yùn)算符重載
- 12.2.1 演示與說明
- 12.2.2 常見的友元使用:重載>>運(yùn)算符
- 12.3 遞增運(yùn)算符重載
- 12.4 賦值運(yùn)算符重載
- 12.5 關(guān)系運(yùn)算符重載
- 12.6 函數(shù)調(diào)用運(yùn)算符重載
12 運(yùn)算符重載
在本講中,我們會(huì)設(shè)計(jì)到一些使得對象操作更美觀的技術(shù)——運(yùn)算符重載。運(yùn)算符重載是一種C++多態(tài)的形式。
運(yùn)算符通過重載,可以實(shí)現(xiàn)其原本沒有的含義。C++允許運(yùn)算符重載擴(kuò)展到用戶定義的類型,如將兩個(gè)對象相加。
我們前面實(shí)際上學(xué)過數(shù)組,如果要將兩個(gè)數(shù)組的對應(yīng)元素相加,我們需要寫一個(gè)for循環(huán)來遍歷數(shù)組中的元素,并且分別進(jìn)行相加。實(shí)際上,這就是一種簡單的重載,我們口頭上所說的arr1+arr2,實(shí)際上賦予了——加是對應(yīng)元素相加的這層含義。
所以,為了簡化它的意思,我們可以將對應(yīng)元素相加后的新數(shù)組寫為以下的形式:
arr3 = arr1+arr2
總結(jié)上述所講:運(yùn)算符重載的含義是:對已有的運(yùn)算符重新進(jìn)行定義,賦予其另一種功能,以適應(yīng)不同的數(shù)據(jù)類型。
重載的通用格式如下所示:
operator op(argument - list)
op指的是C++現(xiàn)有的運(yùn)算符;例如加法運(yùn)算符重載為operator +()。當(dāng)然C++以外的、虛構(gòu)的運(yùn)算符不可重載,這是需要注意的。
12.1 加法運(yùn)算符重載
對于內(nèi)置數(shù)據(jù)類型,編譯器知道如何進(jìn)行運(yùn)算:
- int a = 10;
- int b = 10;
- int c = a+b
但是我們假如是下面這種情況呢?
class Person { public:int m_A;int m_B;}Person p1; p1 m_A = 10; p1 m_B = 10;Person p2; Person p1; p2 m_A = 10; p2 m_B = 10;Person p3 = p1+p2;在這種情況下,C++編譯器就會(huì)蒙了,Person是啥類型,怎么還能相加?
我們實(shí)際上可以編寫一個(gè)成員函數(shù)來實(shí)現(xiàn)此功能,實(shí)現(xiàn)兩個(gè)對象相加屬性后返回新的對象。而對于C++來說,為了指明是加法運(yùn)算符的作用發(fā)生了變化,故該函數(shù)統(tǒng)一命名為operatao +。
如果你按照它的命名規(guī)則來了,那么在調(diào)用函數(shù)時(shí),你就可以使用p3 = p1+p2的格式去調(diào)用這個(gè)函數(shù)。
以上的方式我們叫做通過成員函數(shù)來重載+號(hào)運(yùn)算符,除此之外,我們還可以通過全局函數(shù)來重載+號(hào)運(yùn)算符。
讓我們看看下面的例子來理解我上述的話。
#include <iostream> using namespace std;//加號(hào)運(yùn)算符重載 class Person { public://成員函數(shù)重載+號(hào)/*Person operator+(Person p) {Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}*/int m_A;int m_B;private:};//全局函數(shù)重載+號(hào) Person operator+(Person& p1,Person& p2) {Person temp;temp.m_A = p1.m_A + p2.m_A;temp.m_B = p1.m_B + p2.m_B;return temp; }void test01() {Person p1;p1.m_A = 10;p1.m_B = 10;Person p2;p2.m_A = 10;p2.m_B = 10;Person p3 = p1 + p2;cout << "p3.m_A:" << p3.m_A << endl;cout << "p3.m_B:" << p3.m_B << endl; }int main() {test01(); }out:
運(yùn)算符重載也可以發(fā)生函數(shù)重載。這樣的原因是可以應(yīng)對多種情況。如p3 = p2+10,對于10,我們并沒有寫出對應(yīng)解決的方案函數(shù),所以如果運(yùn)算符重載不能發(fā)送函數(shù)重載,那么就對運(yùn)算符不太友好了。
總的來說,加法運(yùn)算符就是使得自定義的數(shù)據(jù)類型能夠按照自己想要的方式相加。需要注意的是,對于內(nèi)置的數(shù)據(jù)類型的操作運(yùn)算符是不可改變的;并且,對于運(yùn)算符的重載不要濫用。
12.2 左移運(yùn)算符重載
12.2.1 演示與說明
左移運(yùn)算符指的是我們在輸出的時(shí)候使用的那個(gè)運(yùn)算符。
cout<<a<<endl;如果是對于內(nèi)置的基本數(shù)據(jù)類型,那肯定是可以利用左移運(yùn)算符來進(jìn)行輸出的。但是如果是對象呢?
Person p; cout<<p<<endl;這樣的對象輸出的結(jié)果肯定是不被允許的,如下所示。
對此,我們可以通過重載左移運(yùn)算符來獲得我們想要的效果。
我們前面說過可以通過成員函數(shù)和全局函數(shù)來重載,但是對于左移運(yùn)算符,其并不能在成員函數(shù)中被重載,因?yàn)檫壿嫴粚ΑK?#xff0c;對于通常的左移運(yùn)算符重載,我們都是在全局函數(shù)中去實(shí)現(xiàn)它。
試著敲一下下面的代碼你就能理會(huì)其中的思想了。
#include <iostream> using namespace std;class Person { public:int m_A;int m_B; };//只能利用全局函數(shù)重載左移運(yùn)算符 void operator<<(ostream& cout, Person& p) {cout << "m_A = " << p.m_A << "m_B = " << p.m_B ; }void test01() {Person p1;p1.m_A = 10;p1.m_B = 20;cout << p1 ; }int main() {test01();system("pause"); }out:
我們實(shí)際上可以嘗試在重載過后的<<寫上這樣的代碼:
cout<<p1<<endl;很快地,你會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了。報(bào)錯(cuò)的原因是因?yàn)?#xff0c;你的重載函數(shù)并沒有返回值,而對于cout之所以可以使用兩個(gè)左移運(yùn)算符,是因?yàn)槠鋬?nèi)含有鏈?zhǔn)骄幊痰乃枷搿9?#xff0c;如果想要使得可以連續(xù)使用<<,我們必須將函數(shù)的返回值改為cout即可。
順便提一句,cout是ostream實(shí)例化的結(jié)果,即輸出對象。
ostream對象?
ostream對象是指輸出流對象,通常來說,cout輸出的對象是指顯示器,當(dāng)然,還有輸入流對象ofstream,這些我們到后面再去提及。
我們將以上的代碼改寫為如下即可實(shí)現(xiàn)多次使用<<:
#include <iostream> using namespace std;class Person { public:int m_A;int m_B; };//只能利用全局函數(shù)重載左移運(yùn)算符 ostream& operator<<(ostream& cout, Person& p) {cout << "m_A = " << p.m_A << "m_B = " << p.m_B ;return cout; }void test01() {Person p1;p1.m_A = 10;p1.m_B = 20;cout << p1 << endl; }int main() {test01();system("pause"); }out:
為什么使用引用?
在上述的代碼中實(shí)際上兩個(gè)位置使用到了引用。返回值和參數(shù)是引用的原因是:我們自始至終都想這對cout這個(gè)對象做功能上的修改,而不是從ostream再創(chuàng)建一個(gè)cout對象,然后賦予它新功能。故,我們要加上引用。
12.2.2 常見的友元使用:重載>>運(yùn)算符
在實(shí)際開發(fā)中,我們實(shí)際上會(huì)把成員屬性寫入私有,這樣的話,對于重載函數(shù)無法訪問,我們只需要利用友元即可解決這個(gè)問題。
#include <iostream> using namespace std;class Person {friend ostream& operator<<(ostream& cout, Person& p); private:int m_A =10;int m_B = 20; };//重載左移運(yùn)算符 ostream& operator<<(ostream& cout, Person& p) {cout << "m_A = " << p.m_A << "m_B = " << p.m_B;return cout; }void test01() {Person p1;cout << p1 << endl; }int main() {test01(); }out:
12.3 遞增運(yùn)算符重載
我們來看看遞增運(yùn)算符的原理。
int a = 10;cout<< ++a <<endl;//11 cout << a <<endl; //結(jié)果為11cout <<b++<<endl;//10 cout << b <<endl;//11對于++a,其原理為先做++操作再輸出,而對于b++,其原理為先輸出再++。
同樣地,我們對對象做自增操作,其原理如下:
class MyInteger { public:MyInteger(){m_Num = 0;}private:int m_Num; }MyInteger myint; cout <<myint<<endl; //0 cout << ++myint <<endl; //1 cout << myint++ <<endl; //1 cout << myint <<endl; //2我們來試著實(shí)現(xiàn)上述的原理:
#include <iostream> using namespace std;class MyInteger {friend ostream& operator<<(ostream& cout, MyInteger myint); public:MyInteger() {m_Num = 0;}//局部函數(shù)重載++運(yùn)算符(前置++)MyInteger& operator++() //返回一個(gè)引用是為了一直對一個(gè)數(shù)據(jù)進(jìn)行操作{m_Num++;return *this;}//局部函數(shù)重載++運(yùn)算符(后置++)MyInteger operator++(int) //不能返回引用,因?yàn)榇藭r(shí)返回值是局部變量{//1 記錄當(dāng)前結(jié)果MyInteger temp = *this;//2 遞增m_Num++;//3 將記錄結(jié)果返回return temp;}private:int m_Num; };ostream& operator<<(ostream& cout, MyInteger myint) {cout << myint.m_Num;return cout; }void test01() {MyInteger myint;cout << ++myint << endl; }void test02() {MyInteger mying;cout << mying++ << endl;cout << mying << endl; }int main() {test01();test02();system("pause"); }12.4 賦值運(yùn)算符重載
賦值運(yùn)算符的重載原理涉及到深淺拷貝,我們從代碼中理解比較好。
#include <iostream> using namespace std;//賦值運(yùn)算符重載 class Person { public:Person(int age) {m_Age = new int(age);}~Person() {if (m_Age != NULL) {delete(m_Age);m_Age = NULL;}}int* m_Age; };void test01() {Person p1(18);Person p2(20);p2 = p1;//賦值操作cout << "p1的年齡為:" << *p1.m_Age << endl;cout << "p2的年齡為:" << *p2.m_Age << endl;}int main() {test01();system("pause");return 0; }從以上的代碼中,我們會(huì)發(fā)現(xiàn)無法運(yùn)行,根本原因是因?yàn)?#xff1a;
也就是說,根本原因是因?yàn)樵瓉淼馁x值運(yùn)算符提供的是一個(gè)淺拷貝的過程,但是我們想要的是一個(gè)深拷貝的效果,故,我們需要重載賦值運(yùn)算符。如下所示:
#include <iostream> using namespace std;//賦值運(yùn)算符重載 class Person { public:Person(int age) {m_Age = new int(age);}~Person() {if (m_Age != NULL) {delete(m_Age);m_Age = NULL;}}//重載賦值運(yùn)算符Person& operator=(Person &p) {//對于編譯器來說,其提供的是淺拷貝//我們應(yīng)該需要用深拷貝//應(yīng)該先判斷是否有屬性在堆區(qū),如果有先釋放干凈,然后再深拷貝if (m_Age != NULL) {delete m_Age;m_Age = NULL;}//深拷貝m_Age = new int(*p.m_Age);//記住,要返回指針,才能實(shí)現(xiàn)鏈?zhǔn)骄幊蘲eturn *this;}int* m_Age; };void test01() {Person p1(18);Person p2(20);Person p3(30);p3 = p2 = p1;//賦值操作cout << "p1的年齡為:" << *p1.m_Age << endl;cout << "p2的年齡為:" << *p2.m_Age << endl;cout << "p3的年齡為:" << *p3.m_Age << endl; }int main() {test01();system("pause");return 0; }out:
12.5 關(guān)系運(yùn)算符重載
對于自定義的數(shù)據(jù)類型,C++是無法提供對比方式的,如>、<、=等。為此,我們需要重載關(guān)系運(yùn)算符。
讓我們用以下的代碼來解決這個(gè)問題:
#include <iostream> using namespace std;//重載關(guān)系運(yùn)算符 class Person { public:Person(string name, int age) {m_Name = name;m_Age = age;};//重載 == 號(hào)bool operator==(Person& p) {if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return true;}return false;}string m_Name;int m_Age;private:};void test01() {Person p1("Tom", 18);Person p2("jarry", 19);//Person p2("Tom", 18);if (p1 == p2) {cout << "相同" << endl;}else{cout << "不相同" << endl;} }int main() {test01(); }12.6 函數(shù)調(diào)用運(yùn)算符重載
函數(shù)調(diào)用運(yùn)算符()也可以重載,需要注意的是,由于重載后使用的方式非常像函數(shù)的調(diào)用,因此稱為仿函數(shù)。仿函數(shù)沒有固定的寫法,其風(fēng)格多變。
讓我們試著敲一下下面的代碼:
#include <iostream> using namespace std; #include <string>//函數(shù)調(diào)用運(yùn)算符重載 //打印輸出類 class MyPrint { public://重載函數(shù)調(diào)用運(yùn)算符void operator()(string test) {cout << test << endl;} };class MyAdd { public://重載例子2int operator()(int num1, int num2){return num1 + num2;} };void test01() {MyPrint myPrint;myPrint("hello world"); }void test02() {MyAdd myadd;cout << "ret = " << myadd(100, 200) << endl; }int main() {test01();test02(); }C++和Java一樣,也是創(chuàng)建一個(gè)匿名對象,匿名對象使用后當(dāng)場銷毀。如在上例中我想創(chuàng)建一個(gè)匿名對象,只需:
MyAdd()(100,100)總結(jié)
以上是生活随笔為你收集整理的C++从0到1的入门级教学(十二)——运算符重载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: backgroundworder 简单使
- 下一篇: C++ 和ws2def.h相关的一坨错误