C++:12---運算子過載
一、概念
對已有的運算子重新進行定義,賦予其另一種功能,以適應不同的資料型別
過載的運算子是具有特殊名字的函式,該函式也有返回值、引數列表、函式體
二、運算子過載的3種實現方式
成員函式:私有、公有、保護都可以
友元函式:同上
全域性函式:只能訪問公有的
三、運算子過載的規則
C++98,C++0x,C++11對“哪些運算子過載能夠過載”有一致的規定。詳細例如以下:
以下運算子不支援過載:.(類屬運算子),::(作用域運算子),sizeof,?:(三目運算子),#(預處理運算子)
只能用成員函式過載的運算子:=(賦值運算子)、()(強制型別轉換)、[]、new、delete、->
只能用友元、全域性函式過載的運算子:<<、>>
+=、-=、=運算子返回值為引用型別(&):函式執行完之後返回的是*this(物件本身)。如果不加&,則返回的是臨時物件
承接上一注意事項:前置++、--,返回值為引用。後置++、--,返回值不為引用
四、+、-、*、/、+=、-=的過載
//只實現+、*,+=。其它原理都相同
classCperson{private:intdata;public:Cperson operator+(Cperson const& other)const;//類成員函式實現friendCpersonoperator*(Cpersonconst&p1,Cpersonconst&p2);//友元函式方式實現Cperson&operator+=(Cpersonconst&other);};CpersonCperson::operator+(Cpersonconst&other)const{Cpersontemp;temp.data=this->data+other.data;returntemp;} Cperson operator*(Cperson const& p1,Cperson const& p2){Cpersontemp;temp.data=p1.data*p2.data;returntemp;} Cperson& Cperson::operator+=(Cperson const& other){Cpersontemp=*this=other;//因為前面已經實現了+運算子,此處直接使用return*this=temp;}
五、賦值運算子(=)的過載
賦值運算子的過載,返回值應該為運算子左側物件的一個引用,否則返回的是一個臨時物件
如果沒有寫賦值過載運算子,編譯器自動存在一個預設的,就是拷貝構造中所用到的預設拷貝構造,但是如果類成員變數中含有動態記憶體的變數時,需要過載賦值運算子
class Cperson{private:char* name;public:Cperson& operator=(Cperson const& other);};Cperson& Cperson::operator=(Cperson const& other){if(this->name)//先釋放原先的資料delete[] name;if(other)//如果傳入的引數不為空{int len=strlen(other.name);this->name=new char[len+1];strcpy(this->name,other.name);}else//如果傳入的引數為空{other.name=nullptr;}};
拷貝建構函式與拷貝賦值運算子的關係
拷貝建構函式是用另一個物件來初始化一塊記憶體區域,這塊記憶體就是新物件的記憶體區
賦值函式是對於一個已經被初始化的物件來進行operator=操作。例如:
class A;A a;A b = a; // 呼叫拷貝建構函式, 因為b是第一次初始化A c(a); // 呼叫拷貝建構函式, 因為c是第一次初始化b = c; // 呼叫賦值運算子, 因為b已經初始化過了
六、++、--運算子的過載
為了區別前置和後置:後置++的引數為int,前置++無引數
前置++、--,返回值為引用。後置++、--,返回值不為引用
++、--(前置後置),下面只演示++的操作
class Cperson{private:int data;public:Cperson& operator++();//前置++,返回值為引用Cperson operator++(int);//後置++,返回值不為引用};Cperson& Cperson::operator++(){data++;return *this;}Cperson Cperson::operator++(int){Cperson temp=*this;data++;return temp;}
七、==、!=運算子的過載
用途:這兩個運算子用來判斷兩個類物件中資料成員的值是否相等/不相等(可以在過載中判斷單/多個成員是否相等)
設計規則:
通常情況下,==運算子應該具有傳遞性,例如:如果a==b和b==c,那麼a==c應該也為真
如果實現了==運算子,則!=運算子可以直接在return語句中應用剛才實現的==運算子來簡化書寫
如果用成員函式實現只能有一個引數,用友元、全域性函式實現是兩個引數
class Cperson{private:int id;int age;public:Cperson(int Id, int Age) :id(Id), age(Age) {}int getId()const;int getAge()const;friend bool operator==(const Cperson &p1, const Cperson &p2){return ((p1.getId() == p2.getId()) && (p1.getAge() == p2.getAge()));}friend bool operator!=(const Cperson &p1, const Cperson &p2){return !(p1 == p2);}};int Cperson::getId()const { return id; }int Cperson::getAge()const { return age; }
八、>>、<<運算子的過載
輸入輸出運算子的過載不能用成員函式實現,一般用友元實現
過載輸出運算子<<
引數:
引數1:一個非常量ostream物件的引用(ostream是非常量是因為向流寫入內容會改變其狀態)
引數2:一般來說是一個常量的引用,該常量是我們想要列印的類型別(使用引用的原因是我們希望避免複製實參。使用常量是因為不會改變物件的內容)
返回值:返回它的ostream形參
過載輸入運算子>>
引數:
引數1:是運算子將要讀取的流的引用
引數2:將要讀入到的(非常量)的引用(使用非常量是因為輸入運算子本身的目的就是將資料讀入到這個物件中)
返回值:某個給定流的引用
輸入時可能產生的錯誤:
輸入的型別不符
當讀取操作達到檔案末尾或者遇到輸入流的其它錯誤時也會失敗
class Cpoint{private:int x;int y;public:friend ostream& operator<<(ostream& os, const CMyPoint const& pt);//輸出流friend istream& operator>>(ostream& is, const CMyPoint const& pt);//輸入流}ostream& operator<<(ostream& os, const CMyPoint const& pt){os<<pt.x<<pt.y;return os;}istream& operator>>(ostream& is,const CMyPoint const& pt){is>>pt.x>>pt.y;return is;}
九、[]下標運算子的過載
下標操作符 [] 通常用於訪問陣列元素。過載該運算子用於增強操作 C++ 陣列的功能。
下面的例項演示瞭如何過載下標運算子 []。
#include <iostream>
using namespace std;
const int SIZE = 10;
classSafearray
{
private:
int arr[SIZE];
public:
Safearray()
{
register int i;
for(i = 0; i < SIZE; i++)
{
arr[i] = i;
}
}
int& operator[](int i)
{
if( i > SIZE )
{
cout << "索引超過最大值" <<endl;
// 返回第一個元素
return arr[0];
}
return arr[i];
}
};
int main()
{
SafearrayA;
cout << "A[2] 的值為 : " << A[2] <<endl;
cout << "A[5] 的值為 : " << A[5]<<endl;
cout<<"A[12]的值為:"<<A[12]<<endl;
return 0;
}
十、->成員訪問運算子的過載
類成員訪問運算子( -> )可以被過載,但它較為麻煩。它被定義用於為一個類賦予"指標"行為。運算子 -> 必須是一個成員函式。如果使用了 -> 運算子,返回型別必須是指標或者是類的物件。
運算子 -> 通常與指標引用運算子 * 結合使用,用於實現"智慧指標"的功能。這些指標是行為與正常指標相似的物件,唯一不同的是,當您通過指標訪問物件時,它們會執行其他的任務。比如,當指標銷燬時,或者當指標指向另一個物件時,會自動刪除物件。
間接引用運算子 -> 可被定義為一個一元后綴運算子。也就是說,給出一個類:
classPtr{
//...
X *operator->();};
類Ptr的物件可用於訪問類X的成員,使用方式與指標的用法十分相似。例如:
void f(Ptr p ){
p->m =10;// (p.operator->())->m = 10}
語句 p->m 被解釋為 (p.operator->())->m。同樣地,下面的例項演示瞭如何過載類成員訪問運算子 ->。
#include <iostream>
#include <vector>
using namespace std;
// 假設一個實際的類
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// 靜態成員定義
int Obj::i = 10;
int Obj::j = 12;
// 為上面的類實現一個容器
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj); // 呼叫向量的標準方法
}
friend class SmartPointer;
};
// 實現智慧指標,用於訪問類 Obj 的成員
class SmartPointer {
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = 0;
}
// 返回值表示列表結束
bool operator++() // 字首版本
{
if(index >= oc.a.size() - 1) return false;
if(oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) // 字尾版本
{
return operator++();
}
// 過載運算子 ->
Obj* operator->() const
{
if(!oc.a[index])
{
cout << "Zero value";
return (Obj*)0;
}
return oc.a[index];
}
};
int main() {
const int sz = 10;
Obj o[sz];
ObjContainer oc;
for(int i = 0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 建立一個迭代器
do {
sp->f(); // 智慧指標呼叫
sp->g();
} while(sp++);
return 0;
}
十一、()函式呼叫運算子的過載
如果類過載了函式呼叫運算子,那麼我們在使用該物件時就如同呼叫一個函式一樣
注意:()運算子與物件初始化時呼叫建構函式不是一個東西、因此()函式呼叫運算子不能再類初始化時使用,會與建構函式衝突
struct absInt {bool operator()(int value) {return value < 0 ? true : false;}};int main(){absInt a;printf("%d\n", a(-1));//列印1return 0;}