C++友元函数的分析和理解

参考链接
http://blog.csdn.net/insistgogo/article/details/6608672
http://www.cnblogs.com/fzhe/archive/2013/01/05/2846808.html

1、为什么要引入友元函数:在实现类之间数据共享时,减少系统开销,提高效率
    具体来说:为了使其他类的成员函数直接访问该类的私有变量
    即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数
    优点:能够提高效率,表达简单、清晰
    缺点:友元函数破环了封装机制,尽量不使用成员函数,除非不得已的情况下才使用友元函数。

2、什么时候使用友元函数:
    1)运算符重载的某些场合需要使用友元。
    2)两个类要共享数据的时候

3、怎么使用友元函数
    友元函数的参数
       因为友元函数没有this指针,则参数要有三种情况:
       1、  要访问非static成员时,需要对象做参数;--常用(友元函数常含有参数)
       2、  要访问static成员或全局变量时,则不需要对象做参数
       3、  如果做参数的对象是全局对象,则不需要对象做参数

    友元函数的位置
       因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且没有区别。

    友元函数的调用
       可以直接调用友元函数,不需要通过对象或指针


    友元函数的分类
    根据这个函数的来源不同,可以分为三种方法:
1、普通函数友元函数:
    a) 目的:使普通函数能够访问类的友元
    b) 语法:声明位置:公有私有均可,常写为公有
           声明: friend + 普通函数声明
           实现位置:可以在类外或类中
           实现代码:与普通函数相同(不加不用friend和类::)
           调用:类似普通函数,直接调用


2、类Y的所有成员函数都为类X友元函数—友元类
    a)目的:使用单个声明使Y类的所有函数成为类X的友元
           它提供一种类之间合作的一种方式,使类Y的对象可以具有类X和类Y的功能
           具体来说:
               前提:A是B的友元(=》A中成员函数可以访问B中有所有成员,包括私有成员和公有成员--老忘)
               则:在A中,借助类B,可以直接使用~B . 私有变量~的形式访问私有变量

    b)语法:声明位置:公有私有均可,常写为私有(把类看成一个变量)
           声明: friend + 类名---不是对象啊
           调用:


3、类Y的一个成员函数为类X的友元函数
    a)目的:使类Y的一个成员函数成为类X的友元
           具体而言:而在类Y的这个成员函数中,借助参数X,可以直接以X。私有变量的形式访问私有变量
     b)语法:声明位置:声明在公有中 (本身为函数)
            声明:friend + 成员函数的声明
            调用:先定义Y的对象y---使用y调用自己的成员函数---自己的成员函数中使用了友元机制


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)注意:
        把函数声明非模板函数:
friend ostream& operator<< (ostream& cout,const MGraph& G);
 
        把函数声明为模板函数:
friend ostream& operator<< <>(ostream& cout,const MGraph<VexType,ArcType>& G);  
或:
friend ostream& operator<< <VexType,ArcType>(ostream& cout,const MGraph<VexType,ArcType>& G); 

        说明:
            在函数声明中加入operator<< <>:是将operator<<函数定义为函数模板,将函数模板申明为类模板的友员时,是一对一绑定的
            实际的声明函数:这里模板参数可以省略,但是尖括号不可以省略


使用友元类时注意:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

友元函数和类的成员函数的区别:成员函数有this指针,而友元函数没有this指针。

记忆:A是B的友元《=》A是B的朋友《=》借助B的对象,在A中可以直接 通过B。成员变量(可以是公有,也可以为私有变量) 的方式访问B



