新標準C++程序設計讀書筆記_運算符重載
形式
返回值類型 operator 運算符(形參表)
{
……
}
運算符重載
(1)運算符重載的實質是函數重載
(2)可以重載為普通函數,也可以重載為成員函數
1 class Complex 2 { 3 public: 4 double real,imag; 5 Complex( double r = 0.0, double i= 0.0 ):real(r),imag(i) { } 6 Complex operator-(const Complex & c); 7 }; 89 Complex operator+( const Complex & a, const Complex & b) 10 { 11 return Complex( a.real + b.real, a.imag + b.imag); //返回一個臨時對象 12 } 13 14 Complex Complex::operator-(const Complex & c) 15 { 16 return Complex(real - c.real, imag - c.imag); //返回一個臨時對象 17 } 18 19 int main()20 { 21 Complex a(4,4),b(1,1),c; 22 23 //等價於c=operator+(a,b); 24 c = a + b; 25 cout << c.real << "," << c.imag << endl; 26 27 //a-b等價於a.operator-(b) 28 cout << (a - b).real << "," << (a - b).imag << endl; 29 return 0;30 }
(3)把含運算符的表達式轉換成對運算符函數的調用
(4)把運算符的操作數轉換成運算符函數的參數
(5)運算符被多次重載時,根據實參的類型決定調用哪個運算符函數
(6)重載為成員函數時, 參數個數為運算符目數減一;重載為普通函數時, 參數個數為運算符目數
賦值運算符 ‘ =’重載
賦值運算符“ =”只能重載為成員函數
1 #include <iostream> 2 3 using namespace std; 4 5 class String 6 { 7 private: 8 char * str; 9 public: 10 String ():str(new char[1]) { str[0] = 0;} 11 const char * c_str() { return str; }; 12 String & operator = (const char * s); 13 String::~String( ) { delete [] str; } 14 }; 15 16 String & String::operator = (const char * s) 17 { 18 //重載“=”以使得 obj = “hello”能夠成立 19 delete [] str; 20 str = new char[strlen(s)+1]; 21 strcpy( str, s); 22 return * this; 23 } 24 25 int main() 26 { 27 String s; 28 s = "Good Luck," ; //等價於 s.operator=("Good Luck,"); 29 cout << s.c_str() << endl; 30 31 /* 這條語句要是不註釋掉就會出錯,因為這是一個初始化語句而並不是賦值語句 */ 32 // String s2 = "hello!"; 33 34 s = "Shenzhou 8!"; //等價於 s.operator=("Shenzhou 8!"); 35 cout << s.c_str() << endl; 36 return 0; 37 }
淺拷貝和深拷貝
考察下面的代碼
1 String S1, S2; 2 S1 = “this”; 3 S2 = “that”; 4 S1 = S2;
如果還用上面的運算符重載,那就會出現問題
(1)如不定義自己的賦值運算符,那麽S1=S2實際上導致 S1.str和 S2.str指向同一地方。
(2)如果S1對象消亡,析構函數將釋放S1.str指向的空間,再訪問S2的時候就好玩兒了。
(3)另外,如果執行 S1 = "other";會導致S2.str指向的地方被delete
因此,要做如下修改:
1 String & operator = (const String & s) 2 { 3 if( this == & s) 4 return * this; 5 6 delete [] str; 7 str = new char[strlen(s.str)+1]; 8 strcpy( str,s.str); 9 return * this; 10 }
ps:詳細完成一個完整的String類,後續再補充
運算符重載為友元函數
一般情況下,將運算符重載為類的成員函數,是較好的選擇。但有時,重載為成員函數不能滿足使用要求,重載為普通函數,又不能訪問類的私有成員,所以需要將運算符重載為友元。
1 class Complex 2 { 3 double real,imag; 4 public: 5 Complex( double r, double i):real(r),imag(i){ }; 6 Complex operator+( double r ); 7 }; 8 9 Complex Complex::operator+( double r ) 10 { 11 //能解釋 c+5 12 return Complex(real + r,imag); 13 }
上面的類中重載的加號有局限性
Complex c ;
c = c + 5; //有定義,相當於 c = c.operator +(5);
但是:
c = 5 + c; //編譯出錯
為了使得上述的表達式能成立,需要將 + 重載為普通函數。但是普通函數又不能訪問私有成員,所以,需要將運算符 + 重載為友元。
1 class Complex 2 { 3 double real,imag; 4 public: 5 Complex( double r, double i):real(r),imag(i){ }; 6 Complex operator+( double r ); 7 friend Complex operator + (double r,const Complex & c); 8 }; 9 10 Complex Complex::operator+( double r ) 11 { 12 //能解釋 c+5 13 return Complex(real + r,imag); 14 } 15 16 Complex operator+ (double r,const Complex & c) 17 { 18 //能解釋 5+c 19 return Complex( c.real + r, c.imag); 20 }
設計一個變長數組類
想要達到下面目的
1 int main() 2 { 3 CArray a; //開始裏的數組是空的 4 /* 5 要用動態分配的內存來存放數組元素,需要一個指針成員變量 6 */ 7 for( int i = 0;i < 5;++i) 8 a.push_back(i); 9 10 CArray a2,a3; 11 /* 12 要重載“=” 13 */ 14 a2 = a; 15 for( int i = 0; i < a.length(); ++i ) 16 /* 17 要重載“[ ]” 18 */ 19 cout << a2[i] << " " ; 20 21 a2 = a3; //a2是空的 22 for( int i = 0; i < a2.length(); ++i ) //a2.length()返回0 23 cout << a2[i] << " "; 24 cout << endl; 25 26 a[3] = 100; 27 /* 28 要自己寫復制構造函數 29 */ 30 CArray a4(a); 31 for( int i = 0; i < a4.length(); ++i ) 32 cout << a4[i] << " "; 33 34 return 0; 35 }
類的設計如下:
1 class CArray { 2 int size; //數組元素的個數 3 int *ptr; //指向動態分配的數組 4 public: 5 CArray(int s = 0); //s代表數組元素的個數 6 CArray(CArray & a); 7 ~CArray(); 8 void push_back(int v); //用於在數組尾部添加一個元素v 9 CArray & operator=( const CArray & a); 10 //用於數組對象間的賦值 11 int length() { return size; } //返回數組元素個數 12 int & CArray::operator[](int i) //返回值為 int 不行!不支持 a[i] = 4 13 { 14 //用以支持根據下標訪問數組元素, 15 // 如n = a[i] 和a[i] = 4; 這樣的語句 16 return ptr[i]; 17 } 18 }; 19 20 CArray::CArray(int s):size(s) 21 { 22 if( s == 0) 23 ptr = NULL; 24 else 25 ptr = new int[s]; 26 } 27 28 CArray::CArray(CArray & a) 29 { 30 if( !a.ptr) { 31 ptr = NULL; 32 size = 0; 33 return; 34 } 35 ptr = new int[a.size]; 36 memcpy( ptr, a.ptr, sizeof(int ) * a.size); 37 size = a.size; 38 } 39 40 CArray::~CArray() 41 { 42 if( ptr) delete [] ptr; 43 } 44 45 CArray & CArray::operator=( const CArray & a) 46 { 47 //賦值號的作用是使“=”左邊對象裏存放的數組,大小和內容都和右邊的對象一樣 48 if( ptr == a.ptr) //防止a=a這樣的賦值導致出錯 49 return * this; 50 51 if( a.ptr == NULL) { //如果a裏面的數組是空的 52 if( ptr ) 53 delete [] ptr; 54 ptr = NULL; 55 size = 0; 56 return * this; 57 } 58 59 if( size < a.size) { //如果原有空間夠大,就不用分配新的空間 60 if(ptr) 61 delete [] ptr; 62 ptr = new int[a.size]; 63 } 64 65 memcpy( ptr, a.ptr, sizeof(int) * a.size); 66 size = a.size; 67 return * this; 68 } 69 70 void CArray::push_back(int v) 71 { 72 //在數組尾部添加一個元素 73 if( ptr) { 74 int * tmpPtr = new int[size + 1]; //重新分配空間 75 memcpy(tmpPtr, ptr, sizeof(int) * size); //拷貝原數組內容 76 delete [] ptr; 77 ptr = tmpPtr; 78 } 79 else //數組本來是空的 80 ptr = new int[1]; 81 82 ptr[size++] = v; //加入新的數組元素 83 }
流運算符的重載
假定c是Complex復數類的對象,現在希望寫“ cout << c;”,就能以“ a+bi”的形式輸出c的值,寫“ cin>>c;”,就能從鍵盤接受“ a+bi”形式的輸入,並且使得c.real = a,c.imag = b。
1 class Complex 2 { 3 double real,imag; 4 public: 5 Complex( double r=0, double i=0):real(r),imag(i){ }; 6 friend ostream & operator<<( ostream & os, const Complex & c); 7 friend istream & operator>>( istream & is,Complex & c); 8 }; 9 10 ostream & operator<<( ostream & os,const Complex & c) 11 { 12 os << c.real << "+" << c.imag << "i"; //以"a+bi"的形式輸出 13 return os; 14 } 15 16 istream & operator>>( istream & is,Complex & c) 17 { 18 string s; 19 is >> s; //將"a+bi"作為字符串讀入, “a+bi” 中間不能有空格 20 int pos = s.find("+", 0); 21 string sTmp = s.substr(0, pos); //分離出代表實部的字符串 22 c.real = atof(sTmp.c_str()); //atof庫函數能將const char*指針指向的內容轉換成 float 23 sTmp = s.substr(pos+1, s.length()-pos-2); //分離出代表虛部的字符串 24 c.imag = atof(sTmp.c_str()); 25 return is; 26 }
類型轉換運算符
類型強制轉換運算符被重載時不能寫返回值類型,實際上其返回值類型就是該類型強制轉換運算符代表的類型
1 #include <iostream> 2 using namespace std; 3 class Complex 4 { 5 double real,imag; 6 public: 7 Complex(double r=0,double i=0):real(r),imag(i) { }; 8 //重載強制類型轉換運算符 double 9 operator double () { return real; } 10 11 }; 12 13 int main() 14 { 15 Complex c(1.2,3.4); 16 cout << (double)c << endl; //輸出 1.2 17 double n = 2 + c; //等價於 double n=2+c.operator double() 18 cout << n; //輸出 3.2 19 }
自增,自減運算符的重載
1、前置運算符作為一元運算符重載
(1)重載為成員函數:
T & operator++();
T & operator--();
(2)重載為全局函數:
T1 & operator++(T2);
T1 & operator—(T2);
2、後置運算符作為二元運算符重載,多寫一個沒用的參數
(1)重載為成員函數:
T operator++(int);
T operator--(int);
(2)重載為全局函數:
T1 operator++(T2,int );
T1 operator—( T2,int);
1 class CDemo 2 { 3 private: 4 int n; 5 public: 6 CDemo(int i=0):n(i) { } 7 CDemo & operator++(); //用於前置形式 8 CDemo operator++( int ); //用於後置形式 9 operator int ( ) { return n; } 10 friend CDemo & operator--(CDemo & ); 11 friend CDemo operator--(CDemo & ,int); 12 }; 13 14 CDemo & CDemo::operator++() 15 { 16 //前置 ++ 17 n++; 18 return * this; 19 } // ++s即為: s.operator++(); 20 21 CDemo CDemo::operator++( int k ) 22 { 23 //後置 ++ 24 CDemo tmp(* this); //記錄修改前的對象 25 n++; 26 return tmp; //返回修改前的對象 27 } // s++即為: s.operator++(0); 28 29 CDemo & operator--(CDemo & d) 30 { 31 //前置-- 32 d.n--; 33 return d; 34 } //--s即為: operator--(s); 35 36 CDemo operator--(CDemo & d,int) 37 { 38 //後置-- 39 CDemo tmp(d); 40 d.n--; 41 return tmp; 42 } //s--即為: operator--(s, 0); 43 44 int main() 45 { 46 CDemo d(5); 47 cout << (d++ ) << ","; //等價於 d.operator++(0); 48 cout << d << ","; 49 cout << (++d) << ","; //等價於 d.operator++(); 50 cout << d << endl; 51 cout << (d-- ) << ","; //等價於 operator--(d,0); 52 cout << d << ","; 53 cout << (--d) << ","; //等價於 operator--(d); 54 cout << d << endl; 55 return 0; 56 }
運算符重載的註意事項
(1)C++不允許定義新的運算符 ;
(2)重載後運算符的含義應該符合日常習慣;
complex_a + complex_b
word_a > word_b
date_b = date_a + n
(3)運算符重載不改變運算符的優先級;
(4)以下運算符不能被重載:“.” 、“.*” 、“::” 、“?:” 、 sizeof;
(5)重載運算符()、 []、 ->或者賦值運算符=時,運算符重載函數必須聲明為類的成員函數。
新標準C++程序設計讀書筆記_運算符重載