C++運(yùn)算符重載的實(shí)現(xiàn)示例
1. 運(yùn)算符重載的基本概念
運(yùn)算符重載是C++一項(xiàng)強(qiáng)大的特性,它允許我們?yōu)樽远x類(lèi)型(類(lèi)或結(jié)構(gòu)體)重新定義運(yùn)算符的行為。通過(guò)運(yùn)算符重載,我們可以讓自定義類(lèi)型像內(nèi)置類(lèi)型一樣使用標(biāo)準(zhǔn)的運(yùn)算符語(yǔ)法,使代碼更加直觀和自然
從本質(zhì)上講,運(yùn)算符重載是函數(shù)重載的一種特殊形式。當(dāng)我們重載一個(gè)運(yùn)算符時(shí),實(shí)際上是在定義一個(gè)特殊的成員函數(shù)或全局函數(shù),函數(shù)名由operator關(guān)鍵字后接要重載的運(yùn)算符組成
a + b; // 等價(jià)于 a.operator+(b) 或 operator+(a, b) a == b; // 等價(jià)于 a.operator==(b) 或 operator==(a, b)
運(yùn)算符重載提供了語(yǔ)法糖,讓代碼更加直觀和易讀。比較以下兩種寫(xiě)法:
// 使用運(yùn)算符重載 list1 + list2; // 不使用運(yùn)算符重載 list1.concat(list2);
第二種寫(xiě)法顯然更加符合直覺(jué),讓自定義類(lèi)型與內(nèi)置類(lèi)型有一致的操作方式
2. 算術(shù)運(yùn)算符重載
2.1 加法運(yùn)算符重載
在代碼中,復(fù)數(shù)類(lèi)的加法運(yùn)算符重載展示了運(yùn)算符重載的基本用法:
Complex operator+(Complex& a, Complex& b)
{
Complex ret;
ret.real = a.real + b.real;
ret.image = a.image + b.image;
return ret;
}運(yùn)算符重載有兩種實(shí)現(xiàn)方式:?成員函數(shù)形式和全局函數(shù)形式?
?成員函數(shù)形式隱含一個(gè)this指針參數(shù),只需要一個(gè)顯式參數(shù):
class Complex {
public:
Complex operator+(const Complex& other) {
Complex ret;
ret.real = this->real + other.real;
ret.image = this->image + other.image;
return ret;
}
};?全局函數(shù)形式需要兩個(gè)顯式參數(shù),通常需要聲明為類(lèi)的友元函數(shù)以訪問(wèn)私有成員
class Complex {
friend Complex operator+(const Complex& a, const Complex& b);
// ...
};代碼中使用了全局函數(shù)形式,并將它們聲明為友元函數(shù),這樣可以訪問(wèn)Complex類(lèi)的私有成員real和image。
2.2 減法運(yùn)算符重載
減法運(yùn)算符的重載與加法類(lèi)似,只需改變運(yùn)算邏輯:
Complex operator-(Complex& a, Complex& b)
{
Complex ret;
ret.real = a.real - b.real;
ret.image = a.image - b.image;
return ret;
}3. 流運(yùn)算符重載
3.1 輸出運(yùn)算符<<重載
輸出運(yùn)算符<<的重載需要特別注意返回類(lèi)型和參數(shù)類(lèi)型。代碼中實(shí)現(xiàn)了這一功能:
ostream& operator<<(ostream& cout, Complex& other)
{
cout << other.real << "+" << other.image << "i";
return cout;
}這里有幾個(gè)關(guān)鍵點(diǎn):
- ?返回類(lèi)型必須是
ostream&(引用),這樣才能支持鏈?zhǔn)捷敵鋈?code>cout << a << b << c; - ?第一個(gè)參數(shù)是
ostream&類(lèi)型,通常是cout或其變體 - ?第二個(gè)參數(shù)是要輸出的對(duì)象
- 函數(shù)返回輸出流對(duì)象本身,使鏈?zhǔn)讲僮鞒蔀榭赡?/span>
如果返回void類(lèi)型,將無(wú)法實(shí)現(xiàn)鏈?zhǔn)捷敵?。這是因?yàn)?code>cout << c << endl會(huì)被解析為(cout << c) << endl,如果cout << c返回void,那么void << endl就是非法的。
3.2 避免不必要的拷貝
使用const Complex& other而不是Complex other的好處:避免拷貝構(gòu)造。當(dāng)對(duì)象較大時(shí),傳引用可以顯著提高性能。正確的聲明應(yīng)該是:
ostream& operator<<(ostream& cout, const Complex& other);
這里的const確保不會(huì)意外修改對(duì)象狀態(tài)。
4. 自增運(yùn)算符重載
自增運(yùn)算符有前置和后置兩種形式,需要分別處理。
4.1 前置自增運(yùn)算符
Complex& operator++()//前置++
{
this->real += 1;
return *this;
}前置++返回引用,這是為了保持與內(nèi)置類(lèi)型一致的行為。這樣++a本身可以作為左值使用
4.2 后置自增運(yùn)算符
Complex operator++(int)//后置++
{
Complex a = *this;//保存原始值
this->real += 1;
return a;//返回原始值
}后置++通過(guò)int參數(shù)與前置版本區(qū)分,這個(gè)參數(shù)僅用于區(qū)分,并不實(shí)際使用。它返回的是值而不是引用,因?yàn)榉祷氐氖蔷植繉?duì)象,不能返回引用。
注釋中提到的,后置++返回臨時(shí)對(duì)象,這個(gè)對(duì)象在函數(shù)結(jié)束后會(huì)被銷(xiāo)毀,所以不能返回引用。
5. 賦值運(yùn)算符重載
賦值運(yùn)算符重載需要特別注意深拷貝和自賦值問(wèn)題。
Jeff& operator=(Jeff &a)
{
if (age)
{
delete age;
age = NULL;
}
age = new int;//分配新內(nèi)存
*age = *a.age;
return *this;
}這里有幾個(gè)重要考慮:
- ?檢查自賦值?:雖然代碼中沒(méi)有顯式檢查,但
a = a這樣的自賦值應(yīng)該安全處理 - ?釋放舊資源?:在分配新資源前釋放已有資源,防止內(nèi)存泄漏
- ?深拷貝?:創(chuàng)建新內(nèi)存并復(fù)制內(nèi)容,而不是簡(jiǎn)單復(fù)制指針
- ?返回引用?:支持鏈?zhǔn)劫x值
a = b = c
改進(jìn)版本應(yīng)該包含自賦值檢查:
Jeff& operator=(const Jeff &a)
{
if (this != &a) { // 自賦值檢查
if (age) {
delete age;
}
age = new int(*a.age);
}
return *this;
}6. 關(guān)系運(yùn)算符重載
關(guān)系運(yùn)算符重載通常返回bool值,用于比較對(duì)象。
bool operator==(const Point& a)const
{
return this->x == a.x && this->y == a.y;
}
bool operator<(const Point& a)const
{
int c = x * x + y * y;
int d = a.x * a.x + a.y * a.y;
return c < d;
}代碼中通過(guò)比較點(diǎn)到原點(diǎn)的距離來(lái)定義<運(yùn)算符,這是一種常見(jiàn)的做法。注意這些函數(shù)被聲明為const,因?yàn)樗鼈儾粦?yīng)該修改對(duì)象狀態(tài)。
7. 函數(shù)調(diào)用運(yùn)算符重載
函數(shù)調(diào)用運(yùn)算符()的重載創(chuàng)建了所謂的仿函數(shù)?(functor)。
int operator()(int a, int b)
{
data++;
return a + b + data;
}仿函數(shù)比普通函數(shù)更靈活,因?yàn)樗鼈兛梢员3譅顟B(tài)。如你的示例所示,每次調(diào)用都會(huì)增加data的值,這是普通函數(shù)無(wú)法做到的。
仿函數(shù)可以記錄調(diào)用過(guò)程中的狀態(tài),比普通函數(shù)更加靈活,常用于STL算法中的定制行為
8. 運(yùn)算符重載的規(guī)則與最佳實(shí)踐
8.1 可重載的運(yùn)算符
C++允許重載大部分運(yùn)算符,包括:
- 算術(shù)運(yùn)算符:
+,-,*,/,% - 關(guān)系運(yùn)算符:
==,!=,<,>,<=,>= - 邏輯運(yùn)算符:
&&,||,! - 賦值運(yùn)算符:
=,+=,-=,*=,/= - 下標(biāo)運(yùn)算符:
[] - 函數(shù)調(diào)用運(yùn)算符:
() - 流運(yùn)算符:
<<,>> - 自增自減:
++,--
8.2 不可重載的運(yùn)算符
有些運(yùn)算符不能重載,包括:
- 成員訪問(wèn)運(yùn)算符:
. - 成員指針訪問(wèn)運(yùn)算符:
.* - 作用域解析運(yùn)算符:
:: - 條件運(yùn)算符:
?:(三目運(yùn)算符) sizeof運(yùn)算符typeid運(yùn)算符
8.3 最佳實(shí)踐
- ?保持語(yǔ)義一致性?:重載的運(yùn)算符應(yīng)該保持與內(nèi)置類(lèi)型相似的語(yǔ)義
- ?考慮返回值類(lèi)型?:
- 算術(shù)運(yùn)算符通常返回新對(duì)象
- 復(fù)合賦值運(yùn)算符通常返回引用
- 關(guān)系運(yùn)算符返回bool
- ?正確處理常量性?:不修改對(duì)象的函數(shù)應(yīng)聲明為
const - ?遵循三/五法則?:如果定義了拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符、析構(gòu)函數(shù)中的一個(gè),通常需要定義其他相關(guān)函數(shù)
9. 總結(jié)
運(yùn)算符重載是C++面向?qū)ο缶幊痰闹匾匦?,它讓自定義類(lèi)型能夠以更自然的方式集成到語(yǔ)言中。通過(guò)合理使用運(yùn)算符重載,我們可以編寫(xiě)出更加直觀、易維護(hù)的代碼。
關(guān)鍵要點(diǎn):
- 運(yùn)算符重載的本質(zhì)是函數(shù)重載,遵循函數(shù)重載的規(guī)則
- 選擇成員函數(shù)還是全局函數(shù)形式取決于具體需求
- 流運(yùn)算符
<<和>>通常重載為全局友元函數(shù) - 賦值運(yùn)算符需要處理自我賦值和深拷貝問(wèn)題
- 前置和后置自增/自減運(yùn)算符通過(guò)參數(shù)區(qū)分
- 函數(shù)調(diào)用運(yùn)算符重載創(chuàng)建仿函數(shù),可以保持狀態(tài)
合理使用運(yùn)算符重載可以極大提高代碼的可讀性和易用性,但也要避免濫用,保持運(yùn)算符的直觀語(yǔ)義。當(dāng)你面對(duì)自定義類(lèi)型需要類(lèi)似內(nèi)置類(lèi)型的操作時(shí),運(yùn)算符重載是一個(gè)強(qiáng)大的工具。
完整代碼(雜糅版選取復(fù)制):
#include<iostream>
using namespace std;
#include<string>
int main()
{
//1.加法運(yùn)算符
int a = 520;
int b = 1314;
cout << a + b<<endl;
//2.字符串拼接
string c = "520";
string d = "1314";
cout << c + d << endl;
}
復(fù)數(shù)類(lèi):
class Complex
{
friend ostream& operator<<(ostream& cout, Complex& other);
friend Complex operator+(Complex& a, Complex& b);
friend Complex operator-(Complex& a, Complex& b);
public:
Complex() :real(0), image(0)
{
}
Complex (int real, int image)
{
this->real = real;
this->image = image;
}
//Complex operator+(Complex& other)//加了operator后可以進(jìn)行a+b操作,這個(gè)是成員函數(shù)形式
//{
// Complex ret;
// ret.real = this->real + other.real;//this是指針用->表示,other是引用對(duì)象用.表示
// ret.image = this->image + other.image;
// return ret;
//}
void print()
{
cout << real << "+" << image<<"i" << endl;//cout傳進(jìn)來(lái)了
}
private:
int real;
int image;
};
Complex operator+(Complex& a,Complex&b)//加了operator后可以進(jìn)行a+b操作,全員函數(shù)重載+改-就變-了
{
Complex ret;
ret.real = a.real+b.real;//this是指針用->表示,other是引用對(duì)象用.表示
ret.image = a.image+b.image;
return ret;
}
Complex operator-(Complex& a, Complex& b)//加了operator后可以進(jìn)行a+b操作,全員函數(shù)重載+改-就變-了
{
Complex ret;
ret.real = a.real - b.real;//this是指針用->表示,other是引用對(duì)象用.表示
ret.image = a.image - b.image;
return ret;
}
//void operator<<(ostream& cout, Complex& other)//void 不行
//{
// cout << other.real << "+" << other.image<<"i" << endl;
//}
ostream& operator<<(ostream& cout, Complex& other)//我左移后需要不斷的輸出所以要返回ostream可以繼續(xù)加,實(shí)現(xiàn)鏈?zhǔn)捷敵觯。。?
{
cout << other.real << "+" << other.image << "i";
return cout;
}//補(bǔ)充知識(shí)點(diǎn)為什么ostream要加&,因?yàn)閏out是ostream類(lèi)型的函數(shù),全局只有一個(gè)并且不希望外部調(diào)用也不希望內(nèi)部調(diào)用,調(diào)用的話會(huì)自動(dòng)刪除!?。】梢杂胒12看拷貝那一塊的源碼
//去掉&后會(huì)自動(dòng)調(diào)用拷貝函數(shù),然后拷貝函數(shù)就會(huì)被刪除就無(wú)法調(diào)用了!?。?
int main()
{
Complex a(10,20);
Complex b(5,8);
Complex c = a + b; //a.add(b);
Complex d = a - b;
//c.print();
//d.print();
////左移函數(shù)重載
cout << c << endl;//因?yàn)樽笠坪瘮?shù)沒(méi)有返回值void類(lèi)型,我們要返回ostream這個(gè)類(lèi)型才可以繼續(xù)進(jìn)行返回操作
return 0;
}
函數(shù)的遞增
class Complex
{
friend ostream& operator<<(ostream& cout, const Complex &other);//Complex other每次都會(huì)走拷貝構(gòu)造,已知這個(gè)不能修改,我們加一個(gè)&就可以避免拷貝構(gòu)造了
friend Complex operator+(Complex& a, Complex& b);
friend Complex operator-(Complex& a, Complex& b);
private:
int real;
int image;
public:
Complex() :real(0), image(0)
{
}
Complex(int real, int image)
{
this->real = real;
this->image = image;
}
Complex& operator++()//前置++,不加引用就會(huì)生成新的對(duì)象,加了之后就是指向自己的
{
this->real += 1;
return *this ;
}
Complex operator++(int)//后置++,加引用就會(huì)返回臨時(shí)的變量但是我,不想要這個(gè)臨時(shí)的我要本身的,所以不加&讓編譯器自己生成一個(gè)拷貝函數(shù)
{
Complex a = *this;//等于對(duì)象本身,臨時(shí)的變量會(huì)被析構(gòu)掉所以會(huì)返回隨機(jī)值
this->real += 1;
return a;//返回原來(lái)的對(duì)象
}
};
ostream& operator<<(ostream& c,const Complex& a)
{
cout << a.real << "+" << a.image << "i";
return c;
}
int main()
{
int x = 1;
cout << ++x<<endl;
cout << ++x<<endl;
Complex a(10, 20);
/*++a;*/
//cout << ++a << endl;
//cout << ++(++a) << endl;
//cout << a << endl;//第一++成功了,再調(diào)用一次就會(huì)生成新的對(duì)象與原來(lái)的a沒(méi)有關(guān)系了所以要加引用確定原本對(duì)象;
cout << a++ << endl;
cout << (a++)++ << endl;
cout << a << endl;//加了&后這邊就會(huì)報(bào)錯(cuò)為什么因?yàn)閍的返回值是一個(gè)可修改的左值,所以我們可以用const去修飾這個(gè)返回值,這樣就可以避免這樣的事情發(fā)生,這樣也是規(guī)范的寫(xiě)法
}
賦值重載運(yùn)算
class Jeff
{
public:
Jeff():age(NULL)
{
};
Jeff(int age)
{
this->age = new int;
*(this->age) = age;
}
int *age;
~Jeff()
{
if (age != NULL)
{
delete age;
age = NULL;
}
}
Jeff& operator=(Jeff &a)
{
if (age)
{
delete age;
age = NULL;
}
age = new int;//新的內(nèi)存,析構(gòu)不會(huì)析構(gòu)到這一快
*age = *a.age;
return *this;
}
};
int main()
{
Jeff a(1);
Jeff b(2);
cout << *(a.age) << endl;//內(nèi)存泄漏
cout << *(b.age) << endl;
a = b;//a b兩個(gè)對(duì)象本來(lái)有那個(gè)內(nèi)存的,第一次a調(diào)用時(shí)析構(gòu)函數(shù)把a(bǔ)的對(duì)象銷(xiāo)毀了,第二次b調(diào)用時(shí)由于兩個(gè)都指向同一塊地址,b調(diào)用完成后又一次析構(gòu),這時(shí)就造成二次析構(gòu)了
cout << *(a.age) << endl;
cout << *(b.age) << endl;
Jeff c(3);
a = b = c;
return 0;
}
關(guān)系運(yùn)算符重載
class Point
{
int x, y;
public:
Point():x(0), y(0)
{
};
Point(int x, int y)
{
this->x = x;
this->y = y;
}
bool operator==(const Point& a)const
{
return this->x == a.x && this->y == a.y;
}
bool operator<(const Point& a)const
{
int c = x * x + y * y;
int d = a.x * a.x + a.y * a.y;
return c < d;
}
bool operator>(const Point& a)const
{
if (*this==a)
{
return false;
}
else if (*this < a)
{
return false;
}
else {
return true;
}
}
};
int main()
{
Point a(2,6);
Point b(1,8);
if (a > b)
{
cout << "a到原點(diǎn)的距離大于b" << endl;
}
else if (a == b)
{
cout << "a到原點(diǎn)的距離等于b" << endl;
}
else {
cout << "a到原點(diǎn)的距離小于b" << endl;
}
return 0;
}
函數(shù)調(diào)用運(yùn)算符重載
class HJM
{
private:
int data;
public:
HJM() :data(0)
{
};
int operator()(int a, int b)
{
data++;
return a + b + data;
};//仿函數(shù)調(diào)用可以記錄當(dāng)前的狀態(tài)
};
int Add(int a, int b)
{
return a + b;
}
int main()
{
HJM add;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << Add(10, 5)<< endl;
cout << Add(10, 5) << endl;
cout << Add(10, 5) << endl;
cout << Add(10, 5) << endl;
cout << Add(10, 5) << endl;
return 0;
}到此這篇關(guān)于C++運(yùn)算符重載的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)C++運(yùn)算符重載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++數(shù)據(jù)結(jié)構(gòu)與算法之哈夫曼樹(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了C++數(shù)據(jù)結(jié)構(gòu)與算法之哈夫曼樹(shù)的實(shí)現(xiàn)方法,簡(jiǎn)單說(shuō)明了哈夫曼樹(shù)的原理,并結(jié)合具體實(shí)例形式分析了C++實(shí)現(xiàn)哈夫曼樹(shù)的相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
OpenCV提取圖像中圓線上的數(shù)據(jù)具體流程
在對(duì)圖像進(jìn)行處理時(shí),經(jīng)常會(huì)要提取出圖像中某條直線、圓線或者ROI區(qū)域內(nèi)的感興趣數(shù)據(jù),進(jìn)行重點(diǎn)關(guān)注。本文主要介紹了利用OpenCV獲取圖像中圓線上的數(shù)據(jù),需要的可以參考一下2021-11-11
C++實(shí)現(xiàn)掃雷程序開(kāi)發(fā)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)掃雷程序開(kāi)發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
C++實(shí)現(xiàn)LeetCode(52.N皇后問(wèn)題之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(52.N皇后問(wèn)題之二),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語(yǔ)言實(shí)現(xiàn)萬(wàn)年歷源碼
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)萬(wàn)年歷源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10

