C++編程之運算符重載,內含大量示例代碼和相關腦圖

那天,大佬指着review代碼,對我大吼:“那個誰誰誰,兩點位之間相對關係咋這樣計算??GIS是矢量運算,你不知道嗎??按矢量關係,重載運算符,把這些代碼全部重新修改update…”。就這樣,我哼哧哼哧加班,又把運算符重載搞了一遍。這就是本篇文章的靈感來源。苦澀的沙吹痛臉龐的感覺~水手響起

系列文章
C++編程之引用的詳細總結
C++編程之命名空間、const常量


1、爲什麼要運算符重載

何爲運算符重載?在C/C++或其他編程語言中,都會涉及運算,比如加(+)、 減(-)、乘(*)、除(/),不同的運算符在不同數據類型中計算結果不同。比如兩數相加:

int Add(const int a, const int b)
{
	return a + b;
}
// 此函數返回兩個整數的和,使用的是加(+)法運算符。

在C++中,運算符都是需要和相同類型或有父子類關係類型的變量之間運算。

string Add(const int a)
{
	string strName = "Marble";
    string c = strName + a;
    return c;
}
// 以上代碼,在C++編譯器中必定會報錯,編譯器不知道你這是在搞什麼!!!

以上講述,初步引入了運算符重載的需求。即自定義運算符運算規則,讓編譯器知道你這是在幹什麼。如下示例:

class Point
{
public:
	int x;
    int y;
}
// 需求計算兩個點Point之和。

Point Add(Point p1, Point p2)
{
	return p1 + p2;
}
// 上述代碼,編譯器也是一臉懵逼,不知如何處理,只能給你報錯。

在這裏插入圖片描述
以上需求,引入今天的話題“運算符重載”。

2、運算符重載語法格式

類型 類名 :: operator 函數名(參數)
類型           :: operator 函數名(參數)

特別注意:此處的函數名,其實是運算符,指出後續需要計算的符號。運算符重載一般有兩種方式,如上格式,貼出一段簡短的代碼。

Point operator+(const Point &p1, const Point &p2)
{
	Point temp;
	temp.x = p1.x + p2.x;
	temp.y = p1.y + p2.y;
	return temp;
}

3、運算符重載的方式

3.1、成員函數

    讓重載運算符成爲該類的成員函數。這允許運算符函數訪問類的私有成員。它也允許函數使用隱式的this指針來訪問調用對象,這種類型參數個數小於等於1。其語法格式如下:

類型 類名 :: operator 函數名(參數)

// 點類型
class Point
{
public:
	//Point 類的成員函數
	Point operator+(const Point& p1);

private:
	int x;
	int y;
};

Point Point::operator+(const Point &p1)
{
	// 使用隱式的this指針形參來訪問調用對象
	this->x = this->x + p1.x;
	this->y = this->y + p1.y;
	return *this;
}
3.2、友元函數

    讓重載的成員函數成爲獨立分開的函數。當以這種方式重載時,運算符函數必須聲明爲類的友元才能訪問類的私有成員。


class Point
{ 
	// 重載友元函數 全局函數 + 操作符重載
	friend Point operator+(const Point& p1, const Point& p2);

private:
	int x;
	int y;
};
 
 // 全局函數法 實現 + 運算符重載
Point operator+(const Point& p1, const Point& p2)
{
	Point p;
	p.x = p1.x + p2.x;
	p.y = p1.y + p2.y;
	return p;
}

4、哪些運算符可以重載

如下腦圖所示,是可以重載的運算符,後面章節將進行所有可重載運算符實例演示。

在這裏插入圖片描述

5、哪些運算符不可以重載

如下腦圖所示,是不可以重載的運算符,這些運算符是系統預定義的運算符,只有唯一性,不可以重載。

在這裏插入圖片描述

5、運算符重載實例

5.1、雙目運算符重載
class Point
{
public:
	//Point 類的成員函數
    // 加法運算符重載
	Point operator+(const Point& p);
    // 減法運算符重載
	Point operator-(const Point& p);
    // 乘法運算符重載
	Point operator*(const Point& p);
    // 除法運算符重載
	Point operator/(const Point& p);
    // 取模運算符重載
	Point operator%(const Point& p);

private:
	int x;
	int y;
};

