友元
- 可以通過friend關鍵字,把一個全局函數、另一個類的成員函數或者另一個類整體,聲明為授權類的友元
- 友元擁有訪問授權類任何非公有成員的特權
- 友元聲明可以出現在授權類的公有、私有或者保護等任何區域且不受訪問控制限定符的約束
- 友元不是成員,其作用域并不隸屬于授權類,也不擁有授權類類型的this指針。
操作符標記和操作符函數
雙目操作符表達式
L#R
成員函數形式:L.operator#(R)
- 左操作數是調用對象,右操作數是參數對象
全局函數形式:operator#(L,R)
- 左操作數是第一個參數,右操作數是第二個參數
單目操作符表達式
#O
/O#
- 成員函數形式:O.operator#()
- 全局函數形式:operator#(O)
三目操作符表達式: F#S#T
三目操作符無法重載
經典雙目操作符
運算類雙目操作符:+、-、*、/等
- 左右操作數均可以為非常左值、常左值或右值
- 表達式的結果為右值
// 運算類雙目操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}// 成員形式操作符函數
// Human operator+(const Human& r) const {
// return Human(this->m_age+r.m_age, (this->m_name+"+"+r.m_name).c_str());
// }
private:int m_age;string m_name;friend Human operator+(const Human& l,const Human& r); // 友元聲明
};
// 全局形式操作符函數
Human operator+(const Human& l, const Human& r){return Human(l.m_age+r.m_age, (l.m_name+"+"+r.m_name).c_str());
}int main(void){Human a(22,"張飛"), b(20,"趙云"); // 非常左值const Human c(25,"關羽"), d(32,"馬超"); // 常左值Human res = a + b; // ==> a.operator+(b) 或 operator+(a,b)res.getInfo();res = c + d; // ==> c.operator+(d) 或 operator+(c,d)res.getInfo();res = Human(45,"黃忠") + Human(35,"劉備"); // ==> Human(45,"黃忠").operator+(Human(35,"劉備")) 或 // operator+(Human(45,"黃忠"),Human(35,"劉備"))res.getInfo();return 0;
}
賦值類雙目操作符:=、+=、-=、*=、/=等
- 右操作數可以為非常左值、常左值或右值,但左操作數必須為非常左值
- 表達式結果為左操作數本身(而非副本)
// 賦值類雙目操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}// 成員形式操作符函數Human& operator+=(const Human& r){this->m_age = this->m_age + r.m_age;this->m_name = this->m_name + "+" + r.m_name;return *this;}
private:int m_age;string m_name;friend Human operator+(const Human& l,const Human& r); // 友元聲明
};全局形式操作符函數
//Human& operator+(Human& l, const Human& r){
// l->m_age = l->m_age + r.m_age;
// l->m_name = l->m_name + "+" + r.m_name;
// return *l;
//}int main(void){Human a(22,"張飛"), b(20,"趙云"); // 非常左值const Human c(25,"關羽"), d(32,"馬超"); // 常左值((a+=b)+=c)+=Human(45,"黃忠");a.getInfo();return 0;
}
比較類雙目操作符:>、<、==、<=、>=等
- 左右操作數為非常左值、常左值或右值
- 表達式結果為 bool
// 比較類雙目操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}
// // 成員形式操作符函數
// bool operator==(/*const Human* this */ const Human& that)const{
// return this->m_age==that.m_age && this->m_name==that.m_name;
// }
// bool operator!=(/*const Human* this */ const Human& that)const{return this->m_age!=that.m_age || this->m_name!=that.m_name;
// return !(*this==that);//使用operator==
// }
private:int m_age;string m_name;friend bool operator==(const Human& l, const Human& r); // 友元聲明friend bool operator!=(const Human& l, const Human& r); // 友元聲明
};
// 全局形式操作符函數
bool operator==(const Human& l, const Human& r){return l.m_age==r.m_age && l.m_name==r.m_name;
}
bool operator!=(const Human& l, const Human& r){
// return l.m_age!=r.m_age || l.m_name!=r.m_name;return !(l==r);//使用operator==
}int main(void){Human a(22,"張飛"), b(20,"趙云"); // 非常左值const Human c(25,"關羽"), d(32,"馬超"); // 常左值cout << (a == b) << endl; //0 ==> a.operator==(b) 或者 ...cout << (a != b) << endl; //1 ==> a.operator!=(b) 或者 ...cout << (c == d) << endl; //0 ==> c.operator==(d) 或者 ...cout << (c != d) << endl; //1 ==> c.operator!=(d) 或者 ...cout << (Human(45,"黃忠") == Human(35,"劉備")) << endl; //0 ==> Human(45,"黃忠").operator==(Human(35,"劉備")) 或者 ...cout << (Human(45,"黃忠") != Human(35,"劉備")) << endl; //1 ==> Human(45,"黃忠").operator!=(Human(35,"劉備")) 或者 ...return 0;
}
經典單目操作符
運算類單目操作符:-、~、!等
- 操作數為非常左值、常左值或右值
- 表達式的結果為右值
// 運算類單目操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}// 成員形式操作符函數Human operator-(/* const Human* this */)const{return Human(-this->m_age,("-"+this->m_name).c_str());}
private:int m_age;string m_name;
// friend Human operator-(const Human& l); // 友元聲明
};//Human operator-(const Human& l){
// return Human(-l.m_age,("-"+l.m_name).c_str());
//}
int main(void){Human a(22,"張飛"), b(20,"趙云"); // 非常左值const Human c(25,"關羽"), d(32,"馬超"); // 常左值Human res = -a; // ==> a.operator-() 或 ...res.getInfo();//姓名:-張飛, 年齡:-22res = -c; // ==> c.operator-() 或 ...res.getInfo();//姓名:-關羽, 年齡:-25res = -Human(45,"黃忠"); // ==> Human(45,"黃忠").operator-() 或 ...res.getInfo();//姓名:-黃忠, 年齡:-45return 0;
}
前自增減類單目操作符: 前++、前–
- 操作數為非常左值
- 表達式的結果為操作數本身(而非副本)
后自增減類單目操作符: 后+ +、后–
- 操作數為非常左值
- 表達式的結果為右值,且為自增減以前的值
// 自增減類單目操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}//成員形式操作符函數
// // 前++ ++a
// Human& operator++(/* Human* this */){
// this->m_age+=1; // 直接加1
// this->m_name+="a"; // 直接加
// return *this;
// }后++ a++
// Human operator++(/* Human* this */int){
// Human old = *this; // 備份原來的值
// this->m_age+=1; // 直接加1
// this->m_name+="a"; // 直接加
// return old; // 返回原來的值
// }
private:int m_age;string m_name;friend Human& operator++(Human& l); // 友元聲明friend Human operator++(Human& l,int); // 友元聲明
};// 前++ ++aHuman& operator++(Human& l){l.m_age+=1; // 直接加1l.m_name+="a"; // 直接加return l;}// 后++ a++Human operator++(Human& l,int){Human old = l; // 備份原來的值l.m_age+=1; // 直接加1l.m_name+="a"; // 直接加return old; // 返回原來的值}int main(void){Human a(22,"張飛"), b(20,"趙云"); // 非常左值const Human c(25,"關羽"), d(32,"馬超"); // 常左值//姓名:張飛a, 年齡:23(++a).getInfo(); // a.operator++() 或 ...//姓名:趙云, 年齡:20(/*|...|*/b++).getInfo(); // b.operator++(0) 或 ...//姓名:趙云a, 年齡:21b.getInfo();return 0;
}
其他操作符
輸出操作符: <<
- 左操作數為非常左值形式的輸出流(ostream)對象,右操作數為左值或右值
- 表達式的結果為左操作數本身(而非副本)
- 左操作數的類型為ostream,若以成員函數形式重載該操作符,就應將其定義為ostream類的成員,該類為標準庫提供,無法添加新的成員,因此只能以全局函數形式重載該操作符
ostream& operator< < (ostream& os, const RIGHT& right) { ...}
// 輸入/輸出流操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}
/* void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}*/// 成員形式操作符函數
private:int m_age;string m_name;friend ostream& operator<<(ostream& os, const Human& that);friend istream& operator>>(istream& is, Human& that);
};
// 全局形式操作符函數
ostream& operator<<(ostream& os, const Human& that){os << "姓名:" << that.m_name << ", 年齡:" << that.m_age;return os;
}//istream& operator>>(istream& is, Human& that){
// is >> that.m_name >> that.m_age;
// return is;
//}int main(void){Human a(22,"張飛"); // 非常左值const Human b(20,"趙云"); // 常左值cout << a << endl; // cout.operator<<(a) 或 operator<<(cout,a)cout << b << endl; // cout.operator<<(b) 或 operator<<(cout,b)cout << Human(45,"黃忠") << endl; // cout.operator<<(Human(45,"黃忠")) 或 operator<<(cout,Human(45,"黃忠"))// cin >> a; // cin.operator>>(a) 或 operator>>(cin,a)cout << a << endl;return 0;
}
輸入操作符: >>
- 左操作數為非常左值形式的輸入流(istream)對象,右操作數為非常左值
- 表達式的結果為左操作數本身(而非副本)
- 左操作數的類型為istream,若以成員函數形式重載該操作符,就應將其定義為istream類的成員,該類為標準庫提供,無法添加新的成員,因此只能以全局函數形式重載該操作符
istream& operator>> (istream& is, RIGHT& right) { ...}
// 輸入/輸出流操作符函數
#include <iostream>
using namespace std;class Human{
public:Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){// [int m_age = age;]// [string m_name(name);]}
/* void getInfo(){cout << "姓名:" << m_name << ", 年齡:" << m_age << endl;}*/// 成員形式操作符函數
private:int m_age;string m_name;friend ostream& operator<<(ostream& os, const Human& that);friend istream& operator>>(istream& is, Human& that);
};
// 全局形式操作符函數
ostream& operator<<(ostream& os, const Human& that){os << "姓名:" << that.m_name << ", 年齡:" << that.m_age;return os;
}istream& operator>>(istream& is, Human& that){is >> that.m_name >> that.m_age;return is;
}int main(void){Human a(22,"張飛"); // 非常左值cin >> a; // cin.operator>>(a) 或 operator>>(cin,a)cout << a << endl;return 0;
}
下標操作符:[]
- 一般用于在容器類型中以下標方式獲取數據元素
- 非常容器的元素為非常左值,常容器的元素為常左值
類型轉換操作符
若源類型是基本類型,目標類型是類類型,則只能通過類型轉換構造函數實現自定義類型轉換
class 目標類型{目標類型(const 源類型& src){ ... }
}
若源類型是類類型,目標類型是基本類型,則只能通過類型轉換操作符函數 實現自定義類型轉換
class 源類型{operator 目標類型(void) const { ...}
}
若源類型和目標類型都是類類型 (而非基本類型) ,則既可以通過類型轉換構造函數也可以通過類型轉換操作符函數實現自定義類型轉換,但不要兩者同時使用
若源類型和目標類型都是基本類型,則無法實現自定義類型轉換,基本類型間的類型轉換規則完全由編譯器內置
操作符重載的局限
不是所有的操作符都能重載,以下操作符不能重載
- 作用域限定操作符(
::
) - 直接成員訪問操作符(
.
) - 條件操作符(
?:
) - 字節長度操作符(
sizeof
) - 類型信息操作符(
typeid
)
無法重載所有操作數均為基本類型的操作符: 如實現 1+1=3
c++的前++和后++
- 在C語言中
前++: 先加1,再使用 ++a
后++: 先使用,再加1 a++ - 在C++語言中,不管是前++還是后++,都是直接加1(內部原理和C語言并不同)
但是C++希望用戶感覺和C一樣
c++的前++和后++的區別
- 表達式方式區別:i++是先取變量i,再將變量i值+1;而++i是先將變量i值+1,再取變量i。在循環遍歷容器變量時,這兩種方式的結果都是一樣的,但是,本質的效率上有很大的區別,下面介紹另一種效率區別。
- 效率:兩種方式iterator遍歷的次數是相同的,但在STL中效率不同,前++返回引用,后++返回一個臨時對象,因為iterator是類模板,使用 it++這種形式要返回一個無用的臨時對象,而it++是函數重載,所以編譯器無法對其進行優化,所以每遍歷一個元素,你就創建并銷毀了一個無用的臨時對象。C++的標準庫,還有符合標準C++的教材,除了特殊需要和對內置類型外,基本都是使用++it來進行元素遍歷的,不管是源代碼還是教材中都是如此。
下面是標準庫源碼: