c++: 運算符重載(運算符重載碰上友元函數、自增自減(++/--)運算符重載、賦值(=)運算符重載、等於和不等於(==、!=)運算符重載、重載&&、||)

 

目錄

一、運算符重載基本概念

二、運算符重載碰上友元函數

三、可重載的運算符

四、自增自減(++/--)運算符重載

五、賦值(=)運算符重載

六、等於和不等於(==、!=)運算符重載

七、不要重載&&、||

 八、符號重載總結


 

一、運算符重載基本概念

運算符重載,就是對已有的運算符重新進行定義,賦予其另一種功能,以適應不同的數據類型。

  •     運算符重載(operator overloading)只是一種”語法上的方便”,也就是它只是另一種函數調用的方式。

語法:

  • 定義重載的運算符就像定義函數,只是該函數的名字是operator@,這裏的@代表了被重載的運算符。函數的參數中參數個數取決於兩個因素。
  1. 運算符是一元(一個參數)的還是二元(兩個參數);
  2. 運算符被定義爲全局函數(對於一元是一個參數,對於二元是兩個參數)還是成員函數(對於一元沒有參數,對於二元是一個參數-此時該類的對象用作左耳參數)

 

 

二、運算符重載碰上友元函數

友元函數是一個全局函數,和我們上例寫的全局函數類似,只是友元函數可以訪問某個類私有數據。

案例: 重載左移操作符(<<),使得cout可以輸出對象。

class Person{
	friend ostream& operator<<(ostream& os, Person& person);
public:
	Person(int id,int age){
		mID = id;
		mAge = age;
	}
private:
	int mID;
	int mAge;
};

ostream& operator<<(ostream& os, Person& person){
	os << "ID:" << person.mID << " Age:" << person.mAge;
	return os;
}

int main(){

	Person person(1001, 30);
	//cout << person; //cout.operator+(person)
	cout << person << " | " << endl;

	return EXIT_SUCCESS;
}

 

三、可重載的運算符

幾乎C中所有的運算符都可以重載,但運算符重載的使用時相當受限制的。特別是不能使用C中當前沒有意義的運算符(例如用**求冪)不能改變運算符優先級不能改變運算符的參數個數。這樣的限制有意義,否則,所有這些行爲產生的運算符只會混淆而不是澄清寓語意。

 

四、自增自減(++/--)運算符重載

例如當編譯器看到++a(前置++),它就調用operator++(a),當編譯器看到a++(後置++),它就會去調用operator++(a,int).

class MyInter
{
	friend ostream& operator<<(ostream & cout, MyInter myInt);
public:
	MyInter()
	{
		num = 0;
	} 

	//重載遞增運算符
	//前置
	MyInter& operator++()
	{
		//先++
		num++;
		//後返回自身
		return *this;
	}
	//後置 如果在形參中添加int佔位參數,編譯器可以識別出這是後置++
	MyInter operator++(int)
	{
		//先 記錄原來值
		MyInter temp = *this;
		//再 ++ 
		num++;
		//再將記錄的值返回
		return temp;
	}

private:
	int num;
};

//全局函數重載<<
ostream& operator<<( ostream & cout , MyInter myInt)
{
	cout << myInt.num;
	return cout;
}

//前置遞增
void test01()
{
	MyInter myInt;
	cout << myInt << endl; // 0
	cout << ++(++myInt) << endl; //  2 
	cout << myInt << endl;   // 2
}

//後置遞增
void test02()
{
	MyInter myInt;
	cout << myInt << endl; // 0
	cout << myInt++ << endl; // 0
	cout << myInt << endl; // 1
}


int main(){
    test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}
  • 優先使用++和--的標準形式,優先調用前置++。
  • 如果定義了++c,也要定義c++,遞增操作符比較麻煩,因爲他們都有前綴和後綴形式,而兩種語義略有不同。重載operator++和operator--時應該模仿他們對應的內置操作符。
  • 對於++和--而言,後置形式是先返回,然後對象++或者--,返回的是對象的原值。前置形式,對象先++或--,返回當前對象,返回的是新對象。其標準形式爲:

調用代碼時候,要優先使用前綴形式,除非確實需要後綴形式返回的原值,前綴和後綴形式語義上是等價的,輸入工作量也相當,只是效率經常會略高一些,由於前綴形式少創建了一個臨時對象。

 

五、賦值(=)運算符重載

編譯器會默認給一個類添加4個函數:

  1. 構造函數(空實現)  
  2. 析構函數(空實現)
  3. 拷貝構造(值拷貝)
  4. operator= (值拷貝)

