C++運算符重載知識點整理

在C++中,運算符和函數是等價的,它和函數一樣可以通過重載的方式來靈活地解決各種實際問題。

 

運算符重載的格式

運算符重載有兩種形式,一是重載爲成員函數形式,二是重載爲友元(或普通的類外)函數形式。

以Complex複數類的 “+” 運算符爲例,重載爲成員函數的形式爲:

class Complex{
	
	int real,image;
	
	public:
		Complex(int r=0,int i=0):real(r),image(i){};
		
		Complex operator+(const Complex& c);//成員函數形式重載"+" 
	
};

Complex Complex::operator+(const Complex& c){ //具體實現 
	return Complex(real+c.real,image+c.image);
}

重載爲友元函數的形式爲:

class Complex{
	
	
	int real,image;
	
	public:
		Complex(int r=0,int i=0):real(r),image(i){};
		
		friend Complex operator+(const Complex& c,const Complex& c2);//友元函數形式重載"+" 
	
};

Complex operator+(const Complex& c,const Complex& c2){ //具體實現 
	return Complex(c.real+c2.real,c.image+c2.image);
}

運算符重載比函數重載多了一個operator關鍵字,重載爲成員函數時有一個參數爲類對象本身,而重載爲外部函數(友元或者普通函數)時需要將所有參數寫出來。

返回類型 operator 運算符(參數列表){
    
}

或者重載爲成員函數形式:

返回類型 所在類的類名::operator 運算符(參數列表){
    
}

其中參數列表根據重載的運算符定,例如二元運算符重載爲友元函數時需要兩個參數(兩個操作數),重載爲成員函數時需要一個參數(另一個參數是該類對象本身)。

運算符重載的規則

1. C++中不可重載的運算符有:

     “ . ”  成員訪問運算符
     “ .* ” 和 “ ->* ”  成員指針訪問運算符
     “ :: ” 域運算符
    “ sizeof ”  長度運算符
    “ ?: ”  條件運算符
    “ # ”   預處理符號

其餘的運算符皆可以重載,如(包括但不限於):

