第四章 運算符重載
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)例子。可略了。