賦值符常常初學者的混淆。這是毫無疑問的,因爲’=’在編程中是最基本的運算符,可以進行賦值操作,也能引起拷貝構造函數的調用。

class Person
{
public:
	Person(){ cout << "Person默認構造函數調用" << endl; }

	Person(const char * name) // "Tom"
	{
		cout << "Person有參構造函數調用" << endl;
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name ,name);
	}

	Person(const Person & p)
	{
		cout << "Person拷貝構造函數調用" << endl;
		this->m_Name = new char[strlen(p.m_Name) + 1];
		strcpy(this->m_Name, p.m_Name);
	}

	//重載 = 運算符
	Person& operator=(const Person & p)
	{
		//先判斷堆區是否有數據,如果有先釋放乾淨
		if (this->m_Name != NULL)
		{
			delete [] this->m_Name;
			this->m_Name = NULL;
		}
		this->m_Name = new char[strlen(p.m_Name) + 1];
		strcpy(this->m_Name, p.m_Name);
		return *this;
	}

	~Person()
	{
		cout << "Person析構函數調用" << endl;
		if (this->m_Name != NULL)
		{
			delete [] this->m_Name;
			this->m_Name = NULL;
		}
	}

	char * m_Name;
};

void test01()
{
	Person p1("Tom");
	Person p2("Jerry");
	Person p3;

	p3 = p1 = p2; //賦值

	cout << "p1的姓名爲: " << p1.m_Name << endl;
	cout << "p2的姓名爲: " << p2.m_Name << endl;
	cout << "p3的姓名爲: " << p3.m_Name << endl;

	Person p4(p3);
}

int main(){
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

 

六、等於和不等於(==、!=)運算符重載

 

class Complex{
public:
	Complex(char* name,int id,int age){
		this->pName = new char[strlen(name) + 1];
		strcpy(this->pName, name);
		this->mID = id;
		this->mAge = age;
	}
	//重載==號操作符
	bool operator==(const Complex& complex){
		if (strcmp(this->pName,complex.pName) == 0 && 
		    this->mID == complex.mID && 
			this->mAge == complex.mAge){
			return true;
		}
		return false;
	}
	//重載!=操作符
	bool operator!=(const Complex& complex){
		if (strcmp(this->pName, complex.pName) != 0 || 
		    this->mID != complex.mID || 
			this->mAge != complex.mAge){
			return true;
		}
		return false;
	}
	~Complex(){
		if (this->pName != NULL){
			delete[] this->pName;
		}
	}
private:
	char* pName;
	int mID;
	int mAge;
};
void test(){
	Complex complex1("aaa", 10, 20);
	Complex complex2("bbb", 10, 20);
	if (complex1 == complex2){ cout << "相等!" << endl; }
	if (complex1 != complex2){ cout << "不相等!" << endl; }
}

 

七、不要重載&&、||

不能重載operator&& 和 operator|| 的原因是:

無法在這兩種情況下實現內置操作符的完整語義。

說得更具體一些,內置版本版本特殊之處在於:內置版本的&&和||首先計算左邊的表達式,如果這完全能夠決定結果,就無需計算右邊的表達式了。而這種計算模式模式我們一般無法實現,所以不能重載。

我們說操作符重載其實是另一種形式的函數調用而已,對於函數調用總是在函數執行之前對所有參數進行求值。

class Complex{
public:
	Complex(int flag){
		this->flag = flag;
	}
	Complex& operator+=(Complex& complex){
		this->flag = this->flag + complex.flag;
		return *this;
	}
	bool operator&&(Complex& complex){
		return this->flag && complex.flag;
	}
public:
	int flag;
};
int main(){

	Complex complex1(0);  //flag 0 
	Complex complex2(1);  //flag 1

	//原來情況,應該從左往右運算,左邊爲假,則退出運算,結果爲假
	//這邊卻是,先運算(complex1+complex2),導致,complex1的flag變爲complex1+complex2的值, complex1.a = 1
	// 1 && 1
	//complex1.operator&&(complex1.operator+=(complex2))
	if (complex1 && (complex1 += complex2)){   
		cout << "真!" << endl;
	}
	else{
		cout << "假!" << endl;
	}

	return EXIT_SUCCESS;
}

 

 八、符號重載總結

  • =, [], ()-> 操作符只能通過成員函數進行重載
  • <<>>只能通過全局函數配合友元函數進行重載
  • 不要重載 &&|| 操作符,因爲無法實現短路規則

常規建議:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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