// 加法運算符重載
Point Point::operator+(const Point &p1)
{
	this->x = this->x + p1.x;
	this->y = this->y + p1.y;
	return *this;
}
 // 減法運算符重載
Point Point:: operator-(const Point& p)
{
	this->x -= p.x;
	this->y -= p.y;
	return *this;
}

 // 乘法運算符重載
Point Point:: operator*(const Point& p)
{
	this->x *= p.x;
	this->y *= p.y;
	return *this;
}

// 除法運算符重載
Point Point:: operator/(const Point& p)
{
	this->x /= p.x;
	this->y /= p.y;
	return *this;
}

 // 取模運算符重載
Point Point:: operator%(const Point& p)
{
	this->x %= p.x;
	this->y %= p.y;
}
5.2、關係運算符重載
5.2.1、成員函數重載

// 構造點類
class Point
{
public:
     bool operator==(const Point& p);
     bool operator!=(const Point& p);
     bool operator<(const Point& p);
     bool operator>(const Point& p);
     bool operator<=(const Point& p);
     bool operator>=(const Point& p);

private:
     int x;
     int y;
};


bool Point::operator==(const Point& p)
{ 
 	return this->x == p.x && this->y == p.y;
}

bool Point:: operator!=(const Point& p)
{
 	return this->x != p.x && this->y != p.y;
}

bool Point:: operator<(const Point& p)
{
 	return this->x < p.x && this->y < p.y;
}
bool Point:: operator>(const Point& p)
{	
 return this->x > p.x&& this->y > p.y;
}

bool Point:: operator<=(const Point& p)
{
 	return this->x <= p.x && this->y <= p.y;
}

bool Point:: operator>=(const Point& p)
{
     return this->x >= p.x && this->y >= p.y;
}
5.2.2、非成員函數重載

非成員函數重載,如果要訪問類的私有變量,必須爲類的友元,纔可以訪問。類似你想獲取別人隱私,一般要先認識成爲朋友纔可以。如下圖所示:
在這裏插入圖片描述

5.3、邏輯運算符重載

不建議重載邏輯運算符 &&和 ||,原因如下:

#include<iostream>
using namespace std;
class Person
{
public:
	Person(const int age)
	{
		this->age = age;
	}
	int getName()const
	{
		cout << "age is :" << this->age << endl;
		return this->age;
	}
	 
private:
	int age;
};

// 重載&&
bool operator && (const Person& p1, const Person& p2)
{
	return p1.getName() && p2.getName();
}

// 重載||
bool operator || (const Person& p1, const Person& p2)
{
	return p1.getName() || p2.getName();
}
 

int main()
{
	Person p1(-10);
	Person p2(20);
	if (p1 && p2)
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
	system("pause");
	return 0;
}

運行結果
在這裏插入圖片描述
從結果可以看出,並沒有達到我們需要的結果,預期的結果是,如果兩者都爲正數,則爲true,如果有一個爲負數,則爲false。但是運行結果並不是這樣,這就破壞了邏輯運算符原有的短路規則。因此建議不要輕易重載邏輯運算符。

5.4、單目運算符重載
5.4.1 、 +(正)、-(負)運算符重載
class  Point
{
public:
	Point(const int x,const int y)
	{
		this->x = x;
		this->y = y;
	}
	
    // 負值仍然是負值,正值則不變
	Point operator +()
	{
		this->x = +this->x;
		this->y = +this->y;
		return *this;
	}

	// 正值變爲負值,負值變爲正值
	Point operator -()
	{
		this->x = -this->x;
		this->y = -this->y;
		return *this;
	}
	 
private:
	int x;
	int y;

}; 

//測試代碼
void Test()
{
	Point p1(10, 20);
	Point p2 = -p1;
	Point p3 = -p2;
}
5.4.2 、*(指針),&(取地址)重載

模擬智能指針