//测试一:普通函数友元函数
//////////////////////////////////////////////////////////////////////////
class IntegerNumber
{
//////////////////////////////////////////////////////////////////////////
public:
    void SetNumber(int a, int b, int c)
    {
        m_nPublicNumber = a;
        m_nProtectedNumber = b;
        m_nPrivateNumber= c;
    }

//////////////////////////////////////////////////////////////////////////
public:
     void PrintPublicNumber()
     {
         cout<<"[PrintPublicMethod] Public Number is " << m_nPublicNumber << endl;
     }

protected:
    void PrintProtectedNumber()
    {
        cout<<"[PrintProtectedMethod] Protected Number is " << m_nProtectedNumber << endl;
    }

private:
    void PrintPrivateNumber()
    {
        cout<<"[PrintPrivateMethod] Private Number is " << m_nPrivateNumber << endl;
    }

//////////////////////////////////////////////////////////////////////////
//由于友元函数没有this指针,所以一般要求用户传入友元函数所在类的一个对象
//结论一:声明友元函数,友元函数的访问权限不受 public | protected | private 影响
public:
    friend void PublicFriend(IntegerNumber& number)
    {
        cout << "--------------------------------------------------------\n";
                                                                                             //结论二:友元函数可以访问对象的所有成员
        cout << "[PublicFriend] Public Number is " << number.m_nPublicNumber << endl;        //友元函数可以访问公有成员
        cout << "[PublicFriend] Protecdted Number is " << number.m_nProtectedNumber << endl; //友元函数可以访问保护成员
        cout << "[PublicFriend] Private Number is " << number.m_nPrivateNumber << endl;      //友元函数可以访问私有成员

                                         ////结论三:友元函数可以访问对象的所有方法
        number.PrintPublicNumber();      //友元函数可以访问公有方法
        number.PrintProtectedNumber();   //友元函数可以访问保护方法
        number.PrintPrivateNumber();     //友元函数可以访问私有方法
    }

protected:
    friend void ProtectedFriend(IntegerNumber& number)
    {
        cout << "--------------------------------------------------------\n";
        cout << "[ProtectedFriend] Public Number is " << number.m_nPublicNumber << endl;        //友元函数可以访问公有成员
        cout << "[ProtectedFriend] Protecdted Number is " << number.m_nProtectedNumber << endl; //友元函数可以访问保护成员
        cout << "[ProtectedFriend] Private Number is " << number.m_nPrivateNumber << endl;      //友元函数可以访问私有成员

        number.PrintPublicNumber();      //友元函数可以访问公有方法
        number.PrintProtectedNumber();   //友元函数可以访问保护方法
        number.PrintPrivateNumber();     //友元函数可以访问私有方法
    }

private:
     friend void PrivateFriend(IntegerNumber& number)
     {
         cout << "--------------------------------------------------------\n";
         cout << "[PrivateFriend] Public Number is " << number.m_nPublicNumber << endl;        //友元函数可以访问公有成员
         cout << "[PrivateFriend] Protecdted Number is " << number.m_nProtectedNumber << endl; //友元函数可以访问保护成员
         cout << "[PrivateFriend] Private Number is " << number.m_nPrivateNumber << endl;      //友元函数可以访问私有成员

         number.PrintPublicNumber();      //友元函数可以访问公有方法
         number.PrintProtectedNumber();   //友元函数可以访问保护方法
         number.PrintPrivateNumber();     //友元函数可以访问私有方法
     }

//////////////////////////////////////////////////////////////////////////
public:
    int m_nPublicNumber;

protected:
    int m_nProtectedNumber;

private:
    int m_nPrivateNumber;
};

//////////////////////////////////////////////////////////////////////////
void FriendMethodTest()
{
    IntegerNumber cNumber;
    cNumber.SetNumber(8, 9, 10);

    //友元函数只能采取以下直接调用的方式
    //////////////////////////////////////////////////////////////////////////
    PublicFriend(cNumber);//直接调用
    ProtectedFriend(cNumber);//直接调用
    PrivateFriend(cNumber);//直接调用


    // C2039: 'PublicFriend' : is not a member of 'IntegerNumber'
    //cNumber.PublicFriend(cNumber);   //友元函数无法采用一般成员函数的调用方式

    //error C2039: 'ProtectedFriend' : is not a member of 'IntegerNumber'
    //cNumber.ProtectedFriend(cNumber); //友元函数无法采用一般成员函数的调用方式

    //error C2039: 'PrivateFriend' : is not a member of 'IntegerNumber'
    //cNumber.PrivateFriend(cNumber);     //友元函数无法采用一般成员函数的调用方式
}

void NormalMethodTest()
{
    IntegerNumber cNumber;
    cNumber.SetNumber(8, 9, 10);

    //正常情况下,对象只能访问公有成员,无法访问保护成员和私有成员。
    //////////////////////////////////////////////////////////////////////////
    cout << "Public Number is " << cNumber.m_nPublicNumber << endl;        //一般对象可以访问公有成员

    //error C2248: 'IntegerNumber::m_nProtectedNumber' : cannot access protected member declared in class 'IntegerNumber'
    //cout << "Protecdted Number is " << cNumber.m_nProtectedNumber << endl; //一般对象无法访问保护成员

    //error C2248: 'IntegerNumber::m_nPrivateNumber' : cannot access private member declared in class 'IntegerNumber'
    //cout << "Private Number is " << cNumber.m_nPrivateNumber << endl;      //一般对象无法访问私有成员


    //正常情况下,对象只能访问公有方法,无法访问保护方法和私有方法。
    //////////////////////////////////////////////////////////////////////////
    cNumber.PrintPublicNumber();    //一般对象可以访问公有方法

    //error C2248: 'IntegerNumber::PrintProtectedNumber' : cannot access protected member declared in class 'IntegerNumber'
    //cNumber.PrintProtectedNumber(); //一般对象无法访问保护方法

    //error C2248: 'IntegerNumber::PrintPrivateNumber' : cannot access private member declared in class 'IntegerNumber'
    //cNumber.PrintPrivateNumber();   //一般对象无法访问私有方法
}


