一.運(yùn)算符重載基礎(chǔ)知識(shí)
C++的一大特性就是重載,重載使得程序更加簡(jiǎn)潔高效。在C++中不只函數(shù)可以重載,運(yùn)算符也可以重載,運(yùn)算符重載主要是面向?qū)ο笾g的。
①基本概念
運(yùn)算符重載,就是對(duì)已有的運(yùn)算符重新進(jìn)行定義,賦予其另一種功能,以適應(yīng)不同的數(shù)據(jù)類型。
//對(duì)于基礎(chǔ)數(shù)據(jù)類型,運(yùn)算符可以有很好的效果
int a=20,b=20;
int c=a+b;
//對(duì)于自定義數(shù)據(jù)類型類創(chuàng)建的對(duì)象運(yùn)算,是十分繁瑣的
//例如:
class Maker
{
public:
//有參構(gòu)造函數(shù)
Maker(int id, int age)
{
this->id = id;
this->age = age;
}
public:
int id;
int age;
};
void test()
{
Maker m1(1,19);
Maker m2(2,20);
Maker m3(3,17);
m3=m1+m2;//err
//如果我們想要m1和m2相加賦值給m3,我們可以這樣寫
//m3.id=m1.id+m2.id;
//m3.age=m1.age+m2.age;
//這樣寫太繁瑣,如果對(duì)象內(nèi)部有很多成員變量,而且還是私有怎么辦?
//C++用運(yùn)算符重載來(lái)解決問(wèn)題
}
運(yùn)算符重載語(yǔ)法:
在C++中,使用operator關(guān)鍵字定義運(yùn)算符重載。運(yùn)算符重載語(yǔ)法格式如下:
函數(shù)的參數(shù)列表中參數(shù)個(gè)數(shù)取決于兩個(gè)因素。
運(yùn)算符是單目(一個(gè)參數(shù))的雙目(兩個(gè)參數(shù));
??運(yùn)算符被定義為全局函數(shù),對(duì)于單目運(yùn)算符是一個(gè)參數(shù),對(duì)于雙目運(yùn)算符是兩個(gè)參數(shù)。
??被定義為類的成員函數(shù),對(duì)于單目運(yùn)算符沒(méi)有參數(shù),對(duì)于雙目運(yùn)算符是一個(gè)參數(shù)(因?yàn)轭惐旧頌樽髠?cè)參數(shù)(this))。
這種定義很像一個(gè)普通的函數(shù)定義,只是函數(shù)的名字由關(guān)鍵字operator及其緊跟的運(yùn)算符組成。差別僅此而已。它像任何其他函數(shù)一樣也是一個(gè)函數(shù),當(dāng)編譯器遇到適當(dāng)?shù)哪J綍r(shí),就會(huì)調(diào)用這個(gè)函數(shù)。
通過(guò)案例兩個(gè)對(duì)象相加演示“+”運(yùn)算符的重載:
class Maker
{
public:
Maker(int id, int age)
{
this->id = id;
this->age = age;
}
public:
int id;
int age;
};
Maker operator+(Maker &p1,Maker &p2)//2.編譯器檢查參數(shù)是否對(duì)應(yīng),第一個(gè)參數(shù)是加號(hào)的左邊,第二參數(shù)是加號(hào)的右邊
{
Maker temp(p1.id + p2.id, p1.age + p2.age);
return temp;
}
void test()
{
Maker m1(1, 20);
Maker m2(2, 22);
Maker m3=m1 + m2;//1.編譯器看到兩個(gè)對(duì)象相加,那么編譯器會(huì)去找有沒(méi)有叫operator+的函數(shù)
cout << "id:" << m3.id << " age:" << m3.age << endl;
//多個(gè)對(duì)象相加
Maker m4 = m1 + m2 + m3;
cout << "id:" << m4.id << " age:" << m4.age << endl;
}
通過(guò)上面案例可以知道,重載運(yùn)算符并沒(méi)有改變其原來(lái)的功能,只是增加了針對(duì)自定義數(shù)據(jù)類型的運(yùn)算功能,具有了更廣泛的多態(tài)特征。
②運(yùn)算符重載的規(guī)則
- 只能重載C++中已有的運(yùn)算符,且不能創(chuàng)建新的運(yùn)算符。例如,一個(gè)數(shù)的冪運(yùn)算,試圖重載“* *”為冪運(yùn)算符,使用2 ** 4表示2^4是不可行的。
- 重載后運(yùn)算符不能改變優(yōu)先級(jí)和結(jié)合性,也不能改變操作數(shù)和語(yǔ)法結(jié)構(gòu)。
- 運(yùn)算符重載的目的是針對(duì)實(shí)際運(yùn)算數(shù)據(jù)類型的需要,重載要保持原有運(yùn)算符的語(yǔ)義,且要避免沒(méi)有目的地使用運(yùn)算符重載。例如,運(yùn)算符“+”重載后實(shí)現(xiàn)相加的功能,而不會(huì)重載“+”為相減或者其他功能。
- 并非所有C++運(yùn)算符都可以重載,可以重載的運(yùn)算符如下圖所示。其他運(yùn)算符是不可以重載的,如“::”、“.”、“.*”、“?:”、sizeof、typeid等。
③運(yùn)算符重載形式
運(yùn)算符重載一般有三種形式:
- 重載為普通全局函數(shù)(不推薦,因?yàn)橹剌d運(yùn)算符為普通函數(shù),只能訪問(wèn)類的公有成員)
- 重載為類的成員函數(shù)
- 將重載后的全局函數(shù)聲明為類的友元函數(shù)
④運(yùn)算符重載建議
- ?下面的運(yùn)算符只能通過(guò)類的成員函數(shù)進(jìn)行重載。
=:賦值運(yùn)算符
[]:下標(biāo)運(yùn)算符
() :函數(shù)調(diào)用運(yùn)算符
->:通過(guò)指針訪問(wèn)類成員的運(yùn)算符。
- ?<< 和 >> 操作符最好通過(guò)類的友元函數(shù)進(jìn)行重載
- ?不要重載 && 和 || 操作符,因?yàn)闊o(wú)法實(shí)現(xiàn)短路規(guī)則
//短路規(guī)則
a&b&c只要a為假,整個(gè)式子就是假,b往后就短路了,不需要看
a|b|c只要a為真,整個(gè)式子就是真,b往后就短路了,不需要看
常規(guī)建議:
拓展:
二.常用運(yùn)算符重載
①左移(<<)和右移(>>)運(yùn)算符重載
C++輸入輸出標(biāo)準(zhǔn)庫(kù)提供了“>>”和“<<”運(yùn)算符執(zhí)行輸入、輸出操作,但標(biāo)準(zhǔn)庫(kù)只定義了基本數(shù)據(jù)類型的輸入、輸出操作,若要直接對(duì)類對(duì)象進(jìn)行輸入、輸出,則需要在類中重載這兩個(gè)運(yùn)算符。
與其他運(yùn)算符不同的是,輸入、輸出運(yùn)算符只能重載成類的友元函數(shù)?!?lt;<”和“>>”運(yùn)算符重載的格式如下:
解釋:
1??重載后函數(shù)參數(shù)是什么?
①<<和>>是雙目運(yùn)算符,重載時(shí)函數(shù)需要兩個(gè)參數(shù),那么參數(shù)的類型是什么?要打印的對(duì)象類型顯而易見(jiàn),但cout和cin這兩個(gè)對(duì)象的類型是什么呢?
cout 是 ostream 類的對(duì)象。cin是istream類的對(duì)象。ostream 、istream類、 cout 和cin都是在頭文件 < iostream > 中聲明的。
??注意:重載的函數(shù)的參數(shù)都需要用引用,cout是ostream類的對(duì)象,cin是istream類的對(duì)象,ostream類和istream中的拷貝構(gòu)造函數(shù)是私有的無(wú)法調(diào)用,如果不加引用,傳參時(shí)會(huì)調(diào)用ostream類和istream類中的拷貝構(gòu)造,編譯會(huì)出現(xiàn)沖突。
2??重載的函數(shù)返回類型是什么?
以cout為例,cin同理,重載的函數(shù)返回類型可以是void,但有缺點(diǎn),一次只能打印一個(gè)對(duì)象,不能連續(xù)打印。
cout<<m1;??
cout<<m1<<m2<<m3;??等價(jià)于void<<m2<<m3
cout<<m1<<endl;??
解決方法就是返回對(duì)象為ostream &,要加引用(&),不加引用就調(diào)用ostream的拷貝構(gòu)造函數(shù),但ostream類的拷貝構(gòu)造函數(shù)是私有的。
cout<<m1<<m2<<m3;等價(jià)于cout<<m2<<m3~cout<<m3;
3??重載為哪種函數(shù)?
考慮到cout對(duì)象在<<運(yùn)算符左邊, 如果使用類的成員函數(shù)重載<<的話, 需要在ostream類中重載函數(shù),因?yàn)閛stream是標(biāo)準(zhǔn)庫(kù)定義的類,我們不能更改庫(kù)中代碼,在左邊不符合要求。
普通函數(shù)不能訪問(wèn)類的私有變量,直接pass掉。
所以我們選擇將重載后的全局函數(shù)聲明為類的友元函數(shù)。
4??代碼實(shí)現(xiàn)
class Maker
{
//聲明重載運(yùn)算符的函數(shù)
friend ostream& operator<<(ostream &out,Maker &m1);
friend istream& operator>>(istream &in,Maker &m1);
public:
int id;
string Name;
Maker(int a,int b,int c,string name)
{
this->id=a;
this->age=b;
this->money=c;
this->Name=name;
}
private:
int age;
int money;
};
ostream& operator<<(ostream &out,Maker &m1)
{
cout<<m1.id<<'|'<<m1.age<<'|'<<m1.money<<'|'<<m1.Name<<endl;
return out;
}
istream& operator>>(istream &in,Maker &m1)
{
in>>m1.id>>m1.age>>m1.money>>m1.Name;
return in;
}
void test()
{
Maker m1(100,18,5999,"強(qiáng)風(fēng)吹拂");
Maker m2(100,18,7999,"強(qiáng)風(fēng)吹拂king");
cout<<m1<<m2;
cout<<"請(qǐng)重新為m1和m2對(duì)象輸入數(shù)據(jù):"<<endl;
cin>>m1>>m2;
cout<<m1<<m2;
cout<<1000<<100.3142<<endl;
//重載后基礎(chǔ)類型,照樣可以輸入輸出
//因?yàn)樽笠坪陀乙七\(yùn)算符基礎(chǔ)類型本沒(méi)有輸入輸出的能力
//之所以有是因?yàn)樽笠坪陀乙七\(yùn)算符輸入輸出基本數(shù)據(jù)在類內(nèi)已經(jīng)全部重載一遍了
}
②賦值(=)運(yùn)算符重載
賦值符常常初學(xué)者的混淆。這是毫無(wú)疑問(wèn)的,因?yàn)椤?’在編程中是最基本的運(yùn)算符,可以進(jìn)行賦值操作,也能引起拷貝構(gòu)造函數(shù)的調(diào)用。
知識(shí)卡片:
1.編譯器默認(rèn)給類提供了一個(gè)默認(rèn)的賦值運(yùn)算符重載函數(shù),默認(rèn)的賦值運(yùn)算符重載函數(shù)對(duì)成員變量進(jìn)行了簡(jiǎn)單的賦值操作。
??例如:
class Maker
{
public:
Maker()
{
id = 0;
age = 0;
}
Maker(int id, int age)
{
this->id = id;
this->age = age;
}
public:
int id;
int age;
};
void test()
{
Maker m1(10, 20);
Maker m2;
m2 = m1;//賦值操作
//默認(rèn)的賦值運(yùn)算符重載函數(shù)進(jìn)行了簡(jiǎn)單的賦值操作
cout << m2.id << " " << m2.age << endl;
}
2.區(qū)分賦值操作和拷貝構(gòu)造函數(shù),因?yàn)椤?’在編程中是最基本的運(yùn)算符,可以進(jìn)行賦值操作,也能引起拷貝構(gòu)造函數(shù)的調(diào)用。
區(qū)分的關(guān)鍵在于看對(duì)象有沒(méi)有創(chuàng)建。
??例如:
#include <iostream>
using namespace std;
class Student
{
public:
Student(int id, int age)
{
cout << "調(diào)用有參構(gòu)造函數(shù)"<<endl;
this->ID = id;
this->Age = age;
}
Student(const Student&s)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)" << endl;
this->ID =s.ID;
this->Age = s.Age;
}
void Printf()
{
cout << this->ID << this->Age << endl;
}
private:
int ID;
int Age;
};
void test()
{
Student s1(10, 20);//調(diào)用有參構(gòu)造函數(shù)
Student s2 = s1;//調(diào)用拷貝構(gòu)造函數(shù)
s1.Printf();
s2.Printf();
//如果一個(gè)對(duì)象還沒(méi)有被創(chuàng)建,則必須初始化,也就是調(diào)用構(gòu)造函數(shù)
//上述例子由于s1還沒(méi)有初始化,所以會(huì)調(diào)用構(gòu)造函數(shù)
//由于s2是從已有的s1來(lái)創(chuàng)建的,所以只有一個(gè)選擇
//就是調(diào)用拷貝構(gòu)造函數(shù)
Student s3(101, 100);
//這是賦值操作
s3 = s1;
s3.Printf();
//由于s3已經(jīng)創(chuàng)建,不需要再調(diào)用構(gòu)造函數(shù),這時(shí)候調(diào)用的是重載的賦值運(yùn)算符
}
int main()
{
test();
return 0;
}
3.當(dāng)類的成員變量有指針時(shí),有可能會(huì)出現(xiàn)兩個(gè)問(wèn)題
一是在創(chuàng)建對(duì)象調(diào)用拷貝構(gòu)造函數(shù)時(shí),會(huì)出現(xiàn)淺拷貝問(wèn)題,即一個(gè)對(duì)象的成員變量(指針)被拷貝給另一個(gè)對(duì)象的成員變量,然后兩個(gè)對(duì)象的成員變量(指針)指向同一份空間,在析構(gòu)函數(shù)調(diào)用時(shí)就會(huì)出現(xiàn)同一塊空間釋放2次。
解決方法深拷貝。
二是在賦值時(shí),兩個(gè)對(duì)象都已被創(chuàng)建,此時(shí)賦值會(huì)導(dǎo)致一個(gè)對(duì)象的成員變量(指針)會(huì)被覆蓋,導(dǎo)致內(nèi)存泄露。
解決方法重載=運(yùn)算符。
重載=運(yùn)算符解決上述的第二個(gè)問(wèn)題:
class Student
{
public:
Student(const char *name)
{
pName = new char[strlen(name) + 1];
strcpy(pName, name);
}
//防止淺拷貝
Student(const Student &stu)
{
pName = new char[strlen(stu.pName) + 1];
strcpy(pName, stu.pName);
}
//重寫賦值運(yùn)算符重載函數(shù)
Student &operator=(const Student &stu)
{
//1.不能確定this->pName指向的空間是否能裝下stu中的數(shù)據(jù),所以先釋放this->pName指向的空間
if (this->pName != NULL)
{
delete[] this->pName;
this->pName = NULL;
}
//2.申請(qǐng)堆區(qū)空間,大小由stu決定
this->pName = new char[strlen(stu.pName) + 1];
//3.拷貝數(shù)據(jù)
strcpy(this->pName, stu.pName);
//4.返回對(duì)象本身
return *this;
}
~Student()
{
if (pName != NULL)
{
delete[] pName;
pName = NULL;
}
}
void printStudent()
{
cout << "Name:" << pName << endl;
}
public:
char *pName;
};
void test()
{
Student s1("強(qiáng)風(fēng)吹拂");
Student s2("強(qiáng)風(fēng)吹拂king");
s1.printStudent();
s2.printStudent();
s1 = s2;//賦值操作
s1.printStudent();
s2.printStudent();
}
4.為什么operator=返回一個(gè)reference to *this ?
①為了實(shí)現(xiàn)連續(xù)賦值,賦值操作符必須返回一個(gè)引用指向操作符的左側(cè)實(shí)參。這是你為class實(shí)現(xiàn)賦值操作符必須遵循的協(xié)議。這個(gè)協(xié)議不僅適用于標(biāo)準(zhǔn)的賦值形式,也適用于+=、-=、*=等等。
②如果不加引用&,對(duì)象以值的形式返回,會(huì)在返回處調(diào)用拷貝構(gòu)造函數(shù),產(chǎn)生一個(gè)新對(duì)象。
??例如:
void test()
{
Student s1("a");
Student s2("b");
Student s3("c");
s1 = s2 = s3;//s3賦值s2,s2賦值給s1
//判斷s3賦值給s2返回的是不是s2這個(gè)對(duì)象。
cout << &(s2 = s3) << endl;
cout << &s2 << endl;
}
- 一方面如果operator+返回類型不帶引用,那么在返回的過(guò)程中表達(dá)式會(huì)調(diào)用拷貝構(gòu)造函數(shù),產(chǎn)生一個(gè)新的對(duì)象,就不是s2這個(gè)對(duì)象了。
- 另外一個(gè)方面:s1=s2=s3,賦值運(yùn)算符本來(lái)的寓意,是s3賦值s2,s2賦值給s1,也就是說(shuō)s2=s3這個(gè)表達(dá)式要返回s2這個(gè)對(duì)象,所以要返回引用。此處放對(duì)比圖
③關(guān)系運(yùn)算符(== != > <)重載
關(guān)系運(yùn)算符(如“==”或“<”)也可以重載,關(guān)系運(yùn)算符的重載函數(shù)返回值類型一般定義為bool類型,即返回true或false。關(guān)系運(yùn)算符常用于條件判斷中,重載關(guān)系運(yùn)算符保留了關(guān)系運(yùn)算符的原有含義。
??案例演示關(guān)系運(yùn)算符重載:
class Student
{
private:
string _id;
double _score;
public:
Student(string id, double score) : _id(id), _score(score){}
void dis()
{
cout << "學(xué)號(hào)" << _id << "成績(jī)" << _score << endl;
}
//重載關(guān)系運(yùn)算符
friend bool operator==(const Student& st1, const Student& st2);
friend bool operator!=(const Student& st1, const Student& st2);
friend bool operator>(const Student& st1, const Student& st2);
friend bool operator<(const Student& st1, const Student& st2);
};
bool operator==(const Student& st1, const Student& st2)
{
return st1._score == st2._score;//重載“==”運(yùn)算符
}
bool operator!=(const Student& st1, const Student& st2)
{
return !(st1._score == st2._score);//重載“!=”運(yùn)算符
}
bool operator>(const Student& st1, const Student& st2)
{
return st1._score > st2._score;//重載“>”運(yùn)算符
}
bool operator<(const Student& st1, const Student& st2)
{
return st1._score<st2._score;//重載“<”運(yùn)算符
}
void test()
{
Student st1("22031202000", 101), st2("22031202001", 148);
cout << "比較兩名學(xué)生的成績(jī):" << endl;
if (st1>st2)
st1.dis();
else if (st1<st2)
st2.dis();
else
cout << "兩名學(xué)生成績(jī)相同:" << endl;
}
關(guān)系運(yùn)算符重載有以下幾點(diǎn)使用技巧:
- 通常關(guān)系運(yùn)算符都要成對(duì)地重載,例如重載了“>”運(yùn)算符,就要重載“<”運(yùn)算符,反之亦然。
- 通常情況下,“==”運(yùn)算符具有傳遞性,例如:a= =b,b= =c,則a= =c成立。
- 可以把一個(gè)運(yùn)算符的工作委托給另一個(gè)運(yùn)算符,通過(guò)重載后的結(jié)果進(jìn)行判斷。例如,本例中重載“!=”運(yùn)算符是在重載“==”運(yùn)算符的基礎(chǔ)上實(shí)現(xiàn)的。
④前置和后置(++/- -)重載
重載的++和- -運(yùn)算符有點(diǎn)讓人不知所措,因?yàn)榍爸煤秃笾玫倪\(yùn)算符一樣,都是++或- -,重載后的函數(shù)名都是operator++(- -),不便于區(qū)分,所以C++給我們提供了解決方法——占位參數(shù)。
即允許寫一個(gè)增加了無(wú)用 int 類型形參的版本,編譯器處理++或–前置的表達(dá)式時(shí),調(diào)用參數(shù)個(gè)數(shù)正常的重載函數(shù);處理后置表達(dá)式時(shí),調(diào)用多出一個(gè)參數(shù)的重載函數(shù)。
重載形式:
例如:
class Maker
{
friend ostream &operator<<(ostream &out, Maker &m);
public:
Maker(int a)
{
this->a = a;
}
//重載前置加加,重載后的函數(shù)參數(shù)個(gè)數(shù)正常0個(gè)
Maker &operator++()
{//返回類型是你自定義的類型,因?yàn)橛?+(m++)情況存在
++this->a;//先完成遞增操作
return *this;//再返回當(dāng)前對(duì)象
}
//重載后置加加,重載后的函數(shù)參數(shù)比正常情況多一個(gè)占位參數(shù)int
Maker operator++(int)//占位參數(shù),必須是int
{
//后置加加,先返回,在加加,
//但一旦先返回,加加就操作不了,所以我們采取一個(gè)臨時(shí)對(duì)象來(lái)延遲返回操作。
//先保存舊值,然后加加,最后返回舊值
Maker tmp(*this);//1.*this里面的值a是等于2
++this->a;//這個(gè)對(duì)象的a等3
return tmp;
}
private:
int a;
};
ostream &operator<<(ostream &out, Maker &m)
{
out << m.a << endl;
return out;
}
void test()
{
Maker m1(1);
cout << m1 << endl;//1
cout << ++m1 << endl;//2
//++(++m1);
cout << m1++ << endl;//2 這里返回的拷貝的tmp對(duì)象
cout << m1 << endl;//3 這里打印的是++this->a的對(duì)象
//同等條件下,優(yōu)先使用前置加加,不需要產(chǎn)生新的對(duì)象和調(diào)用拷貝構(gòu)造
}
對(duì)比前置++和后置++運(yùn)算符的重載可以發(fā)現(xiàn),后置++運(yùn)算符的執(zhí)行效率比前置的低。因?yàn)楹笾梅绞降闹剌d函數(shù)中要多生成一個(gè)局部對(duì)象 tmp,而對(duì)象的生成會(huì)引發(fā)構(gòu)造函數(shù)調(diào)用,需要耗費(fèi)時(shí)間。同理,后置–運(yùn)算符的執(zhí)行效率也比前置的低。
思考??
為什么前置++運(yùn)算符的返回值類型是Maker &,而后置++運(yùn)算符的返回值類型是Maker?(Maker是你自定義的數(shù)據(jù)類型)
原因有二個(gè)方面:
- ①是因?yàn)檫\(yùn)算符重載最好保持原運(yùn)算符的用法。C++ 固有的前置++運(yùn)算符的返回值本來(lái)就是操作數(shù)的引用,而后置++運(yùn)算符的返回值則是操作數(shù)值修改前的復(fù)制品。
- ②后置++或- -,返回的是局部對(duì)象(默認(rèn)情況下,局部對(duì)象的聲明周期局限于所在函數(shù)的每次執(zhí)行期間,只有當(dāng)函數(shù)被調(diào)用的時(shí)候才存在),當(dāng)函數(shù)執(zhí)行完畢時(shí),會(huì)立即釋放分配給局部對(duì)象的存儲(chǔ)空間。此時(shí),對(duì)局部對(duì)象的引用就會(huì)指向不確定的內(nèi)存。
⑤類型轉(zhuǎn)換(類型轉(zhuǎn)換函數(shù)和轉(zhuǎn)換構(gòu)造函數(shù))
基本數(shù)據(jù)類型的數(shù)據(jù)可以通過(guò)強(qiáng)制類型轉(zhuǎn)換操作符將數(shù)據(jù)轉(zhuǎn)換成需要的類型,例如static_cast(3.14),這個(gè)表達(dá)式是將實(shí)型數(shù)據(jù)3.14轉(zhuǎn)換成整型數(shù)據(jù)。對(duì)于自定義的類,C++提供了類型轉(zhuǎn)換函數(shù)來(lái)實(shí)現(xiàn)自定義類與基本數(shù)據(jù)類型之間的轉(zhuǎn)換。
1??類型轉(zhuǎn)換函數(shù)
對(duì)于自定義的類,C++提供了類型轉(zhuǎn)換函數(shù)用來(lái)將類對(duì)象轉(zhuǎn)換為基本數(shù)據(jù)類型。
類型轉(zhuǎn)換函數(shù)也稱為類型轉(zhuǎn)換運(yùn)算符重載函數(shù),定義格式如下所示:
類型轉(zhuǎn)換函數(shù)以operator關(guān)鍵字開(kāi)頭,這一點(diǎn)和運(yùn)算符重載規(guī)律一致。從類型轉(zhuǎn)換函數(shù)格式可以看出,在重載的數(shù)據(jù)類型名前不能指定返回值類型,返回值的類型由重載的數(shù)據(jù)類型名確定,且函數(shù)沒(méi)有參數(shù)。由于類型轉(zhuǎn)換函數(shù)的主體是本類的對(duì)象,因此只能將類型轉(zhuǎn)換函數(shù)重載為類的成員函數(shù)。
?示例:
class king
{
public:
king(string id,const char*name)
{
this->ID=id;
Name = new char[strlen(name) + 1];
strcpy(Name, name);
}
void Show()
{
cout<<"ID:"<<ID<<","<<"Name:"<<Name<<endl;
}
operator char*()//類型轉(zhuǎn)換運(yùn)算符重載
{//如果要將對(duì)象轉(zhuǎn)換為char*指針
//就直接將對(duì)象轉(zhuǎn)換為自己的成員char*類型的Name
return Name;
}
private:
string ID;
char *Name;
};
void test()
{
king k1("2203120000","強(qiáng)風(fēng)吹拂king");
k1.Show();
char *PK=k1;//調(diào)用類型轉(zhuǎn)換函數(shù)
cout<<PK<<endl;
//通過(guò)調(diào)用重載的char*類型轉(zhuǎn)換函數(shù),將對(duì)象s1成功轉(zhuǎn)換為了char*類型
}
將對(duì)象k1成功轉(zhuǎn)換為char*類型
2??轉(zhuǎn)換構(gòu)造函數(shù)
轉(zhuǎn)換構(gòu)造函數(shù)指的是構(gòu)造函數(shù)只有一個(gè)參數(shù),且參數(shù)不是本類的const引用。用轉(zhuǎn)換構(gòu)造函數(shù)不僅可以將一個(gè)標(biāo)準(zhǔn)類型數(shù)據(jù)轉(zhuǎn)換為類對(duì)象,也可以將另一個(gè)類的對(duì)象轉(zhuǎn)換為轉(zhuǎn)換構(gòu)造函數(shù)所在的類對(duì)象。轉(zhuǎn)換構(gòu)造函數(shù)的語(yǔ)法格式如下所示:
??案例演示轉(zhuǎn)換構(gòu)造函數(shù):
三維坐標(biāo)類轉(zhuǎn)換為二維坐標(biāo)類。
class Solid//三維坐標(biāo)類
{
friend class Point;
//由于需要在Point類中訪問(wèn)Solid的成員變量
//因此將Solid類聲明為Point類的友元類。
public:
Solid(int x,int y,int z) :x(x), y(y),z(z){}
void Show()
{
cout<<"三維坐標(biāo)"<<"("<<x<<","<<y<<","<<z<<")"<<endl;
}
private:
int x,y,z;
};
class Point//二維點(diǎn)類
{
public:
Point(int x, int y) :x(x), y(y){}
Point(const Solid &another)//定義轉(zhuǎn)換構(gòu)造函數(shù)
{
this->x=another.x;
this->y=another.y;
}
void Show()
{
cout<<"二維坐標(biāo)"<<"("<<x<<","<<y<<")"<<endl;
}
private:
int x,y;
};
void test()
{
Point p1(1,1);
p1.Show();
Solid s1(2,2,2);
s1.Show();
cout<<"三維轉(zhuǎn)換二維坐標(biāo)"<<endl;
p1=s1;
p1.Show();
}
⑥數(shù)組下標(biāo)([])運(yùn)算符重載
在程序設(shè)計(jì)中,通常使用下標(biāo)運(yùn)算符“[]”訪問(wèn)數(shù)組或容器中的元素。為了在類中方便地使用“[]”運(yùn)算符,可以在類中重載運(yùn)算符“[]”。重載“[]”運(yùn)算符有兩個(gè)目的:
- “對(duì)象[下標(biāo)]”的形式類似于“數(shù)組[下標(biāo)]”,更加符合用戶的編寫習(xí)慣。
- 可以對(duì)下標(biāo)進(jìn)行越界檢查。
重載下標(biāo)運(yùn)算符“[]”的語(yǔ)法格式如下所示:
上述格式中,“[]”運(yùn)算符重載函數(shù)有且只有一個(gè)整型參數(shù),表示下標(biāo)值。重載下標(biāo)運(yùn)算符時(shí)一般把返回值指定為一個(gè)引用,因?yàn)閿?shù)組下標(biāo)既要做到讀數(shù)據(jù),也要做到寫數(shù)據(jù),所以要能當(dāng)左右值。
?案例演示:
class MyArray
{
public:
MyArray();//無(wú)參構(gòu)造函數(shù)
MyArray(const MyArray &arr);//拷貝構(gòu)造函數(shù)
MyArray(int capacity, int val = 0);//有參構(gòu)造參數(shù),如果不指名初始值,默認(rèn)為val=0。
~MyArray();//析構(gòu)函數(shù)
//重寫賦值運(yùn)算符重載函數(shù)
MyArray&operator=(const MyArray &m);
//重寫[]運(yùn)算符,要能當(dāng)左右值,左值可以放數(shù)據(jù),右值可以讀數(shù)據(jù)
int &operator[](int index);
//打印數(shù)據(jù)
void PrintfMyArray();
private:
int *pArray;//指向堆區(qū)空間,存儲(chǔ)數(shù)據(jù)
int mSize;//元素個(gè)數(shù)
int mCapacity;//數(shù)組容量
};
//無(wú)參構(gòu)造函數(shù)
MyArray::MyArray()
{
this->mCapacity = 10;
this->mSize = 0;
this->pArray = new int[this->mCapacity];
for (int i = 0; i < this->mCapacity; i++)
{
this->pArray[i] = 0;
}
}
//析構(gòu)函數(shù)
MyArray::~MyArray()
{
if (this->pArray != NULL)
{
delete[] this->pArray;
this->pArray = NULL;
}
}
//拷貝構(gòu)造函數(shù)
MyArray::MyArray(const MyArray &arr)
{
this->mCapacity = arr.mCapacity;
this->mSize = arr.mSize;
//1.申請(qǐng)空間
this->pArray = new int[arr.mCapacity];
//2.拷貝數(shù)據(jù)
for (int i = 0; i < this->mSize; i++)
{
this->pArray[i] = arr.pArray[i];
}
}
//有參構(gòu)造函數(shù),不指定初始值,則默認(rèn)全部初始化為0
MyArray::MyArray(int capacity, int val)
{
this->mCapacity = capacity;
this->mSize = capacity;
this->pArray = new int[capacity];
for (int i = 0; i < this->mSize; i++)
{
this->pArray[i] = val;
}
}
//重寫賦值運(yùn)算符重載函數(shù)
MyArray&MyArray::operator=(const MyArray &m)
{
//1.釋放原來(lái)的空間
if (this->pArray != NULL)
{
delete[] this->pArray;
this->pArray = NULL;
}
this->mCapacity = m.mCapacity;
this->mSize = m.mSize;
//2.申請(qǐng)空間,大小由m決定
this->pArray = new int[m.mCapacity];
//3.拷貝數(shù)據(jù)
for (int i = 0; i < this->mCapacity; i++)
{
this->pArray[i] = m.pArray[i];
}
return *this;
}
//要能當(dāng)左右值
int &MyArray::operator[](int index)
{
static int x=-1;
//數(shù)組下標(biāo)檢查
if(mSize<index||index<0)
{
cout<<"index越界"<<endl;
return x;
}
else if (this->mSize ==index)
{ //當(dāng)m.Size元素個(gè)數(shù)等于下標(biāo)index
this->mSize++;
}
//當(dāng)else if執(zhí)行說(shuō)明此時(shí)要加入新數(shù)據(jù),此時(shí)m.Size先要++,然后return this->pArray[index];返回的是左值。
//上面兩個(gè)if都不執(zhí)行,說(shuō)明只讀取下標(biāo)為index的元素,然后return this->pArray[index];返回的是右值。
return this->pArray[index];
}
void MyArray::PrintfMyArray()
{
for(int i=0;i<mSize;i++)
{
cout<<pArray[i]<<" |";//調(diào)用[]運(yùn)算符重載函數(shù),輸出數(shù)組類
}
}
void test()
{
MyArray arr;//無(wú)參構(gòu)造函數(shù)
for (int i = 0; i < 10; i++)
{
arr[i] = i + 10;//調(diào)用[]運(yùn)算符重載函數(shù),初始化數(shù)組類
}
arr.PrintfMyArray();
cout << endl;
MyArray arr2(10, 66);//有參構(gòu)造函數(shù)
arr2.PrintfMyArray();
cout << endl;
MyArray arr3(arr2);//拷貝構(gòu)造函數(shù)
arr3.PrintfMyArray();
cout << endl;
MyArray arr4;
arr4 = arr;//調(diào)用賦值運(yùn)算符=的重載函數(shù),arr賦值給arr4
arr4.PrintfMyArray();
cout << endl;
arr2[6]=10086;//調(diào)用[]運(yùn)算符,修改特定下標(biāo)的值
cout << arr2[6] << "|";
cout << endl;
cout << arr2[60] << "|";
cout << endl;
}
⑦指針運(yùn)算符(*、 ->)重載
智能指針類
智能指針(smart pointer)是存儲(chǔ)指向動(dòng)態(tài)分配(堆)對(duì)象指針的類,用于生存期控制,能夠確保自動(dòng)正確的銷毀動(dòng)態(tài)分配的對(duì)象,防止內(nèi)存泄露。
智能指針的本質(zhì)是使用引用計(jì)數(shù)的方式解決懸空指針的問(wèn)題,通過(guò)重載“*”和“?>”運(yùn)算符來(lái)實(shí)現(xiàn)。
為什么有智能指針類?
在平時(shí)我們寫代碼的時(shí)候,用new開(kāi)辟出來(lái)的空間雖然我們知道要進(jìn)行資源的回收,但可能會(huì)因?yàn)槌绦驁?zhí)行流的改變導(dǎo)致資源沒(méi)有歸還所導(dǎo)致的內(nèi)存泄漏的問(wèn)題,智能指針就幫助我們解決這個(gè)問(wèn)題。
在學(xué)習(xí)引用計(jì)數(shù)、重載“*”和“?>”運(yùn)算符之前,需要理解普通指針在資源訪問(wèn)中導(dǎo)致的指針懸空問(wèn)題。
??例如:
class king
{
public:
king(string name) :Name(name)
{
cout << "king構(gòu)造函數(shù)調(diào)用" << endl;
}
~king(){}
void Show()
{
cout << "強(qiáng)風(fēng)吹拂king的博客" << endl;
}
private:
string Name;
};
void test()
{
king *pstr1 = new king("感謝支持我的博客");
king *pstr2 = pstr1;
king *pstr3 = pstr1;
pstr1->Show();
delete pstr1;
pstr2->Show();
}
??問(wèn)題:
指針pstr1、pstr2、pstr3共享同一個(gè)對(duì)象,若釋放pstr1指向的對(duì)象,pstr2和pstr3仍然在使用該對(duì)象,將造成pstr2和pstr3無(wú)法訪問(wèn)資源,成為懸空指針,程序運(yùn)行時(shí)出現(xiàn)異常。
為了解決懸空指針的問(wèn)題,C++語(yǔ)言引入了引用計(jì)數(shù)的概念。引用計(jì)數(shù)是計(jì)算機(jī)科學(xué)中的一種編程技術(shù),用于存儲(chǔ)計(jì)算機(jī)資源的引用、指針或者句柄的數(shù)量。當(dāng)引用計(jì)數(shù)為零時(shí)自動(dòng)釋放資源,使用引用計(jì)數(shù)可以跟蹤堆中對(duì)象的分配和自動(dòng)釋放堆內(nèi)存資源。
案例演示:
class king
{
public:
king(string name) :Name(name)
{
cout << "king構(gòu)造函數(shù)調(diào)用" << endl;
}
~king(){}
void Show()
{
cout << "感謝支持強(qiáng)風(fēng)吹拂king的博客" << endl;
}
private:
string Name;
};
class Count//Count類用于存儲(chǔ)指向同一資源的指針數(shù)量
{
//聲明SmartPtr智能指針類為Count類的友元類
//因?yàn)镾martPtr要訪問(wèn)Count類成員
friend class SmartPtr;
public:
//Count成員pking指向由SmartPtr類傳過(guò)來(lái)king類對(duì)象的指針
Count(king *pstr1) :pking(pstr1), count(1)
{
cout << "Count類構(gòu)造函數(shù)" << endl;
}
~Count()
{
cout << "Count類析構(gòu)函數(shù)" << endl;
delete pking;//釋放king類指針
}
private:
king *pking;
int count;
};
class SmartPtr//SmartPtr類用于對(duì)指向king類對(duì)象的指針實(shí)現(xiàn)智能管理
{
public:
//CountNum是成員對(duì)象指針,指向由king類指針初始化的Count類
SmartPtr(king* pstr1) : CountNum(new Count(pstr1))
{
cout << "SmartPtr有參構(gòu)造函數(shù)調(diào)用" << endl;
}
//拷貝構(gòu)造函數(shù)
SmartPtr(const SmartPtr& another) :CountNum(another.CountNum)
{
++CountNum->count;//只要調(diào)用拷貝構(gòu)造函數(shù),就說(shuō)明多一個(gè)指針指向king對(duì)象,所以count++
cout << "Smartptr類拷貝構(gòu)造函數(shù)" << endl;
}
//定義析構(gòu)函數(shù)釋放king類對(duì)象的資源,
//當(dāng)記錄指向king類對(duì)象指針的數(shù)量count為0時(shí),釋放資源。
~SmartPtr()
{
if (--CountNum->count == 0)
{
delete CountNum;//釋放指向成員對(duì)象Count的指針
cout << "Smartptr類析構(gòu)函數(shù)" << endl;
}
}
//通過(guò)重載“*”和“->”運(yùn)算符就可以指針的方式實(shí)現(xiàn)king類成員的訪問(wèn)。
//->運(yùn)算符重載,返回指向king類對(duì)象的指針。
king *operator->()
{
return CountNum->pking;
}
//*運(yùn)算符重載,返回king類對(duì)象。
king &operator*()
{
return *CountNum->pking;
}
int disCount()
{
return CountNum->count;
}
private:
//成員對(duì)象指針
Count *CountNum;
};
void test()
{
king *pstr1 = new king("感謝支持我的博客");
SmartPtr pstr2 = pstr1;//調(diào)用有參構(gòu)造函數(shù),count不++,所以count的默認(rèn)值才設(shè)為1
(*pstr1).Show();
SmartPtr pstr3 = pstr2;//調(diào)用拷貝構(gòu)造函數(shù),count++
pstr2->Show();
cout << "使用基類對(duì)象的指針數(shù)量:" << pstr2.disCount() << endl;
}
在使用智能指針申請(qǐng)king類對(duì)象存儲(chǔ)空間后并沒(méi)有使用delete釋放內(nèi)存空間。使用智能指針可以避免堆內(nèi)存泄漏,只需申請(qǐng),無(wú)須關(guān)注內(nèi)存是否釋放。通過(guò)重載“*”和“?>”運(yùn)算符可以實(shí)現(xiàn)對(duì)象中成員的訪問(wèn)。
⑧函數(shù)調(diào)用()運(yùn)算符重載——仿函數(shù)
1.類里有重載函數(shù)調(diào)用符號(hào)的類實(shí)例化的對(duì)象也叫仿函數(shù)。
2.仿函數(shù)的作用:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-493032.html
- 方便代碼維護(hù)
- 方便有權(quán)限的調(diào)用函數(shù)。
- 作為算法的策略(仿函數(shù)在STL的算法中使用比較廣泛。)
class Maker
{
public:
Maker()
{
name = "強(qiáng)風(fēng)吹拂king的博客";
}
void printMaker()
{
cout << "感謝支持"+name << endl;
}
//函數(shù)調(diào)用運(yùn)算符()重載
void operator()(const string str )
{
cout << str << endl;
}
//函數(shù)調(diào)用運(yùn)算符()重載
void operator()(int v1,int v2)
{
cout << v1+v2 << endl;
}
public:
string name;
};
void test()
{
Maker func;
func("感謝支持強(qiáng)風(fēng)吹拂king的博客");//看起來(lái)像函數(shù)調(diào)用,其實(shí)func是對(duì)象
func(10, 20);//像調(diào)用函數(shù)一樣調(diào)用對(duì)象func
func.printMaker();
}
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-493032.html
到了這里,關(guān)于C++核心編程——詳解運(yùn)算符重載的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!