雙目算術運算符 	+ (加),-(減),*(乘),/(除),% (取模)
關係運算符 	==(等於),!= (不等於),< (小於),> (大於>,<=(小於等於),>=(大於等於)
邏輯運算符 	||(邏輯或),&&(邏輯與),!(邏輯非)
單目運算符 	+ (正),-(負),*(指針),&(取地址)
自增自減運算符 	++(自增),--(自減)
位運算符 	| (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移)
賦值運算符 	=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空間申請與釋放 	new, delete, new[ ] , delete[]
其他運算符 	()(函數調用),->(成員訪問),,(逗號),[](下標)

2.C++規定 “=”、“[]”、“()”、“->” 運算符只能以成員函數形式重載,不能重載爲友元函數

3.重載運算符不可改變其優先級、不可改變其操作數個數、不可臆造新運算符。

4.運算符重載只能和自定義類型一起使用,即參數裏至少要有一個是類對象,例如試圖重載整數運算的“ + ”是不被允許的:

int operator + (int a,int b) 
{
    return a-b; 
}

5.除上述特別說明的運算符之外的運算符,可以重載爲類的成員函數、友元函數、普通的類外函數,不可重載爲靜態函數

運算符重載的調用方式

以上文重載的Complex類的“+”運算符爲例,聲明對象c1和c2,並且讓它們相加:

Complex c1(1,2),c2(3,4);
Complex c3 = c1 + c2;

其中"c1 + c2"會被編譯器處理成“c1.operator+(c2)”的形式,如果是重載爲友元函數形式就會被處理成“operator+(c1,c2)”.如果我們重載一元運算符“-”(一元的“-”意爲取負,不是相減運算符):

class Complex{
	
	
	int real,image;
	
	public:
		Complex(int r,int i):real(r),image(i){};
		
		friend Complex operator+(const Complex& c,const Complex& c2);//友元函數形式重載"+" 
		Complex operator-();//友元形式重載取負運算符 
	
};

Complex operator+(const Complex& c,const Complex& c2){ //具體實現 
	return Complex(c.real+c2.real,c.image+c2.image);
}
Complex Complex::operator-(){//具體實現 
	return Complex(-real,-image);
}

int main(){
	
	Complex c1(1,2);
	Complex c3 = -c1;//注意這裏

	return 0;
} 

則“-c1”會被處理成“c1.operator-()”,友元函數形式則是“operator-(c1)”。

 

自增自減運算符的重載

那麼自增和自減運算符又怎樣重載?我們以自增爲例,自減同理。自增又分爲前置自增和後置自增,C++規定前置自增重載形式爲“operator++(void)”後置自增重載爲“operator++(int)”,這是成員函數重載形式,友元函數重載則是“operator++(const Complex& c)”和“operator++(const Complex& c,int)”這兩種寫法分別是前置和後置自增運算符的重載寫法,後置++使用一個int參數來與前置++區分。

下面給出重載爲成員函數的自增運算符寫法,請注意對比前置++和後置++的實現和返回值類型:

#include <iostream>

using namespace std;

class Complex{
	
	int real,image;
	
	public:
		Complex(int r=0,int i=0):real(r),image(i){};
		
		friend Complex operator+(const Complex& c,const Complex& c2);//友元函數形式重載"+" 
		Complex operator-();//友元形式重載取負運算符 
		
		Complex& operator++();//前置++ 
		Complex operator++(int);//後置++ 
		
		void show();//測試用,輸出整個虛數 
		
};

void Complex::show(){
	cout << real
	<< " + "
	<< image
	<< "i"
	<< endl;
}
Complex operator+(const Complex& c,const Complex& c2){ //具體實現 
	return Complex(c.real+c2.real,c.image+c2.image);
}
Complex Complex::operator-(){//具體實現 
	return Complex(-real,-image);
}
Complex& Complex::operator++(){//前置++實現 
	cout << "前置++"
	<< endl;
	
	real++;
	image++;
	return *this; 
}
Complex Complex::operator++(int){//後置++實現 
	cout << "後置++"
	<< endl;
	
	Complex tmp = *this;
	real++;
	image++;
	return tmp;
}


int main(){
	Complex c;
	c.show();
	//測試前置++ 
	++c;
	c.show();
	//測試後置++ 
	Complex c2 = c++;
	c.show();
	c2.show();

	return 0;
} 

習慣上來說,前置++應該返回一個左值,我們應該返回原對象的引用,而後置++則會返回一個未修改前的副本,我們應該直接返回一個對象,讓C++去創建副本返回。類比C++本身的自增運算符,例如對一個int變量分別使用前置++和後置++,我們可以知道前置++是可以作爲左值的,而後置++是不可以作爲左值的(後置++返回的是一個臨時變量,是不可尋址的,所以不能其賦值),我們設計Complex類的時候也應該考慮到這些問題。

其他運算符重載的細節問題

首先要注意的是,我們在重載運算符時應該仔細斟酌返回值類型,例如在重載“=”時,應該返回賦值後的對象的引用,這樣就可以實現連續賦值。“=”運算符系統會幫我們自動生成,當類成員裏有指針的時候,系統實現的 “=”只會幫我們把所有成員變量原封不動地賦值,賦值完畢後,“=”左右兩邊的對象都指向同一塊內存,當其中一個對象先銷燬,另一個對象再銷燬的時候就會造成重複釋放內存的問題,遇到這種情況我們應該自己手動實現一個“=”的重載,爲新對象的指針申請獨立的內存,並把內容拷貝過來。此外,還要注意“c1 = c1”這種寫法,在處理“=”運算符時我們應該先判斷一下“=”左右兩邊的對象是否相等,相等就直接返回原對象的引用。還要注意的是,在重載“=”運算符的時候,如果對象本身帶有指向用new申請的內存的指針成員變量,要注意“被覆蓋的對象 的成員指針內存的銷燬問題”,這句話聽起來好像有點難懂,其實就是當我們寫出c1 = c2這樣的代碼時,c1對象的成員變量裏如果帶有用new申請內存的指針變量的話,要注意先釋放再賦值,否則會導致內存泄露。

 

到此,運算符重載的知識點就總結得差不多了,本文就寫到這裏,感謝您的閱讀,如果文中有什麼錯誤或者難以理解的地方,歡迎各位在評論區留言。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章