C++筆記之運算符重載詳解

一.運算符重載簡介

1.運算符重載實質

(1) 對已有的運算符賦予多重含義
(2) 必要性:C++預定義的運算符運算對象只能是基本數據類型,而不適用於用戶自定義類型(如 類)
(3) 實現機制:

  • 將指定的運算符表達式轉化爲對運算符函數的調用,運算對象轉化運算符函數的實參
  • 編譯系統對重載運算符的選擇,遵循函數重載的選擇原則。

2.規則和限制

(1) 可以重載的運算符:
Alt
上述運算符中,[ ] 是下標運算符,()是函數調用運算符,++和 – 是自增自減運算符包括前置後置形式。
另外,有4個運算符不能重載,即 長度運算符 sizeof、條件運算符 ?:、成員選擇符== . == 和域解析運算符== : : == 。
(2) 其他規則

  • 不改變原運算符的優先級和結合性。
  • 不能改變操作數個數。
  • 經重載的運算符,其操作數中至少應該有一個是自定義類型。

二.運算符的重載形式

1.重載爲類成員函數

聲明形式:

函數類型 operator 運算符(形參)
{
	函數體;
}

重載爲友元函數時,參數個數=原操作數個數-1 (後置++、–除外)

(1) 雙目運算符
如果重載運算符B 爲類成員函數,使之能實現表示式oprd1 B oprd2, 其中oprd1位 A類對象,則 B 可被重載爲 A 類的成員函數,形參類型是 oprd2 所屬類型。經重載過,表達式 oprd1 B oprd2 相當於oprd1.operator B(oprd2)

舉例說明:
重載 + 、+=,實現複數類complex 對象想相加。

#include<iostream>    
using namespace std;
class complex
{
public:
	complex(double real = 0.0, double image = 0.0)
	{ this->real = real;this->image = image;}
	complex operator + (complex &c1); // 重載 + 爲成員函數
	void operator += (complex &c1); //重載 +=
	int showComplex(); //輸出複數
private:
	double real;
	double image;
};
complex complex::operator + (complex &c1) //重載 + 爲成員函數的實現
{
  /*complex c;						//實現1:創建一個局部變量
	c.real = c1.real + real;
	c.image = c1.image + image;
	return c;  
	*/
	return complex(c1.real + real, c1.image + image); //實現2:創建臨時變量,直接一句代碼返回結果
}
void complex::operator+=(complex &c1)  //重載 += 
{
	real += c1.real;
	image += c1.image;
}
int complex::showComplex()
{
	cout << real;
	if (image > 0)
		cout << "+";
	if (image != 0)
		cout << image << "i" << endl;
	return 0;
}
int main()
{
	complex c1(1, 2), c2(-3, 4), c3;
	cout << "c1=";c1.showComplex();
	cout << "c2=";c2.showComplex();
	c3 = c1 + c2; //重載+ 的使用,兩邊都爲complex對象
	cout << "c3=c1+c2=";c3.showComplex(); 
	c2 += c1;//重載+= 的使用,兩邊都爲complex對象
	cout << "c2+=c1結果: ";c2.showComplex(); 
	return 0;
}

(2) 前置單目運算符
如果要重載 U 爲類成員函數,使之能夠實現表達式 U oprd,其中 oprd 爲A類對象,則 U 可被重載爲 A 類的成員函數,無形參。經重載後,表達式 U oprd 相當於 oprd.operator U()
(3) 後置單目運算符 ++和–
複習一下++ 、–

示例 解釋
++i i自增1後再參與運算;i的值加1,++i的值也加1
i++ i參與運算後,i的值再加1;i的值加1,i++的值不變

如果要重載 ++或–爲類成員函數,使之能夠實現表達式 oprd++ 或 oprd-- ,其中 oprd 爲A類對象,則 ++或-- 可被重載爲 A 類的成員函數,且具有一個 int 類型形參。經重載後,表達式 oprd++ 相當於oprd.operator ++(0)

舉例說明(1):
++ 實現Point類的座標自增