void main()
{
    NormalMethodTest();
    FriendMethodTest();
}





//测试二:指定其他类的方法为自己的友元函数
class Girl;

class Boy
{
public:
    Boy(int nId, int nAge)
    {
        m_nID = nId;
        m_nAge = nAge;
    }

    void Display(Girl& girl);

private:
    int m_nID;
    int m_nAge;
};

class Girl
{
public:  
    Girl(int nId, int nAge)
    {
        m_nID = nId;
        m_nAge = nAge;
    }

    //声明类Boy的成员函数DispGirl()为类Girl的友元函数
    friend void Boy::Display(Girl& girl);

private:
    int m_nID;
    int m_nAge;
};

void Boy::Display(Girl& girl)
{
    //访问自己(Boy)的对象成员,直接访问自己的私有变量 
    cout  <<  "Boy's id is:"  <<  m_nID  <<  ",age:"  <<  m_nAge  <<  endl;

    //借助友元,在Boy的成员函数disp中,借助Girl的对象,直接访问Girl的私有变量
    //正常情况下,只允许在Girl的成员函数中访问Girl的私有变量  
    cout  <<  "Girl's id is:"  <<  girl.m_nID<<  ",age:"  <<  girl.m_nAge  <<endl;
}

void FriendMethodByOtherClassTest()
{   
    Boy b(123, 18);
    Girl g(345, 20);
    b.Display(g);
}

void main()
{
    FriendMethodByOtherClassTest();
}




//测试三:友元类,可以访问类的全部函数,可以理解成特殊的友元函数
class Wife;
class Husband
{  
private:
    int m_nMoney;

public:  
    Husband(int nMoney)
    {
        m_nMoney = nMoney;
    }

    void Display(Wife& wife);
};

class Wife
{
private:
    int m_nMoney;

    friend Husband;   //声明类Husband是类Wife的友元类

public:
    Wife(int nMoney)
    {
        m_nMoney = nMoney;
    }
};

//函数Display()为类Husband的成员函数,也是类Wife的友元函数 
void Husband::Display(Wife& wife) 
{ 
    //正常情况,Husband的成员函数disp中直接访问Husband的私有变量
    cout  <<  "Husband's money is:"  <<  m_nMoney  <<  endl;

    //借助友元类,在Husband的成员函数disp中,借助Wife的对象,直接访问Wife的私有变量
    //正常情况下,只允许在Wife的成员函数中访问Wife的私有变量
    cout  <<  "Wife's money is:"  <<  wife.m_nMoney  <<endl;
}

void FrientClassTest()
{   
    Husband h(123);
    Wife w(456);
    h.Display(w);  //h调用自己的成员函数,但是以w为参数,友元机制体现在函数Display中
}

void main()
{
    FrientClassTest();
}




//测试四:友元函数进行操作符重载
#include <iostream>
#include <string>
using namespace std;

class rect {
    int x1, y1, x2, y2;    //矩形座标
public:
    rect() {
        x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    }
    rect(int m1, int n1, int m2, int n2) {
        x1 = m1, y1 = n1, x2 = m2, y2 = n2;
    }
    void print() {
        cout << "  x1=" << x1;
        cout << "  y1=" << y1;
        cout << "  x2=" << x2;
        cout << "  y2=" << y2;
        cout << endl;
    }
    //rect operator++();                 //这是类的运算符的重载
    friend rect operator++(rect &ob);   //这是全局运算符的重载
};

rect operator++(rect &ob) {
    ob.x1++, ob.y1++; 
    ob.x2++, ob.y2++;

    return ob;
}

int main ( )
{
    rect r(12, 20, 50, 40);
    r.print();

    rect obj;
    obj = r++;
    obj.print();

    return 0;
}

猜你喜欢

转载自jacky-dai.iteye.com/blog/2305426