第四章 運算符重載
C++提供了數據抽象的手段:用戶自己定義數據類型 -- 類
? 調用類的成員函數—>操作它的對象
類的成員函數—>操作對象時,很不方便
? 在數學上,兩個復數可以直接進行+/-等運算 Vs. 在C++中,直接將+或-用於復數是不允許的
class Complex { public: Complex( double r = 0.0, double i= 0.0 ){ real = r; imaginary = i; } double real; // real part double imaginary; // imaginary part }; Complex operator+ (const Complex & a, const Complex & b) { return Complex( a.real+b.real, a.imaginary+b.imaginary); } // “類名(參數表)” 就代表一個對象 Complex a(1,2), b(2,3), c; c = a + b;// 相當於什麼? operator+(a,b)重載為普通函數時,參數個數為運算符目數
class Complex { public: Complex( double r= 0.0, double m = 0.0 ):real(r), imaginary(m) { } // constructor Complex operator+ ( const Complex & ); // addition Complex operator- ( const Complex & ); // subtraction private: double real; // real part double imaginary; // imaginary part }; // Overloaded addition operator Complex Complex::operator+(const Complex & operand2) { return Complex( real + operand2.real,imaginary + operand2.imaginary ); } // Overloaded subtraction operator Complex Complex::operator- (const Complex & operand2){ return Complex( real - operand2.real,imaginary - operand2.imaginary ); } int main(){ Complex x, y(4.3, 8.2), z(3.3, 1.1); x = y + z;// 相當於什麼? y.operator+(z) x = y - z;// 相當於什麼? y.operator-(z) return 0; }重載為成員函數時,參數個數為運算符目數減一
需要 重載賦值運算符 ‘=’
賦值運算符 “=” 只能重載為 成員函數編寫一個長度可變的字符串類String
? 包含一個char * 類型的成員變量
—> 指向動態分配的存儲空間
? 該存儲空間用於存放 ‘\0’ 結尾的字符串
class String { private: char * str; public: String () : str(NULL) { } //構造函數, 初始化str為NULL const char * c_str() { return str; } //返回值為const類型,保證str不會被修改。比如char* p=str.c_str();則編譯器會報錯,類型不匹配。 char * operator = (const char * s); ~String( );//需要考慮String對象是否指向了動態分配的存儲空間 }; //重載‘=’使得obj = “hello”能夠成立 char * String::operator = (const char * s){ if(str) delete [] str; if(s) { //s不為NULL才會執行拷貝 str = new char[strlen(s)+1]; strcpy(str, s); } else str = NULL; return str; } String::~String( ) { if(str) delete [] str; }; int main(){ String s; s = “Good Luck,” ; cout << s.c_str() << endl; // String s2 = “hello!”; //這條語句要是不注釋掉就會出錯 s = "Shenzhou 8!"; cout << s.c_str() << endl; return 0; }
S1 = S2;
淺復制/淺拷貝
深復制/深拷貝
在 class MyString 裡添加成員函數:
String & operator = (const String & s) { if(str) delete [] str; str = new char[strlen(s.str)+1]; strcpy(str, s.str); return * this; }
考慮下面語句,是否會有問題?
MyString s; s = “Hello”; s = s;
正確寫法:
String & String::operator = (const String & s){ if(str == s.str) return * this;//增加此行 if(str) delete [] str; if(s.str) { //s.str不為NULL才會執行拷貝 str = new char[strlen(s.str)+1]; strcpy( str,s.str); } else str = NULL; return * this; }
為 String類編寫 復制構造函數 時,會面臨和 ‘=’ 同樣的問題,用同樣的方法處理
String::String(String & s) { if(s.str) { str = new char[strlen(s.str)+1]; strcpy(str, s.str); } else str = NULL; }
class Complex{ double real, imag; public: Complex(double r, double i):real(r), imag(i){ }; Complex operator+(double r); }; Complex Complex::operator+(double r){ //能解釋 c+5 return Complex(real + r, imag); }經過上述重載後:
Complex operator+ (double r, const Complex & c) { //能解釋 5+c return Complex( c.real + r, c.imag); }普通函數不能訪問私有成員 —> 將運算符+重載為友元函數
class Complex { double real, imag; public: Complex( double r, double i):real(r),imag(i){ }; Complex operator+( double r ); friend Complex operator + (double r, const Complex & c); };
class CArray { int size; //數組元素的個數 int *ptr; //指向動態分配的數組 public: CArray(int s = 0); //s代表數組元素的個數 CArray(CArray & a); ~CArray(); void push_back(int v); //用於在數組尾部添加一個元素v CArray & operator=( const CArray & a); //用於數組對象間的賦值 int length() { return size; } //返回數組元素個數 int & CArray::operator[](inti) //返回值為 int 不行!不支持 a[i] = 4 {//用以支持根據下標訪問數組元素, //如n = a[i]和a[i] = 4; 這樣的語句 return ptr[i]; } }; CArray::CArray(int s):size(s) { if( s == 0) ptr = NULL; else ptr = new int[s]; } CArray::CArray(CArray & a) { if( !a.ptr) { ptr = NULL; size = 0; return; } ptr = new int[a.size]; memcpy( ptr, a.ptr, sizeof(int ) * a.size); size = a.size; }
CArray::~CArray() { if( ptr) delete [] ptr; } CArray & CArray::operator=( const CArray & a) { //賦值號的作用是使“=”左邊對象裡存放的數組,大小和內容都和右邊的對象一樣 if( ptr == a.ptr) //防止a=a這樣的賦值導致出錯 return * this; if( a.ptr == NULL) { //如果a裡面的數組是空的 if( ptr ) delete [] ptr; ptr = NULL; size = 0; return * this; } if( size < a.size) { //如果原有空間夠大,就不用分配新的空間 if(ptr) delete [] ptr; ptr = new int[a.size]; } memcpy( ptr,a.ptr,sizeof(int)*a.size); size = a.size; return * this; } // CArray & CArray::operator=( const CArray & a) void CArray::push_back(int v) { //在數組尾部添加一個元素 if( ptr) { int * tmpPtr = new int[size+1]; //重新分配空間 memcpy(tmpPtr,ptr,sizeof(int)*size); //拷貝原數組內容 delete [] ptr; ptr = tmpPtr; } else //數組本來是空的 ptr = new int[1]; ptr[size++] = v; //加入新的數組元素 }
cout << 5 << “this”;為什麼能夠成立?
cout是什麼?“<<” 為什麼能用在 cout上?
cout 是在 iostream 中定義的,ostream 類的對象。
“<<” 能用在cout 上是因為,在iostream裡對 “<<” 進行了重載。
考慮,怎麼重載才能使得cout << 5; 和 cout << “this”都能成立?
有可能按以下方式重載成 ostream類的成員函數:
void ostream::operator<<(int n) { …… //輸出n的代碼 return; }
因為ostream已經封裝好了,不可能把這個重載寫成成員函數,所以寫成全局函數。
cout << 5 ; 即 cout.operator<<(5);怎麼重載才能使得cout << 5 << “this” ;成立?
ostream & ostream::operator<<(int n) { …… //輸出n的代碼 return * this; } ostream & ostream::operator<<( const char * s ) { …… //輸出s的代碼 return * this; }cout << 5 << “this”;本質上的函數調用的形式是什麼?
假定下面程序輸出為 5hello, 該補寫些什麼
class CStudent{ public: int nAge; }; int main(){ CStudent s ; s.nAge = 5; cout << s <<"hello"; return 0; }
ostream & operator<<( ostream & o,const CStudent & s){ o << s.nAge ; return o; }
(教材P218)例子。可略了。