#include<iostream>    
using namespace std;
class Point
{
private:
	int x, y;
public:
	Point(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
	Point() {}
	Point & operator++();//重載前置單目運算符++爲成員函數    ++i i加1,i++整體值加1
	Point operator++(int);//重載後置單目運算符++成員函數     i++ 只有i加1,i++值不變
	void showPoint();
};
Point & Point::operator++ () //++前置 原來的點變,x,y變
{
	++x;
	++y;
	return *this;
}
Point Point::operator++(int) //後置++  原來的點不變,x,y加1
{
	Point old = (*this); //將舊對象放在新對象裏,末尾返回舊對象
	x++;
	y++;
	return old;
}
void Point::showPoint() {
	cout << "(" << x << "," << y << ")" << endl;
}

int main() {
	Point p1(1, 2), p2(3, 4), p3, p4;
	p3 = ++p1; //++前置
	p3.showPoint();  //(2,3)
	p4 = p2++; //後置++
	p4.showPoint(); //(3,4)
	return 0;
}

舉例說明(2)
++ 實現時間的自增

#include<iostream>    
using namespace std;

class Clock {
private:
	int Hour, Minute, Second;
public:
	Clock(int NewH = 0, int NewM = 0, int NewS = 0);
	void ShowTime();
	Clock operator ++();// 前置單目運算符重載
	Clock operator++(int);//後置單目運算符重載
};
Clock::Clock(int NewH, int NewM, int NewS) {
	Hour = NewH;
	Minute = NewM;
	Second = NewS;
}
Clock Clock::operator++() // 前置單目運算符重載
{
	Second++;
	if (Second >= 60)
	{
		Second = Second - 60;
		Minute++;
		if (Minute >= 60)
		{
			Minute = Minute - 60;
			Hour++;
			Hour = Hour % 24;
		}
	}
	cout << "++Clock:";
	return *this;
}
Clock Clock::operator++(int)  // 後置單目運算符重載
{
	Clock old = *this;
	Second++;
	if (Second >= 60)
	{
		Second = Second - 60;
		Minute++;
		if (Minute >= 60)
		{
			Minute = Minute - 60;
			Hour++;
			Hour = Hour % 24;
		}
	}
	cout << "Clock++:";
	return old;
}
void Clock::ShowTime() 
{
	cout << Hour << ":" << Minute << ":" << Second << endl;
}

int main() {
	Clock myclock(23, 59, 59);
	cout << "First clock:";
	myclock.ShowTime();
	(++myclock).ShowTime();
	(myclock++).ShowTime();
	return 0;
}

2.重載爲友元函數

聲明形式:

friend 函數類型  operator 運算符(形參)
{
       函數體;
}

重載爲友元函數時 , 參數個數=原操作數個數,且至少應該有一個自定義類型的形參。

(1) 運算符友元函數的設計

