1. 程式人生 > >C++之中的友元函式的作用

C++之中的友元函式的作用

友元提供了不同類的成員函式之間、類的成員函式與一般函式之間進行資料共享的機制。通過友元,一個不同函式或另一個類中的成員函式可以訪問類中的私有成員和保護成員。c++中的友元為封裝隱藏這堵不透明的牆開了一個小孔,外界可以通過這個小孔窺視內部的祕密。

友元的正確使用能提高程式的執行效率,但同時也破壞了類的封裝性和資料的隱藏性,導致程式可維護性變差。
友元函式 :

友元函式是可以直接訪問類的私有成員的非成員函式。它是定義在類外的普通函式,它不屬於任何類,但需要在類的定義中加以宣告,宣告時只需在友元的名稱前加上關鍵字friend,其格式如下:
friend 型別 函式名(形式引數);

友元函式的宣告可以放在類的私有部分,也可以放在公有部分,它們是沒有區別的,都說明是該類的一個友元函式。
  一個函式可以是多個類的友元函式,只需要在各個類中分別宣告。
  友元函式的呼叫與一般函式的呼叫方式和原理一致。

友元類 :
友元類的所有成員函式都是另一個類的友元函式,都可以訪問另一個類中的隱藏資訊(包括私有成員和保護成員)。
當希望一個類可以存取另一個類的私有成員時,可以將該類宣告為另一類的友元類。定義友元類的語句格式如下:
friend class 類名;
其中:friend和class是關鍵字,類名必須是程式中的一個已定義過的類。


   1:  #include <iostream>

   2:  using namespace std;

   3: 

   4:  class Radius

   5:  {

   6
: friend class Circle; //宣告Circle為Radius的友元類 7: friend void Show_r(Radius &n); //宣告Show_r為友元函式 8: public: 9: Radius(int x) 10: { 11: r = x; 12: } 13: ~Radius() 14: { 15: } 16: 17: private:
18: int r; 19: }; 20: 21: void Show_r(Radius &n) 22: { 23: cout<<"圓的半徑為: "<<n.r<<endl; //呼叫Radius物件的私有成員變數r 24: } 25: 26: class Circle 27: { 28: public: 29: Circle() {} 30: ~Circle(){} 31: double area(Radius a) 32: { 33: s = a.r * a.r * 3.1415926; //呼叫Radius物件的私有成員變數r 34: return s; 35: } 36: private: 37: double s; 38: }; 39: 40: int main(int argc, char *argv[]) 41: { 42: Radius objRadius(9); 43: Circle objCircle; 44: 45: Show_r( objRadius ); 46: cout<<"面積為:"<<objCircle.area(objRadius)<<endl; 47: 48: return 0; 49: }

友元函式是可以直接訪問類的私有成員的非成員函式。它是定義在類外的普通函式,它不屬於任何類,但需要在類的定義中加以宣告,宣告時只需在友元的名稱前加上關鍵字friend,其格式如下:
friend 型別 函式名(形式引數);

友元函式的宣告可以放在類的私有部分,也可以放在公有部分,它們是沒有區別的,都說明是該類的一個友元函式。
  一個函式可以是多個類的友元函式,只需要在各個類中分別宣告。
  友元函式的呼叫與一般函式的呼叫方式和原理一致。

轉載部分:

1、為什麼要引入友元函式:在實現類之間資料共享時,減少系統開銷,提高效率

  具體來說:為了使其他類的成員函式直接訪問該類的私有變數

  即:允許外面的類或函式去訪問類的私有變數和保護變數,從而使兩個類共享同一函式

  優點:能夠提高效率,表達簡單、清晰

  缺點:友元函式破環了封裝機制,儘量不使用成員函式,除非不得已的情況下才使用友元函式。

2、什麼時候使用友元函式:

  1)運算子過載的某些場合需要使用友元。

  2)兩個類要共享資料的時候

3、怎麼使用友元函式:

友元函式的引數:

   因為友元函式沒有this指標,則引數要有三種情況:

   1、  要訪問非static成員時,需要物件做引數;--常用(友元函式常含有引數)

   2、  要訪問static成員或全域性變數時,則不需要物件做引數

   3、  如果做引數的物件是全域性物件,則不需要物件做引數

友元函式的位置:

   因為友元函式是類外的函式,所以它的宣告可以放在類的私有段或公有段且沒有區別。

友元函式的呼叫:

   可以直接呼叫友元函式,不需要通過物件或指標

友元函式的分類:

根據這個函式的來源不同,可以分為三種方法:

1、普通函式友元函式:

   a) 目的:使普通函式能夠訪問類的友元

   b) 語法:宣告位置:公有私有均可,常寫為公有

                    宣告: friend + 普通函式宣告

                    實現位置:可以在類外或類中

                    實現程式碼:與普通函式相同(不加不用friend和類::)

                    呼叫:類似普通函式,直接呼叫
