那天,大佬指着review代碼,對我大吼:“那個誰誰誰,兩點位之間相對關係咋這樣計算??GIS是矢量運算,你不知道嗎??按矢量關係,重載運算符,把這些代碼全部重新修改update…”。就這樣,我哼哧哼哧加班,又把運算符重載搞了一遍。這就是本篇文章的靈感來源。苦澀的沙吹痛臉龐的感覺~水手響起
文章目錄
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的行爲:
- 先開闢內存空間
- 再調用類的構造函數,開闢內存空間的部分,可以被重載。
delete的行爲:
- 先調用類的析構函數
- 再釋放內存空間釋放內存空間的部分,可以被重載。
那麼爲什麼要要重載這兩個運算符呢?
比如頻繁的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++運算符重載應用比較廣泛,因此做筆記於此。博主切身體會到,編程基礎的重要性,以及能靈活使用,更是重中之重。希望本篇文章,對你有幫助。若有助於你,請點贊支持我繼續寫下去;如果您在閱讀中發現有錯誤,請留言或隨時私信我,我第一時間更新修復,萬分感謝。