操作符重載!
操作符重載為操作符提供不同的語義
#include <iostream> using namespace std; struct Complex { int a; int b; }; int main() { Complex c1 = {1,2}; Complex c2 = {3,4}; Complex c3 = c1 + c2;//編譯出錯 cout << "Press any key to continue..." << endl; cin.get(); return 0; }View Code
上個例子中,不能用“+”直接操作兩個結構體,改動一下,提供一個函數。
//增加一個add函數 #include <iostream> using namespace std; struct Complex { int a; int b; }; Complex add(const Complex& c1,const Complex& c2) { Complex ret; ret.a = c1.a + c2.a; ret.b = c1.b + c2.b; return ret; } int main() { Complex c1View Code= {1,2}; Complex c2 = {3,4}; Complex c3 = add(c1,c2);//編譯出錯 cout<<"c3.a = "<<c3.a<<endl; cout<<"c3.b = "<<c3.b<<endl; cout << "Press any key to continue..." << endl; cin.get(); return 0; }
C++中操作符重載的本質
C++中通過operator
//把add替換為“operator+" #include <iostream> using namespace std; struct Complex { int a; int b; }; Complex operator+ (const Complex& c1,const Complex& c2) { Complex ret; ret.a = c1.a + c2.a; ret.b = c1.b + c2.b; return ret; } int main() { Complex c1 = {1,2}; Complex c2 = {3,4}; Complex c3 = operator+ (c1,c2); cout<<"c3.a = "<<c3.a<<endl; cout<<"c3.b = "<<c3.b<<endl; c3 = c1 + c2; cout<<"c3.a = "<<c3.a<<endl; cout<<"c3.b = "<<c3.b<<endl; cout << "Press any key to continue..." << endl; cin.get(); return 0; }View Code
operator關鍵字拓展的操作符應用到類。
利用友元friend關鍵字可以例外的開放private聲明的類成員的使用權限
//友元friend和全局函數 使操作符重載應用與類 #include <iostream> using namespace std; class Complex { int a; int b; public: Complex(int a=0,int b=0) { this->a = a; this->b = b; } int getA() { return a; } int getB() { return b; } friend Complex operator+ (const Complex& c1,const Complex& c2); }; Complex operator+ (const Complex& c1,const Complex& c2) { Complex ret; ret.a = c1.a + c2.a; ret.b = c1.b + c2.b; return ret; } int main() { Complex c1(1,2); Complex c2(3,4); Complex c3 = c1 + c2; cout<<"c3.a = "<<c3.getA()<<endl; cout<<"c3.b = "<<c3.getB()<<endl; cout << "Press any key to continue..." << endl; cin.get(); return 0; }View Code
以上是“+”操作符,接下來重載“<<”操作符
//左移操作符重載 #include <iostream> using namespace std; class Complex { int a; int b; public: Complex(int a=0,int b=0) { this->a = a; this->b = b; } int getA() { return a; } int getB() { return b; } friend Complex operator+ (const Complex& c1,const Complex& c2); friend ostream& operator<< (ostream& out,const Complex& c); }; Complex operator+ (const Complex& c1,const Complex& c2) { Complex ret; ret.a = c1.a + c2.a; ret.b = c1.b + c2.b; return ret; } ostream& operator<< (ostream& out,const Complex& c) { out<<c.a<<"+"<<c.b<<"i"; return out;//返回ostream類型的out 是為了能連續輸出 } int main() { Complex c1(1,2); Complex c2(3,4); Complex c3 = c1 + c2; //cout<<c1; -> operator<<(cout,c1) 如果沒有返回cout的話,不能接著輸出endl cout<<c1<<endl;//->((operator<<(cout,c1)))<<endl; cout<<c2<<endl; cout<<c3<<endl; cout << "Press any key to continue..." << endl; cin.get(); return 0; }View Code
小結:
1、操作符重載的本質是通過函數擴展操作符的語義 2、operator關鍵字是操作符重載的關鍵
3、friend關鍵字可以函數或類開發訪問權限 4、操作符重載遵循函數重載的規則
通過operator關鍵字能夠講操作符定義為全局函數,操作符重載的本質就是函數重載,那麽類的成員函數是否可以作為操作符重載的函數?
#include <iostream> #include <cstdlib> using namespace std; using namespace std; class Complex { int a; int b; public: Complex(int a,int b) { this ->a = a; this ->b = b; } int getA() { return a; } int getB() { return b; } Complex operator+ (const Complex& c2); friend ostream& operator<< (ostream& out,const Complex& c); }; Complex Complex::operator+ (const Complex& c2) { Complex ret(0,0); ret.a = this->a + c2.a; ret.b = this->b + c2.b; return ret; } ostream& operator<< (ostream& out,const Complex& c) { out<<c.a<<" + "<<c.b<<"i";//數學裏的虛數表示法 return out;//考慮c++標準庫重載的左移操作符支持鏈式調用,所以要返回cout,如果不返回,則不能接著返回endl了; } int main(int argc,char *argv[]) { Complex c1(1,2); Complex c2(3,4); Complex c3 = c1 + c2;//->c3 = c1.operator+(c2) cout<<c1<<endl; cout<<c2<<endl; cout<<c3<<endl; cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS; }View Code
輸出結果一樣。
用成員函數重載的操作符比全局操作符函數少一個參數,即左操作符,而且不需要friend。
問題來了,什麽時候用全局的函數重載,什麽時候有成員函數重載。
1,當無法修改左操作數的類時,使用全局函數進行重載 2,=,[],(),->操作符只能通過成員函數進行重載。
//Array.h #ifndef ARRAY #define ARRAY class Array { private: int mLength; int* mSpace; public: Array(int length); Array(const Array& obj); int length(); ~Array(); int& operator[] (int i); Array& operator= (const Array& obj); bool operator== (const Array& obj); bool operator!= (const Array& obj); }; #endif // ARRAY //Array.c #include "array.h" Array::Array(int length) { if(length < 0) { length = 0; } mLength = length; mSpace = new int[mLength]; } Array::Array(const Array &obj) { mLength = obj.mLength; mSpace = new int[mLength]; for(int i = 0;i<mLength;i++) { mSpace[i] = obj.mSpace[i]; } } int Array::length() { return mLength; } Array::~Array() { mLength = -1; delete[] mSpace; } int& Array::operator[] (int i)//作為左值的調用語句返回的必須是引用 { return mSpace[i]; } Array& Array::operator= (const Array& obj)//返回Array的引用是為了可以連續賦值 { //a3 = a2 = a1;->a3 = a2.operator=(a1); delete[] mSpace;//先釋放自己的原有的堆空間 因為接下來要申請 mLength = obj.mLength; mSpace = new int[mLength]; for(int i = 0;i<mLength;i++) { mSpace[i] = obj.mSpace[i]; } return *this; } bool Array::operator== (const Array& obj) { bool ret = true; if(mLength==obj.mLength) { for(int i=0;i<mLength;i++) { if(mSpace[i] != obj.mSpace[i]) { return false; break; } } } else { ret = false; } return ret; } bool Array::operator!= (const Array& obj) { return !(*this == obj); } //main.c #include <iostream> #include <cstdlib> #include "array.h" using namespace std; int main(int argc,char *argv[]) { Array a1(10); Array a2(0); Array a3(1); for(int i = 0;i<a1.length();i++) { a1[i] = i+ 1; } for(int i = 0;i < a1.length();i++) { cout<<"Element"<<i<<":"<<a1[i]<<endl; } a2 = a1;//-> a2.operator=(a1); //a3 = a2 = a1;//->a3 = a2.operator=(a1); for(int i=0;i<a2.length();i++) { cout<<"Element"<<i<<":"<<a2[i]<<endl; } if(a1 == a2) { cout<<"a1 == a2"<<endl; } if(a3!=a2) { cout<<"a3 != a2"<<endl; } cout << "Press the enter key to continue ..."; cin.get(); return 0; }View Code
為什麽要用到賦值操作符重載?
C++編譯器會為每個類提供默認的賦值操作符,但默認的賦值操作符只是簡單的值復制,一旦有指針類的成員變量則就只復制指針,它們將指向同一片空間,而相應的空間就沒用了,所以類中存在指針成員變量時就需要重載賦值操作符
++操作符的重載
++操作符只有一個操作數,且有前綴和後綴的區分。如何重載++操作符才能區分前置運算和後置運算呢?
#include <iostream> #include <cstdlib> using namespace std; using namespace std; class Complex { int a; int b; public: Complex(int a,int b) { this ->a = a; this ->b = b; } int getA() { return a; } int getB() { return b; } Complex operator+ (const Complex& c2); Complex operator ++(int);//占位參數 Complex& operator ++(); friend ostream& operator<< (ostream& out,const Complex& c); }; Complex Complex::operator+ (const Complex& c2) { Complex ret(0,0); ret.a = this->a + c2.a; ret.b = this->b + c2.b; return ret; } ostream& operator<< (ostream& out,const Complex& c) { out<<c.a<<" + "<<c.b<<"i";//數學裏的虛數表示法 return out;//考慮c++標準庫重載的左移操作符支持鏈式調用,所以要返回cout,如果不返回,則不能接著返回endl了; } Complex Complex:: operator ++(int) { Complex ret = *this;//先將當前對象做一個備份 /*首先解釋++後綴的原理 int a=0; cout<<a++<<endl;則會打印0 且a=1。 為了模擬這個過程則要先保存一個當前值的備份 */ a++;//然後當前對象進行+1操作 b++; return ret;//將備份返回 } Complex& Complex:: operator ++()//為什麽要返回引用? { ++a; ++b; return *this; } int main(int argc,char *argv[]) { Complex c1(1,2); Complex c2(3,4); Complex c3 = c1 + c2;//->c3 = c1.operator+(c2) cout<<c1<<endl; cout<<c2<<endl; cout<<c3<<endl; cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS; }View Code
為什麽不用重載&&和||操作符?
首先在語法上是沒有錯誤的,但最好不要去重載,&&和||的特性是短路。
//短路 int a1 = 0; int a2 = 1; if(a1&&(a1+a2)) { cout<<"Hello"<<endl; } //因為a1=0,則(a1+a2)根本不會執行。這個就是所謂的短路。 //如果重載了&& class Test { int a; public: Test(int i) { this->a = i; } Test operator+ (const Test& obj) { Test ret = 0; ret.a = this->a+obj.a; return ret; } bool operator&& (const Test& obj) { return a&&obj.a; } }; Test t1 = 0; Test t2 =1; if(t1.operator&&(t1.operator+(t2))) { cout<<"Hello"<<endl; } //則會先執行+操作,違反了短路原則。
&&和||是c++中非常特殊的操作符。
&&和||內置實現了短路規則,操作符重載是靠函數重載來實現的,操作數作為函數參數傳遞,c++的函數參數都會被求值,無法實現短路規則。
操作符重載!