C++:友元
前言:友元對(duì)于我來說一直是一個(gè)難點(diǎn),最近看了一些有關(guān)友元的課程與博客,故在此將自己的學(xué)習(xí)收獲做一個(gè)簡(jiǎn)單的總結(jié)
一、什么是友元
在C++的自定義類中,一個(gè)常規(guī)的成員函數(shù)聲明往往意味著:
• 該成員函數(shù)能夠訪問其所在類的私有部分
• 該成員函數(shù)位于其所在類的作用域之中
• 該成員函數(shù)必須由一個(gè)對(duì)象去激活從而被調(diào)用(通過this指針來實(shí)現(xiàn))
如果將一個(gè)函數(shù)聲明為另一個(gè)類的友元,則可以使該函數(shù)只具有上面的第一個(gè)特性,即可以使該函數(shù)訪問類中的私有部分。
友元函數(shù)的語(yǔ)法:friend+普通函數(shù)聲明 友元類的語(yǔ)法: friend+類名(不是對(duì)象名) 友元成員函數(shù)的語(yǔ)法:friend+成員函數(shù)的聲明
[注]:如果將類A聲明為類B的友元類,則類A中的所有成員函數(shù)都可以訪問類B的私有部分,即類A中的成員函數(shù)都是類B的友元函數(shù)。
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 class Student;
6 void show(Student &);//一個(gè)函數(shù)或類在類中被聲明為友元時(shí),其在類外必須要有聲明!
7 class Student{
8 public:
9 friend void show(Student &);//將函數(shù)show聲明為類Student的友元函數(shù)
10 Student()=default;
11 Student(string name,int age):Name(name),Age(age){}
12 private:
13 string Name;
14 int Age;
15 };
16
17 void show(Student &stu){
18 cout<<"Name:"<<stu.Name<<endl;
19 cout<<"Age:"<<stu.Age<<endl;
20 }
21
22 int main(){
23 Student stu("Tomwenxing",23);
24 show(stu);
25 return 0;
26 }
特別注意:
1.友元函數(shù)不是類的成員函數(shù)。和一般函數(shù)相比友元函數(shù)可以訪問類中的所有成員,而一般函數(shù)只能訪問類中的非公有成員
2.友元函數(shù)不受類中的訪問權(quán)限關(guān)鍵字的限制,因此可以把友元函數(shù)或友元類的聲明放在類的任意位置(不管該位置是公有、私有還是受保護(hù)),其結(jié)果是相同的(建議將友元函數(shù)或友元類的聲明放在類定義最開始的地方)
3.友元函數(shù)的作用域并非其聲明所在類的作用域。如果友元函數(shù)是另一個(gè)類的成員函數(shù),則其作用域和另一個(gè)類的作用域;否則該友元函數(shù)的作用域和一般函數(shù)的作用域相同
二、為什么使用友元
1.使用友元可以實(shí)現(xiàn)類之間的數(shù)據(jù)共享,減少系統(tǒng)的開銷,提高效率
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 class Age;//聲明類
6 class Name{
7 public:
8 friend Age;//將類Age聲明為類Name的友元類
9 Name()=default;
10 Name(string name){
11 this->name=name;
12 }
13 void show1(const Age&);
14 private:
15 string name;
16 };
17
18 class Age{
19 public:
20 friend Name;//將類Name聲明為類Age的友元類
21 Age()=default;
22 Age(int age){
23 this->age=age;
24 }
25 void show2(const Name&);
26 private:
27 int age;
28 };
29 void Name::show1(const Age& age){
30 cout<<"調(diào)用類Name中的成員函數(shù)show1"<<endl;
31 cout<<"Name:"<<name<<endl;
32 cout<<"Age:"<<age.age<<endl;
33 }
34 void Age::show2(const Name& name){
35 cout<<"調(diào)用類Age中的成員函數(shù)show2"<<endl;
36 cout<<"Name:"<<name.name<<endl;
37 cout<<"Age:"<<age<<endl;
38 }
39
40 int main(){
41 Name name("Tomwenxing");
42 Age age(23);
43 name.show1(age);
44 cout<<"------------分界線----------------"<<endl;
45 age.show2(name);
46 return 0;
47 }
上例中Name類和Age類互為友元類,從而實(shí)現(xiàn)了類Age和類Name之間的數(shù)據(jù)共享。
2.運(yùn)算符重載的某些場(chǎng)合需要使用友元
Example 1:重載+號(hào)時(shí)利用友元實(shí)現(xiàn)“加法的交換律”
先來看一個(gè)有關(guān)復(fù)數(shù)加法的例子:
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 class Complex;//對(duì)類Comple進(jìn)行聲明
6 class Complex{
7 public:
8 Complex():real(0),img(0){} //默認(rèn)構(gòu)造函數(shù)
9 Complex(int r,int i):real(r),img(i){} //帶參數(shù)的構(gòu)造函數(shù)
10 void show(){ //打印復(fù)數(shù)
11 cout<<"("<<real<<","<<img<<")"<<endl;
12 }
13 Complex operator+(const Complex &c){ //對(duì)+進(jìn)行重載
14 return Complex(real+c.real,img+c.img);
15 }
16 Complex operator+(const int &value){
17 return Complex(real+value,img);
18 }
19 private:
20 int real; //復(fù)數(shù)實(shí)部
21 int img; //復(fù)數(shù)虛部
22 };
23
24 int main(){
25 Complex c1(100,20);
26 Complex c2(100,30);
27 Complex sum1=c1+c2;
28 sum1.show();
29 Complex sum2=c1+10;
30 sum2.show();
31 return 0;
32 }
上例中Comple對(duì)象和Complex對(duì)象或int型整數(shù)的加法本質(zhì)上是調(diào)用類中的成員函數(shù),即語(yǔ)句sum1=c1+c2等價(jià)于sum1=c1.operator+(c2),語(yǔ)句sum2=c1+10等價(jià)于sum2=c1.operator+(10) ,因此這兩條語(yǔ)句可以在系統(tǒng)中可以順利執(zhí)行。但如果main函數(shù)中出現(xiàn)如下語(yǔ)句時(shí),編譯器會(huì)報(bào)錯(cuò):
1 Comple sum3=10+c1; //錯(cuò)誤! 2 sum3.show();
這是由于10是int型整數(shù)而非Complex類的對(duì)象,因而無法調(diào)用類中的成員函數(shù)operator+()來完成Complex對(duì)象和int型整數(shù)的加法,換句話說就是如果僅僅在類中對(duì)+進(jìn)行重載是無法使對(duì)象在和int型整數(shù)進(jìn)行加法時(shí)滿足加法的交換律。這時(shí)候如果想解決這個(gè)問題,就需要借助友元的力量了,如下:
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 class Complex;//對(duì)類Comple進(jìn)行聲明
6 Complex operator+(const int&,const Complex&); //對(duì)函數(shù)進(jìn)行聲明
7 class Complex{
8 public:
9 friend Complex operator+(const int&,const Complex&);
10 Complex():real(0),img(0){} //默認(rèn)構(gòu)造函數(shù)
11 Complex(int r,int i):real(r),img(i){} //帶參數(shù)的構(gòu)造函數(shù)
12 void show(){ //打印復(fù)數(shù)
13 cout<<"("<<real<<","<<img<<")"<<endl;
14 }
15 Complex operator+(const Complex &c){ //對(duì)+進(jìn)行重載
16 return Complex(real+c.real,img+c.img);
17 }
18 Complex operator+(const int &value){
19 return Complex(real+value,img);
20 }
21 private:
22 int real; //復(fù)數(shù)實(shí)部
23 int img; //復(fù)數(shù)虛部
24 };
25
26 Complex operator+(const int &value,const Complex &c){
27 return Complex(value+c.real,c.img);
28 }
29 int main(){
30 Complex c(100,20);
31 Complex sum1=c+10;
32 sum1.show();
33 cout<<"----------分界線-------------"<<endl;
34 Complex sum2=10+c;
35 sum2.show();
36 return 0;
37 }
此時(shí)語(yǔ)句sum2=10+c相當(dāng)于sum2=operator+(10,c),從而實(shí)現(xiàn)了加法的交換律
Example 2:對(duì)>>和<<的重載
我們希望對(duì)>>和<<進(jìn)行重載,從而使Comple對(duì)象可以直接使用cout和cin。那么只在類中對(duì)運(yùn)算符>>和<<進(jìn)行重載是否可以?我們可以先來試一下:
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 class Complex{
6 public:
7 Complex():real(0),img(0){} //默認(rèn)構(gòu)造函數(shù)
8 Complex(int r,int i):real(r),img(i){} //帶參數(shù)的構(gòu)造函數(shù)
9 void show(){ //打印復(fù)數(shù)
10 cout<<"("<<real<<","<<img<<")"<<endl;
11 }
12 ostream& operator<<(ostream &out) const{
13 out<<"("<<real<<","<<img<<")";
14 return out;
15 }
16 istream& operator>>(istream &in){
17 in>>real>>img;
18 return in;
19 }
20 private:
21 int real; //復(fù)數(shù)實(shí)部
22 int img; //復(fù)數(shù)虛部
23 };
24
25 int main(){
26 Complex c;
27 cout<<"請(qǐng)輸入復(fù)數(shù):";
28 c>>cin; //輸入復(fù)數(shù)
29 c<<cout;//輸出對(duì)象
30 return 0;
31 }
由上面的例子可以看出其實(shí)是可以的,但由于對(duì)運(yùn)算符>>和<<的使用本質(zhì)上是對(duì)類中成員函數(shù)的調(diào)用,因此完成對(duì)復(fù)數(shù)進(jìn)行輸入操作的語(yǔ)句是c>>cin(相當(dāng)于c.operator>>(cin)),而完成對(duì)復(fù)數(shù)進(jìn)行輸出操作的語(yǔ)句時(shí)c<<out(相當(dāng)于c.operator<<(cout)),但這和我們平時(shí)的操作習(xí)慣有很大不同,并且可讀性也很差。為了解決這個(gè)問題,我們需要借助友元的力量:
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 class Complex;//聲明類
6 ostream& operator<<(ostream&,const Complex&);//聲明函數(shù)
7 istream& operator>>(istream&,Complex&);//聲明函數(shù)
8 class Complex{
9 public:
10 friend ostream& operator<<(ostream &out,const Complex &c);//聲明為友元函數(shù)
11 friend istream& operator>>(istream &in,Complex &c);
12 Complex():real(0),img(0){} //默認(rèn)構(gòu)造函數(shù)
13 Complex(int r,int i):real(r),img(i){} //帶參數(shù)的構(gòu)造函數(shù)
14 void show(){ //打印復(fù)數(shù)
15 cout<<"("<<real<<","<<img<<")"<<endl;
16 }
17 private:
18 int real; //復(fù)數(shù)實(shí)部
19 int img; //復(fù)數(shù)虛部
20 };
21 ostream& operator<<(ostream &out,const Complex &c){
22 out<<"("<<c.real<<","<<c.img<<")";
23 return out;
24 }
25 istream& operator>>(istream &in,Complex &c){
26 in>>c.real>>c.img;
27 return in;
28 }
29 int main(){
30 Complex c;
31 cout<<"請(qǐng)輸入復(fù)數(shù):";
32 cin>>c;//輸入復(fù)數(shù)
33 cout<<c;//輸出對(duì)象
34 return 0;
35 }
此時(shí)語(yǔ)句cin>>c相當(dāng)于operator>>(cin,c),而語(yǔ)句cout<<c相當(dāng)于operator<<(cout,c),從而完成所期望的功能
三、友元的特別注意事項(xiàng)
1.切記友元函數(shù)不是類的成員函數(shù),故編譯器不會(huì)在友元函數(shù)中隱式地插入this指針
2.友元是不能被繼承的,原因很簡(jiǎn)單: “父親的朋友不一定也是兒子的朋友”
3.友元破壞了類的封裝性,因此使用友元時(shí)必須要是是十分慎重
總結(jié)
- 上一篇: 中央特约评论员是什么意思 抖音中央特约评
- 下一篇: 抖音无水印解析 Python