class  Person
{
public:
	Person(const int age)
	{
		cout << "Person 構造" << endl;
		this->m_age = age;
	}
	~Person()
	{
		cout << "Person析構" << endl;
	}

	int getAge()const
	{
		return this->m_age;
	}

private:
	int m_age;
}; 

class SmartPointor
{
public:
	SmartPointor(const Person* person)
	{
		cout << "Smartpointor 構造" << endl;
		this->p = person;
	}

	~SmartPointor()
	{
		cout << "SmartPointor 析構" << endl;
		if (this->p != NULL)
		{
			delete this->p;
			this->p = NULL;
		}
	}

	Person operator *()
	{
		return *p;
	}

	Person* operator &()
	{
		return p;
	}
private:
	Person* p;
};

void test()
{
	SmartPointor sp = SmartPointor(new Person(10));
    // 下面兩種調用屬於* 和& 重載後的調用結果,感興趣的可以體驗一下。
	(*sp).getAge();
	(&sp)->getAge();
}

int main()
{
	// 普通情況下,new對象後,還需要手動釋放,每次都需要
	/*Person *person = new Person(10);
	delete person;*/

	// 使用智能指針後,就可以避免手動調用delete,避免遺漏導致的內存泄漏
	test();
	
	system("pause");
	return 0;
}
5.5、自增++、自減–運算符重載
class  MyInt
{
public:
	MyInt(const int a)
	{
		this->m_int = a;
	}

	// 前置++ 即 ++a
	MyInt operator ++()
	{
		this->m_int++;
		return *this;
	}

	// 後置++ 即 a++,採用佔位符,請體驗此處的妙用!!!!
	MyInt operator ++(int)
	{
		MyInt temp = *this;
		this->m_int++;
		return temp;
	}

	// 前置-- 即 --a
	MyInt operator --()
	{
		this->m_int--;
		return *this;
	}

	// 後置-- 即 a--
	MyInt operator --(int)
	{
		MyInt temp = *this;
		this->m_int--; // 延後--
		return temp;
	}

private:
	int m_int;
};
5.6、位運算運算符重載
class  MyInt
{
public:
	MyInt(const int a)
	{
		this->m_int = a;
	}

	// 重載|
	MyInt operator |(const MyInt& a)
	{
		return this->m_int | a.m_int;
	}

	// 重載&
	MyInt operator &(const MyInt& a)
	{
		return this->m_int & a.m_int;
	}

	// 重載~
	MyInt operator ~()
	{
		return ~this->m_int;
	}

	// 重載^
	MyInt operator ^(const MyInt& a)
	{
		return this->m_int ^ a.m_int;
	}

	// 重載<<
	MyInt operator <<(const int len)
	{
		return this->m_int << len;
	}

	MyInt operator >>(const int len)
	{
		return this->m_int >> len;
	}

	void print()
	{
		cout << this->m_int << endl;
	}

private:
	int m_int;
};
 
int main()
{
	MyInt p(8);
	MyInt q(5);

	MyInt r1 = p | q;
	r1.print();

	MyInt r2 = p^q;
	r2.print();

	MyInt r3 = p&q;
	r3.print();

	MyInt r4 = ~p;
	r4.print();

	MyInt r5 = p << 3;
	r5.print();

	MyInt r6 = p >> 3;
	r6.print();

	system("pause");
	return 0;
}

運行結果如圖所示:

在這裏插入圖片描述

5.7、賦值運算符重載

class  MyInt
{
public:
	MyInt(const int a)
	{
		this->m_int = a;
	}

	// 重載=,如何實現 a=b=c=d,鏈式編程呢?這個問題留在後面講解。
	MyInt operator =(const MyInt& a)
	{
		return this->m_int = a.m_int;
	}

	// 重載+=
	MyInt operator +=(const MyInt& a)
	{
		return this->m_int += a.m_int;
	}

	// 重載-=
	MyInt operator -=(const MyInt& a)
	{
		return this->m_int -= a.m_int;
	}

