一. 友元(friend)
友元是一種定義在類外部的普通函數(shù)或類
1 友元函數(shù)
1.1 全局函數(shù)作為友元函數(shù)
聲明一個全局函數(shù)作為類的友元函數(shù),則允許該全局函數(shù),訪問類中各個權(quán)限下的成員
在類中要將該函數(shù)進行聲明:friend 全局函數(shù)頭;
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
int age;
public:
Stu() {}
Stu(string n, int a) : name(n), age(a) {}
void show()
{
cout << "name = " << name << " age = " << age << endl;
}
friend void display(Stu s); // 在類中將全局函數(shù)聲明成類的友元函數(shù)
};
// 定義全局函數(shù)
void display(Stu s)
{
cout << "name = " << s.name << " age = " << s.age << endl; // 正常的全局函數(shù)不能訪問類中私有屬性
}
int main()
{
display(Stu("zhangpp", 18));
return 0;
}
1.2 類的成員函數(shù)作為友元函數(shù)(了解)
-
聲明一個其他類的成員函數(shù)作為自己類的友元函數(shù),則允許讓該函數(shù)訪問自己類中的所有成員
-
要求該函數(shù)必須類內(nèi)聲明類外定義
2. 友元類
-
在一個類中聲明另一個類當做友元類,則允許友元類中所有成員訪問自己的所有權(quán)限下的成員
-
聲明格式:friend class 類名;
#include <iostream>
using namespace std;
class Stu; // 類的前置聲明
class Teacher // 老師類
{
private:
string sub;
public:
Teacher() {}
Teacher(string s) : sub(s) {}
void show(Stu s); // 類內(nèi)聲明
};
class Stu // 學生類
{
private:
string name;
int age;
public:
Stu() {}
Stu(string n, int a) : name(n), age(a) {}
void show()
{
cout << "name = " << name << " age = " << age << endl;
}
friend void display(Stu s); // 在類中將全局函數(shù)聲明成類的友元函數(shù)
// friend void Teacher::show(Stu s); //聲明老師類中的成員函數(shù)作為友元函數(shù)
// 將整個Teacher類當做友元類
friend class Teacher;
};
// 定義全局函數(shù)
void display(Stu s)
{
cout << "name = " << s.name << " age = " << s.age << endl; // 正常的全局函數(shù)不能訪問類中私有屬性
}
// 老師類中的成員函數(shù)的定義
void Teacher::show(Stu s)
{
cout << "Teacher::sub = " << sub << endl;
cout << "Stu::name = " << s.name << endl; // 其他類不能訪問類中私有成員
}
int main()
{
display(Stu("zhangpp", 18));
Teacher t1("C++");
t1.show(Stu("zhangpp", 18));
return 0;
}
3. 使用友元的注意事項
-
友元具有方向性,A把B當做朋友,允許B訪問A的所有權(quán)限,但是A不一定能訪問B的所有權(quán)限
-
友元不具有交換性:A是B的朋友,但B不一定是A的朋友
-
友元不具有傳遞性:A是B的朋友,B是C的朋友,則A不一定是C的朋友
-
友元不具有繼承性:父類的朋友不一定是子類的朋友
-
由于友元的出現(xiàn),破壞了了類的封裝性,使得訪問權(quán)限形同虛設(shè)。所以不在萬不得已的情況下,盡可能少用友元
-
必須使用友元的情況:插入和提取運算符重載。(后期講)
二. 常成員函數(shù)和常對象(const)
1. 常成員函數(shù)
由const修飾的成員函數(shù)叫常成員函數(shù)
格式:
-
返回類型 成員函數(shù)名(參數(shù)表) const;
例如:int function(int x) const
?
特點:
-
在常成員函數(shù)中,不能修改成員變量的值(保護成員變量不被修改)
-
類中同名的常成員函數(shù)和非常成員函數(shù)構(gòu)成重載關(guān)系,原因是隱藏的this指針的類型不同
非常成員函數(shù)中: Stu * const this 常成員函數(shù)中: const Stu * const this
-
非常對象,優(yōu)先調(diào)用非常成員函數(shù),如果沒有非常成員函數(shù),則去調(diào)用同名的常成員函數(shù)
-
常對象只能調(diào)用常成員函數(shù),沒有常成員函數(shù)會報錯
2. 常對象
const修飾的對象為常對象
-
常對象只能調(diào)用常成員函數(shù),不能調(diào)用非常成員函數(shù)
-
當一個常引用的目標為非常對象,則通過引用只能調(diào)用常成員函數(shù),通過對象優(yōu)先調(diào)用非常成員函數(shù)
3. mutable關(guān)鍵字
功能:
? 取消成員常屬性
使用方式:
? 定義變量前加mutable,那么,該變量就能在常成員函數(shù)中被修改
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
mutable int age; // 由mutable關(guān)鍵字修飾的成員變量,能在常成員函數(shù)中被修改
double score;
public:
Stu() {}
Stu(string n, int a, double s) : name(n), age(a), score(s) {}
// 有const修飾的成員函數(shù)就是常成員函數(shù)
void show() const // const Stu * const this;
{
// this = nullptr;
// this->score = 50; // 在常成員函數(shù)中不能修改成員變量的值
this->age = 100; // 可以更改,因為有關(guān)鍵字修飾
cout << "name = " << name << endl;
cout << "age = " << age << endl;
cout << "score = " << score << endl;
cout << "AAAAAAAAAAAAAAAAAAAAAA" << endl;
}
void show() // Stu * const this;
{
this->score = 50; // 在常成員函數(shù)中不能修改成員變量的值
cout << "name = " << name << endl;
cout << "age = " << age << endl;
cout << "score = " << score << endl;
cout << "BBBBBBBBBBBBBBBBBBBBBBBB" << endl;
}
};
int main()
{
Stu s1("張三", 20, 99);
s1.show(); // 50
const Stu &r = s1;
r.show(); // 常成員函數(shù)
s1.show(); // 優(yōu)先調(diào)用非常成員函數(shù)
return 0;
}
三. 運算符重載
單、算、關(guān)、邏、條、賦、逗
1. 定義
所謂運算符重載,就是給運算符新的含義,能夠?qū)崿F(xiàn)“一符多用”,也是屬于靜態(tài)多態(tài)的一種,
他能夠?qū)崿F(xiàn)將原本加載到基本數(shù)據(jù)類型的運算符,在自定義類對象減使用。
好處:能夠使得代碼更加簡潔、易懂,優(yōu)雅好看
2. 重載的方法
統(tǒng)一的名稱:
? operator# //#表示運算符
3. 運算符重載要求
功能:
? 實現(xiàn)運算符對應(yīng)的操作
參數(shù):
? 由運算符本身決定
返回值:
? 由用戶自己決定
4. 調(diào)用時機及調(diào)用原則
調(diào)用時機:當使用該運算符時,系統(tǒng)自動調(diào)用,無需手動調(diào)用
調(diào)用原則:左調(diào)右參 //a = b; a.operator=(b)
5. 運算符重載的格式
每種運算符都有兩個版本的格式:
-
成員函數(shù)版本,類對象本身就是一個參數(shù),形參個數(shù)是操作數(shù)個數(shù)-1
-
全局函數(shù)版,需要使用友元完成,此時參數(shù)個數(shù)等于操作數(shù)個數(shù)
以上兩個版本的重載函數(shù),只能實現(xiàn)一個,否則調(diào)用時會混亂報錯
5.1 算術(shù)類運算符重載(雙目運算符)
種類:
? +、-、*、/、%。。。
表達式:
? L#R //L表示左操作數(shù),#表示運算符,R表示右操作數(shù)
-
左操作數(shù):既可以是左值也可以是右值
-
右操作數(shù):既可以是左值也可以是右值
-
結(jié)果:右值
定義格式:
- 成員函數(shù)版:const 類名 operator#( const 類名 &R ) const
第一個const:保護返回結(jié)果不被改變
第二個const:保護右操作數(shù)不被修改
第三個const:保護左操作數(shù)不被修改
- 全局函數(shù)版:const 類名 operator#(const 類名 &L, const 類名 &R)
5.2 賦值類運算符重載
種類:
? =、-=、*=、/=、%=、+=。。。
表達式:
? L#R //L表示左操作數(shù),#表示運算符,R表示右操作數(shù)
-
左操作數(shù):只能是左值
-
右操作數(shù):既可以是左值也可以是右值
-
結(jié)果:右值
定義格式:
-
成員函數(shù)版: 類名 & operator#( const 類名 &R )
-
全局函數(shù)版: 類名 & operator#(類名 &L, const 類名 &R)
5.3 關(guān)系運算符重載
種類:
? >=、<=、!=、>、<、==。。。
表達式:
? L#R //L表示左操作數(shù),#表示運算符,R表示右操作數(shù)
-
左操作數(shù):既可以是左值也可以是右值
-
右操作數(shù):既可以是左值也可以是右值
-
結(jié)果:bool類型的右值
定義格式:
-
成員函數(shù)版: const bool operator#( const 類名 &R ) const
-
==全局函數(shù)版: const bool operator#( const 類名 &L,const 類名 &R ) ==
5.4 單目運算符重載
種類:
? &、!、-(負號)。。。
表達式:
? #O //#表示運算符 O表示操作數(shù)
-
操作數(shù):既可以是左值也可以是右值
-
結(jié)果:右值
定義格式:
-
成員函數(shù)版: const 類名 operator#( void ) const
-
全局函數(shù)版: const 類名 operator#( conts 類名 &O)
5.4 單目運算符重載
種類:
? &、!、-(負號)。。。
表達式:
? #O //#表示運算符 O表示操作數(shù)
-
操作數(shù):既可以是左值也可以是右值
-
結(jié)果:右值
定義格式:
-
成員函數(shù)版: const 類名 operator#( void ) const
-
全局函數(shù)版: const 類名 operator#( conts 類名 &O)
5.5 自增、自減運算符重載
前置自增:++a
表達式:
? #O //#表示運算符 O表示操作數(shù)
操作數(shù):只能是左值
結(jié)果:左值,自身的引用
定義格式:
成員函數(shù)版: 類名 & operator#( )
全局函數(shù)版: 類名 & operator#( 類名 &O )
后置自增:a++
表達式:
? O# //#表示運算符 O表示操作數(shù)
操作數(shù):只能是左值
結(jié)果:右值
定義格式:
成員函數(shù)版: 類名 operator#( int )
全局函數(shù)版: 類名 operator#( 類名 &O , int)
5.6 插入、提取運算符重載
- cin和cout的來源
namespace std
{
extern istream cin; /// Linked to standard input
extern ostream cout; /// Linked to standard output
}
從這兩個類對象可以看出,cin和cout來自于內(nèi)置對象,想要對<<和>>進行重載時,如果想要實現(xiàn)成員函數(shù)版,則需要多istream和ostream類進行修改,難度較大。
此時,我們可以使用全局函數(shù)版實現(xiàn)這兩個運算符的重載,將該全局函數(shù)設(shè)置成友元函數(shù)即可
因為要用到istream和ostream的類對象和自定義類對象,原則上來說需要在兩個類中都要設(shè)置成友元函數(shù)
但是,在運算符重載函數(shù)中,只會對自定義的類對象訪問私有成員,而不會對istream和ostream的類訪問私有成員
所以,最終只需要在自定義類中,將全局函數(shù)設(shè)置成友元函數(shù)即可
表達式:
? L#R (L是cin或cout #是<<或者>> R是自定義的類對象)
-
左操作數(shù):istream和ostream的類對象
-
右操作數(shù):自定義的類對象
-
結(jié)果:左操作數(shù)自身的引用
格式:
-
ostream &operator<<(ostream &L, const 類名 &O);
-
istream &operator>>(istream &L, const 類名 &O);
5.6 不能重載的運算符
-
成員運算符 .
-
成員指針運算符 .*
-
條件表達式 ?:
-
求字節(jié)運算 sizeof
-
作用域限定符 ::
5.7 運算符重載注意事項
-
運算符重載只能在已有的運算符基礎(chǔ)上進行重載,不能憑空捏造一個運算符
-
運算符重載不能更改運算符的本質(zhì):如不能在加法運算符重載中實現(xiàn)減法
-
運算符重載不能改變運算符的優(yōu)先級
-
運算符重載不能改變運算符的結(jié)合律
-
運算符重載函數(shù)不能設(shè)置默認參數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-422491.html
? L#R (L是cin或cout #是<<或者>> R是自定義的類對象)
-
左操作數(shù):istream和ostream的類對象
-
右操作數(shù):自定義的類對象
-
結(jié)果:左操作數(shù)自身的引用
格式:
-
ostream &operator<<(ostream &L, const 類名 &O);
-
istream &operator>>(istream &L, const 類名 &O);
5.6 不能重載的運算符
-
成員運算符 .
-
成員指針運算符 .*
-
條件表達式 ?:
-
求字節(jié)運算 sizeof
-
作用域限定符 ::
5.7 運算符重載注意事項
-
運算符重載只能在已有的運算符基礎(chǔ)上進行重載,不能憑空捏造一個運算符
-
運算符重載不能更改運算符的本質(zhì):如不能在加法運算符重載中實現(xiàn)減法
-
運算符重載不能改變運算符的優(yōu)先級
-
運算符重載不能改變運算符的結(jié)合律
-
運算符重載函數(shù)不能設(shè)置默認參數(shù)
-
成員函數(shù)版的參數(shù)要比全局函數(shù)版的參數(shù)少一個,原因是對象本身就是一個參數(shù)文章來源地址http://www.zghlxwxcb.cn/news/detail-422491.html
到了這里,關(guān)于c++學習(day4)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!