友元(C++學習筆記 28)
在不放棄私有成員(或保護成員)資料安全性的情況下,使得一個普通函式或者類的成員函式可以訪問到封裝在某一類中的資訊(私有、保護成員)。
C++中的友元為資料隱藏這堵不透明的牆開了一個小孔,外界可以通過這個小孔窺視類內部的祕密,友元是一扇通向私有(保護)成員的後門。如果一個類聲明瞭許多友元,則相當於在牆上開了好多小孔,顯然這將破壞資料的隱蔽性和類的封裝性,降低了程式的可維護性,因此使用友元函式應謹慎。
C++引入友元機制的目的:
①友元機制是對類的封裝機制的補充,利用友元機制,一個類可以賦予某些函式訪問它的私有成員的特權。如果沒有友元機制,外部函式訪問類的私有資料,必須通過呼叫公有的成員函式,這在需要頻繁呼叫私有資料的情況下,會帶來較大的開銷,從而降低程式的執行效率。
②友元提供了不同類的成員函式之間、類的成員函式與一般函式之間進行資料共享的機制。尤其當一個函式需要訪問多個類時,友元函式非常有用,普通的成員函式只能訪問其所屬的類,但是多個類的友元函式能夠訪問相關的所有類的資料。
友元包括 友元函式 和 友元類
一、友元函式
- 既可以是不屬於任何類的非成員函式,也可以是另一個類的成員函式,統稱為友元函式。
- 友元函式不是當前類的成員函式,而是獨立於當前類的外部函式,但它可以訪問該類所有的成員,包括私有成員、保護成員和公有成員。
- 在類中宣告友元函式時,需在其函式名前加上關鍵字 friend。此宣告可以放在公有部分,也可以放在保護部分和私有部分。
- 友元函式可以定義在類內部,也可以定義在類外部。
1、將非成員函式宣告為友元函式。
例 1:友元函式的使用。
#include<iostream>
#include<string.h>
using namespace std;
class Girl{
public:
Girl(char *n,int d){ //定義建構函式,為 *name 和 age賦初值
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
friend void disp(Girl &); //宣告disp函式為類Girl的友元函式
void show();
~Girl(){
delete name;
}
private:
char *name;
int age;
};
void disp (Girl &x){
cout<<"女孩的姓名是:"<<x.name<<";年齡:"<<x.age<<endl;
}
void Girl::show(){
cout<<"the girl's name is:"<<name<<";age is:"<<age<<endl;
}
int main(){
Girl g1("小紅",18);
disp(g1);
g1.show();
return 0;
}
說明:
(1)友元函式雖然可以訪問類物件的私有成員,但它畢竟不是成員函式,因此在類的外部定義友元函式時,不必像成員函式那樣,在函式名前加上“類名::”。
(2)因為友元函式不是類的成員,所以它不能直接訪問物件的資料成員,也不能通過this指標訪問物件的資料成員,它必須通過作為入口引數傳遞進來的物件名(或物件指標,物件引用)來訪問引用該物件的資料成員。
(3)由於函式disp是Girl類的友元函式,所以disp函式可以訪問Girl中的私有資料成員name和age。但在訪問name 和 age時,必須加上物件名 x。
例 2:一個函式同時定義為兩個類的友元函式。
例如有 Boy 和 Girl 兩個類,現要求打印出所有男生和女生的名字和年齡。只需一個獨立的函式 prdata 就能夠完成,但是它必須同時定義為這兩個類的友元函式。
#include<iostream>
#include<string.h>
using namespace std;
class Boy; //對Boy類的提前引用宣告,第8行使用到了Boy類
class Girl{
public:
Girl(char N[],int A);
friend void prdata(const Girl &,const Boy &); //宣告 prdata 函式為類 Girl 的友元函式
private:
char name[25];
int age;
};
Girl::Girl(char N[],int A){
strcpy(name,N);
age=A;
}
class Boy{
public:
Boy(char N[],int A);
friend void prdata(const Girl &plg,const Boy &plb); //宣告 prdata 函式為類 Boy 的友元函式
private:
char name[25];
int age;
};
Boy::Boy(char N[],int A){
strcpy(name,N);
age=A;
}
void prdata(const Girl &plg,const Boy &plb){ //定義函式 prdata 為類 Girl 和 Boy 的友元函式,
//形參 plg 和 plb 分別是 Girl 類和 Boy 類的物件的引用
cout<<"女孩的姓名:"<<plg.name<<"\n";
cout<<"女孩的年齡:"<<plg.age<<"\n";
cout<<"男孩的姓名:"<<plb.name<<"\n";
cout<<"男孩的年齡:"<<plb.age<<"\n";
}
int main(){
Girl g1("小紅",18);
Girl g2("小芳",17);
Girl g3("小夏",18);
Boy b1("小明",18);
Boy b2("小強",19);
Boy b3("小剛",18);
prdata(g1,b1);
prdata(g2,b2);
prdata(g3,b3);
return 0;
}
執行結果:
由於函式 prdata 被定義成類 Boy 和 Girl 的友元函式,所以它能夠訪問者兩個類中的所有資料(包括私有資料)。
2、將成員函式宣告為友元函式
一個類的成員函式也可以作為另一個類的友元,它是友元的一種,稱為友元成員函式。
友元成員函式不僅可以訪問自己所在類物件中的私有成員和公有成員,還可以訪問 friend 宣告語句所在類物件中的所有成員,這樣能使兩個類相互合作,協調工作,完成某一任務。
例 3:宣告 disp 為類 Girl 的成員函式,又是類 Boy 的友元函式。
#include<iostream>
#include<string.h>
using namespace std;
class Boy; //對Boy類的提前引用宣告,第8行使用到了Boy類
class Girl{
public:
Girl(char *n,int d){
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
void prdata(Boy &);
~Girl(){
delete name;
}
private:
char *name;
int age;
};
class Boy{
public:
Boy(char *n,int d){
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
friend void Girl::prdata(Boy &plb); //宣告類 Girl 的成員函式 prdata 函式為類 Boy 的友元函式
~Boy(){
delete name;
}
private:
char *name;
int age;
};
void Girl::prdata(Boy &x){
cout<<"女孩的姓名:"<<name<<"\n";
cout<<"女孩的年齡:"<<age<<"\n";
cout<<"男孩的姓名:"<<x.name<<"\n";
cout<<"男孩的年齡:"<<x.age<<"\n";
}
int main(){
Girl g1("小紅",18);
Boy b1("小明",18);
g1.prdata(b1);
return 0;
}
執行結果:
說明:
一個類的成員函式作為另一個類的友元函式時,必須先定義這個類。比如,在例3中類 Girl 的成員函式為類 Boy 的友元函式,必須先定義類 Girl,並且在宣告友元函式時要加上成員函式所在類的類名。
二、友元類
- 不僅函式可以作為一個類的友元,一個類也可以作為另一個類的友元,稱為友元類。
- 友元類的宣告是在另一個類宣告中加入語句:friend 類名;
此類名為友元類的類名。這條語句可以放在公有部分也可以放在私有部分。例如:
class Y{
······
};
class X{
······
friend Y; //宣告類Y為類X的友元類
······
};
當類 Y 被說明為類 X 的友元時,類 Y 的所有成員函式都成為類 X 的友元函式,即作為友元類 Y 中的所有成員函式都可以訪問類 X 中的所有成員(包括私有成員)。
例 4:友元類的應用。
#include<iostream>
#include<string.h>
using namespace std;
class Boy; //對Boy類的提前引用宣告,第8行使用到了Boy類
class Girl{
public:
Girl(char *n,int d){
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
void prdata(Boy &);
~Girl(){
delete name;
}
private:
char *name;
int age;
};
class Boy{
public:
Boy(char *n,int d){
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
friend Girl; //例4和例3的唯一一處不同
~Boy(){
delete name;
}
private:
char *name;
int age;
};
void Girl::prdata(Boy &x){
cout<<"女孩的姓名:"<<name<<"\n";
cout<<"女孩的年齡:"<<age<<"\n";
cout<<"男孩的姓名:"<<x.name<<"\n";
cout<<"男孩的年齡:"<<x.age<<"\n";
}
int main(){
Girl g1("小紅",18);
Boy b1("小明",18);
g1.prdata(b1);
return 0;
}
程式的執行結果同例3。
在例4中,聲明瞭類 Girl 為類 Boy 的友元類,則類 Girl 中的所有成員函式為類 Boy 的友元成員函式,因此類 Girl 中的成員函式 prdata 是類 Boy 的友元成員函式,它不但可以訪問類 Girl 中的所有成員,也可以訪問類 Boy 中的所有成員。
說明:
友元關係是單向的,不具有交換性。若聲明瞭類 X 是類 Y 的友元(即在類 Y 定義中宣告類 X 為friend類),不等於類 Y 一定是 X 的友元,這要看在類 X 中是否有相應的宣告。
友元關係也不具有傳遞性,若類 X 是類 Y 的友元,類 Y 是類 Z 的友元,不一定類 X 是類 Z 的友元。如果想讓類 X 是 類 Z 的友元類,應在類 Z 中作宣告。