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