  • 如果需要重載一個運算符,使之能夠用於操作某類對象的私有成員,可以此將運算符重載爲該類的友元函數。
  • 函數的形參 代表 依自左至右次序排列的各操作數。
  • 後置單目運算符 ++和–的重載函數形參列表中要增加一個int,但不必寫形參名

雙目運算符 B 重載後,表達式:oprd1 B oprd2 等同於:operator B(oprd1,oprd2)
前置單目運算符 B重載後,表達式:B oprd 等同於:operator B(oprd)
後置單目運算符 ++和–重載後,表達式:oprd B 等同於:operator B(oprd,0)

舉例說明(1):
+、-、=、+=重載爲友元函數 ,以complex類舉例

#include<iostream>
using namespace std;
class complex	//複數類聲明
{
public:	     
	complex(double real = 0.0, double image = 0.0) { this->real = real; this->image = image; }	//構造函數
	friend complex operator + (complex c1, complex c2); //運算符+ 重載爲友元函數
	friend complex operator - (complex c1, complex c2); //運算符- 重載爲友元函數
	friend bool operator==(const complex &c1, const complex &c2);
	friend complex operator += (complex &c1,complex &c2); //運算符+= 重載爲友元函數
	void showComplex();	//顯示覆數的值
private:	
	double real;
	double image;
};
complex operator+(complex c1, complex c2)
{
	return complex(c2.real + c1.real, c2.image + c1.image);
}
complex operator-(complex c1, complex c2)
{
	return complex(c1.real - c2.real, c1.image - c2.image);
}
bool operator==(const complex &c1, const complex &c2) {
    if (c1.real == c2.real && c1.image == c2.image) {
        return true;
    } else {
        return false;
    }
}
complex operator+=(complex &c1, complex &c2)
{
	c1.real += c2.real;
	c1.image += c2.image;
}
void complex::showComplex()
{
	cout << real;
	if (image > 0)
		cout << "+";
	if (image != 0)
		cout << image << "i" << endl;
}
int main()
{
	complex c1(1, 2), c2(-3, 4), c3, c4;
	cout << "c1=";c1.showComplex();
	cout << "c2=";c2.showComplex();
	c3 = c1 + c2;
	cout << "c3=c1+c2=";c3.showComplex();
	c4 = c1 - c3;
	cout << "c4=c1-c3=";c4.showComplex();
	if(c4==c1){
		cout << "c4==c1"<<endl;
	}
	return 0;
}

舉例說明(2):
c1+c2, c1+i, i+c1

#include <iostream>
using namespace std;
class complex
{
public:
	complex(double real = 0.0, double image = 0.0)
	{this->real = real;this->image = image;}
	void show();
	friend complex operator + (complex&c1,  complex&c2);
	friend complex operator + (complex&c1, int c2);
	friend complex operator + (int c1, complex&c2);
private:
	double real;
	double image;
};
complex operator + (complex&c1, complex&c2)
{
	complex c;
	c.real = c1.real + c2.real;
	c.image = c1.image + c2.image;
	return c;
}
complex operator + (complex&c1, int c2)
{
	complex c;
	c.real = c1.real + c2;
	c.image = c1.image;
	return c;
}
complex operator + (int c1, complex&c2)
{
	complex c;
	c.real = c2.real + c1;
	c.image = c2.image;
	return c;
}
void complex::show()
{
	cout << "(" << real << " + " << image << "i" << ")"<<endl;
}
int main()
{
	complex c1(1, 2),c2(3,4),c3,c4,c5;
	int i = 2;
	cout << "i=" << i << endl;
	cout << "c1=";c1.show();
	cout << "c2=";c2.show();
	c3 = c1 + c2;
	cout << "c3=c1+c2=";c3.show();
	c4 = c1+i;
	cout << "c4=c1+i=";c4.show();
	c5 = i + c1;
	cout << "c5=i+c1=";c5.show();
	return 0;
}

3.特殊介紹

(1) 下標運算符[ ] 重載爲成員函數
該重載函數在類中的聲明格式:

返回值類型 & operator[](參數);
const 返回值類型 & operator[](參數) const;

使用第一種聲明方式,[ ]不僅可以訪問元素,還可以修改元素;
使用第二種聲明方式,[ ]只能訪問而不能修改元素;

在實際開發中,我們應該同時提供以上兩種形式,這樣做是爲了適應 const 對象,因爲通過 const 對象只能調用 const 成員函數,如果不提供第二種形式,那麼將無法訪問 const 對象的任何元素;

#include<iostream> 
#include<cstring> 
using namespace std;

class MyString {
public:
	MyString();//默認構造函數
	MyString(const char*const); //構造函數
	MyString(const MyString&);//複製構造函數
	~MyString();//析構函數
	char & operator[](int n); //成員函數重載[],該形式可修改可訪問元素
	char operator[](int n)const;//常成員函數重載[],用於const對象調用
	int getLen()const {	return length;}
	const char *getMyString()const {return myString;}
private:
	char *myString;
	int length;
};
MyString::MyString() { //默認構造函數
	myString = new char[1];
	myString[0] = '\0';
	length = 0;
}
MyString::MyString(const char *const str) { //構造函數
	length = strlen(str);
	myString = new char[length + 1];
	for (int i = 0;i < length;i++)
		myString[i] = str[i];
	myString[length] = '\0';
}
MyString::MyString(const MyString &str) { //複製構造函數
	length = str.getLen();
	myString = new char[length + 1];
	for (int i = 0;i < length;i++)
		myString[i] = str[i];
	myString[length] = '\0';
}
MyString::~MyString() { //析構函數
	delete[]myString;
	length = 0;
}
 
char & MyString::operator [](int n) { //下標運算符[]重載
	if (n > length | n < 0)
		cout << "數組下標越界!" << endl;
	else
		return myString[n];
}
char MyString::operator [](int n)const
{
	if (n > length | n < 0)
		cout << "數組下標越界!" << endl;
	else
		return myString[n];
}
int main() {
	MyString str1("xiaobencong  zhen shuai!");
	cout << "str1:     " << str1.getMyString() << endl;
	cout << "str1[4]:  " << str1[4] << endl;
	cout << "str1[-1]: " << str1[-1] << endl;
	cout << "str1[99]:" << str1[999] << endl;
	return 0;
}

(2) 關於 什麼時候用友元函數和成員函數,大家可以閱讀這篇文章
C++ 運算符重載

歡迎關注微信公衆號:學編程的金融客,作者:小笨聰

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