	// 重載*=
	MyInt operator *=(const MyInt& a)
	{
		return this->m_int *= a.m_int;
	}

	// 重載/=
	MyInt operator /=(const int len)
	{
		return this->m_int /= len;
	}

	// 重載%=
	MyInt operator %=(const int len)
	{
		return this->m_int %= len;
	}

	// 重載&=
	MyInt operator &=(const int len)
	{
		return this->m_int &= len;
	}

	// 重載|=
	MyInt operator |=(const int len)
	{
		return this->m_int |= len;
	}

	// 重載^=
	MyInt operator ^=(const int len)
	{
		return this->m_int ^= len;
	}
	// 重載<<=
	MyInt operator <<=(const int len)
	{
		return this->m_int = this->m_int << len;
	}
	// 重載>>=
	MyInt operator >>=(const int len)
	{
		return this->m_int = this->m_int >> len;
	}

	void print()
	{
		cout << this->m_int << endl;
	}

private:
	int m_int;
};
5.8、空間申請與釋放運算符重載

重載new,delete運算符,new,delete在c++中也被歸爲運算符,所以可以重載它們。

new的行爲:

  1. 先開闢內存空間
  2. 再調用類的構造函數,開闢內存空間的部分,可以被重載。

delete的行爲:

  1. 先調用類的析構函數
  2. 再釋放內存空間釋放內存空間的部分,可以被重載。

那麼爲什麼要要重載這兩個運算符呢?

比如頻繁的new和delete對象,會造成內存碎片,內存不足等問題,影響程序的正常執行,所以一次開闢一個適當大的空間,作爲內存池,每次需要對象的時候,不再需要去開闢內存空間,只需要調用構造函數(使用placement new)即可。

new,delete的重載函數,可以是全局函數,也可以是類內部的公有重載函數;當既有全局的重載函數,也有類內部的公有重載函數時,實際調用的是類內部的公有重載函數。

new,delete可以有多種重載方式,但是,new函數的第一個參數一定要是size_t類型

// new 單個對象
void* operator new(size_t sz)
{
  void* o = malloc(sz);
  return o;
}
void operator delete(void *o)
{
  free(o);
}
// new對象的數組
void* operator new[](size_t sz)
{
  void* o = malloc(sz);
  return o;
}
void operator delete[](void *o)
{
  free(o);
}
 // 不開闢空間,只是調用給定對象(用地址識別)的構造方法,也叫placement new 
void* operator new(size_t sz, String* s, int pos)
{
  return s + pos;
}
5.9、其他運算符重載

括號運算符一般用於仿函數中

void operator()(const string str)
{
   cout << str ;
}

成員訪問符(->)重載

class  Person
{
public:
	Person(const int age)
	{
		cout << "Person 構造" << endl;
		this->m_age = age;
	}
	~Person()
	{
		cout << "Person析構" << endl;
	}

	int getAge()const
	{
		return this->m_age;
	}

private:
	int m_age;
}; 

class SmartPointor
{
public:
	SmartPointor(const Person* person)
	{
		cout << "Smartpointor 構造" << endl;
		this->p = person;
	}

	~SmartPointor()
	{
		cout << "SmartPointor 析構" << endl;
		if (this->p != NULL)
		{
			delete this->p;
			this->p = NULL;
		}
	}

	// 模擬智能指針,重載->運算符
	Person* operator ->()
	{
		return p;
	}

private:
	Person* p;
};

逗號運算符和[]下標運算符重載,請各位小夥伴們自行實現,具體實現邏輯和前面差不多。找一個應用場景,即可實現。

6、寫在結尾

本文是博主被大佬暴擊後,review學過的知識點,總結的筆記,文中的示例代碼都是經過調試測試,無問題後才貼上去的。由於C++運算符重載應用比較廣泛,因此做筆記於此。博主切身體會到,編程基礎的重要性,以及能靈活使用,更是重中之重。希望本篇文章,對你有幫助。若有助於你,請點贊支持我繼續寫下去;如果您在閱讀中發現有錯誤,請留言或隨時私信我,我第一時間更新修復,萬分感謝。

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