class INTEGER
{  
private:
    int num;
public:
    friend void Print(const INTEGER& obj);//宣告友元函式
};
void Print(const INTEGER& obj)//不使用friend和類::
{
    //函式體
}
void main()
{
    INTEGER obj;
    Print(obj);//直接呼叫
}

2、類Y的所有成員函式都為類X友元函式—友元類

  a)目的:使用單個宣告使Y類的所有函式成為類X的友元

                    它提供一種類之間合作的一種方式,使類Y的物件可以具有類X和類Y的功能

                    具體來說:

                            前提:A是B的友元(=》A中成員函式可以訪問B中有所有成員,包括私有成員和公有成員--老忘)

                                則:在A中,藉助類B,可以直接使用~B . 私有變數~的形式訪問私有變數

  b)語法:宣告位置:公有私有均可,常寫為私有(把類看成一個變數)

                    宣告: friend + 類名---不是物件啊

                    呼叫:

  c)程式碼:
class girl;

class boy
{  
private:
    char *name;  
    int age;  
public:  
    boy();
    void disp(girl &);   
};  

void boy::disp(girl &x) //函式disp()為類boy的成員函式,也是類girl的友元函式 
{ 
    cout<<"boy's name is:"<<name<<",age:"<<age<<endl;//正常情況,boy的成員函式disp中直接訪問boy的私有變數
    cout<<"girl's name is:"<<x.name<<",age:"<<x.age<<endl; 
    //藉助友元,在boy的成員函式disp中,藉助girl的物件,直接訪問girl的私有變數
    //正常情況下,只允許在girl的成員函式中訪問girl的私有變數
}

class girl
{  
privatechar *name;  
    int age;  
    friend boy;   //宣告類boy是類girl的友元  
public:  
    girl();   
};  
void main()  
{   
    boy b;  
    girl g;  
    b.disp(g);  //b呼叫自己的成員函式,但是以g為引數,友元機制體現在函式disp中
}

3、類Y的一個成員函式為類X的友元函式

  a)目的:使類Y的一個成員函式成為類X的友元

         具體而言:而在類Y的這個成員函式中,藉助引數X,可以直接以X。私有變數的形式訪問私有變數

  b)語法:宣告位置:宣告在公有中 (本身為函式)

                    宣告:friend + 成員函式的宣告

                    呼叫:先定義Y的物件y---使用y呼叫自己的成員函式---自己的成員函式中使用了友元機制

  c)程式碼: 
class girl; 
class boy
{  
private:
    char *name;  
    int age;  
public:  
    boy();
    void disp(girl &);     
};   

class girl
{
private:
    char *name;  
    int age;  
public:  
    girl(char *N,int A);  
    friend void boy::disp(girl &); //宣告類boy的成員函式disp()為類girl的友元函式  
};  

void boy::disp(girl &x)  
{   
    cout<<"boy's name is:"<<name<<",age:"<<age<<endl;  //訪問自己(boy)的物件成員,直接訪問自己的私有變數  
    cout<<"girl's name is:"<<x.name<<",age:"<<x.age<<endl;  
    //藉助友元,在boy的成員函式disp中,藉助girl的物件,直接訪問girl的私有變數
    //正常情況下,只允許在girl的成員函式中訪問girl的私有變數  
}  
void main()  
{   
    boy b();  
    girl g();  
    b.disp(g);  }

4、在模板類中使用友元operator<<(對<<運算子的過載)

a)使用方法:

在模板類中宣告:

friend ostream& operator<< <>(ostream& cout,const MGraph<VexType,ArcType>& G); 

在模板類中定義:

template<class VexType,class ArcType>
ostream& operator<<(ostream& cout,const MGraph<VexType,ArcType>& G)
{
    //函式定義
}

b)注意:

把函式宣告非模板函式:
[cpp] view plain copy

friend ostream& operator<< (ostream& cout,const MGraph& G);  

把函式宣告為模板函式:
[cpp] view plain copy

friend ostream& operator<< <>(ostream& cout,const MGraph<VexType,ArcType>& G);  

或:
[cpp] view plain copy

friend ostream& operator<< <VexType,ArcType>(ostream& cout,const MGraph<VexType,ArcType>& G);  

說明:
在函式宣告中加入operator<< <>:是將operator<<函式定義為函式模板,將函式模板申明為類模板的友員時,是一對一繫結的
實際的宣告函式:這裡模板引數可以省略,但是尖括號不可以省略
[cpp] view plain copy

friend ostream& operator<< <VexType,ArcType>(ostream& cout,const MGraph<VexType,ArcType>& G);  

5、友元函式和類的成員函式的區別:成員函式有this指標,而友元函式沒有this指標。

6、記憶:A是B的友元《=》A是B的朋友《=》藉助B的物件,在A中可以直接 通過B。成員變數(可以是公有,也可以為私有變數) 的方式訪問B

分割線

因為友元函式是類外的函式所以友元函式的宣告放在了類外或則類內是沒有區別的
友元函式可以直接呼叫不需要通過物件或者指標