c++友元函数和友元类详解

我们在设计类时,一般将数据成员设计成私有的,体现面向对象的信息隐藏和封装性;这些私有的数据成员只能由类成员函数所访问,类外函数不能访问;当在某些情况下,我们可能又需要访问类对象的私有成员,那我们应该怎么办呢?

为了能够访问类私有成员,我们可以采用友元函数,在c++中以关键字friend加以声明,友元可以包括友元函数和友元类;友元函数又可以分为友元函数和友元成员函数;其一般形式分别如下:

友元函数:
friend 函数返回类型 函数名(形参列表)
形如:friend void Display(const CMyTime& time)

友元成员函数:
friend 函数返回类型 类型名::函数名(形参列表)
形如:friend void CMyDate::Display(const CMyTime& time)

友元类:
friend 类型名
形如friend CMyDate

友元函数

友元函数就是将类外的函数,并在本类中加以friend关键字声明,那么这个函数就是本类的友元函数;

下面就将普通函数声明为友元函数;

class CMyTime
{
public:
    CMyTime(int hour,int minute, int second);
    //全局函数Display是本类的友元函数,可以访问其私有数据成员
    friend void Display(const CMyTime& time);
private:
    int m_Hour;
    int m_Minute;
    int m_Second;
};

CMyTime::CMyTime(int hour,int minute, int second)
{
    this->m_Hour   = hour;
    this->m_Minute = minute;
    this->m_Second = second;
}

在这个例子中,Display函数是一个全局的普通函数,不属于任何类,没有this指针;我们将其设置为友元函数后就能访问CMyTime类的私有成员了,否则Display函数将会报错;

需要注意的是,为了能够引出类的成员函数,这个友元函数的形参一般是类对象的引用或者指针;
友元函数如下:

void Display(const CMyTime& time)
{
   //因为Display函数没有this指针,引用这些私有成员数据,需要指定对象
   cout << time.m_Hour   << ":"
        << time.m_Minute << ":"
        << time.m_Second << "
"
        << endl;
}

测试代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
    CMyTime time(12,13,14);
    Display(time);
    return 0;
}

运行结果:12:13:14

友元成员函数

通常情况下,类和类之间是相互隔离的,但有可能一个类中的成员函数需要访问另一个类中的私有成员,我们则可将类成员函数设置为友元函数,则可以到达这个目的;

比如 有一个日期类(CMyDate)对象和一个时间类(CMyTime)对象,要求一次性输出其中的日期和时间;

现在先设计一个CMyDate类,用成员函数Display完成输出日期和时间,具体实现如下:

//提前引用声明,表示存在CMyTime类名,类的具体内容后续再声明
//在没有具体声明CMyTime类的内容时,不允许定义对象和引用函数名
//比如在引用声明class CMyTime之后,添加对象定义CMyTime time 编译器将报错;
class CMyTime;  
class CMyTime* pTime = NULL;
//class CMyTime time; error 

class CMyDate
{
public:
    //构造函数
    CMyDate(int year,int month, int day);    
    //成员函数,输出具体时间和日期
    void Display(const CMyTime& time) const; 
private:
    int m_Year;
    int m_Month;
    int m_Day;
};

CMyDate::CMyDate(int year,int month, int day)
{
    this->m_Year  = year;
    this->m_Month = month;
    this->m_Day   = day;
}

class CMyTime
{
public:
    CMyTime(int hour,int minute, int second);
    //将CMyDate类中Display函数是CMyTime的友元成员函数
    friend void CMyDate::Display(const CMyTime& time) const;
private:
    int m_Hour;
    int m_Minute;
    int m_Second;
};

//CMyDate::Display函数实现需要放在类CMyTime声明之后,否则不清楚CMyTime类有哪些成员
void CMyDate::Display(const CMyTime& time) const
{   
   cout << m_Year  << "/"
        << m_Month << "/"
        << m_Day   << " " ;

   cout << time.m_Hour   << ":"
        << time.m_Minute << ":"
        << time.m_Second << "
"
        << endl;
}

测试代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
    CMyTime time(12,12,12);
    CMyDate date(2017,7,16);
    //time对象作为实参,输出时间,Display是CMyDate类成员函数,有this指针,作为日期的输出源;
    date.Display(time);
    return 0;
}

运行结果:2017/7/17 12:12:12

这里需要特别说明c++允许对类作“提前引用”的声明,即只先声明类名,不包含类体

若在当前函数中需要先引用(不是指引用符&)某个类对象作为形参,但这个类还未声明;我们可以在文件开头先进行类名声明,不声明类体,类体声明稍后再给出,如上述代码中的 class CMyTime

在对一个类作了“提前引用声明”后,可以用该类的名字去定义指向该类型对象的指针或者引用,因为定义一个指针变量和引用与这个类大小没有关系,但是不能用于定义对象,定义对象需要声明类体后才行
,如上述代码中的class CMyTime* pTime = NULL和Display的形参;

友元类

在c++中我们还可以将一个类设置为另一个类的友元类,这样友元类中的函数都可以访问另一个类的所有成员数据,并且友元类是单向的且不具备传递性,比如类A是类B的友元类,类B是类C的友元类,不能推出类A是类C的友元类,以及类B是类A的友元类;
友元类使用如下:

class CMyDate
{
public:
    //构造函数
    CMyDate(int year,int month, int day);    
    //成员函数
    void Display(const CMyTime& time) const; 
private:
    int m_Year;
    int m_Month;
    int m_Day;
};

class CMyTime
{
public:
    CMyTime(int hour,int minute, int second);
    //友元类, CMyDate是CMyTime的友元类
    friend  CMyDate;
private:
    int m_Hour;
    int m_Minute;
    int m_Second;
};

测试代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
    CMyTime time(12,12,12);
    CMyDate date(2017,7,16);
    //time对象作为实参,输出时间,Display是CMyDate类成员函数,有this指针,作为日期的输出源;
    date.Display(time);
    return 0;
}

运行结果:2017/7/17 12:12:12

如果没有了friend关键字声明,在Display函数中将会报如下错误:

error C2248: “CMyTime::m_Hour”: 无法访问 private 成员
error C2248: “CMyTime::m_Minute”: 无法访问 private 成员
error C2248: “CMyTime::m_Second”: 无法访问 private 成员

友元利弊分析

面向对象程序设计的一个基本原则就是封装性和信息隐蔽,而友元函数和友元类却可以访问其他类的私有成员,在一定程度上这是封装性的小破坏;
因此进行类设计时,不推荐将整个类设置为友元类,只将必要成员函数或者普通函数设置为友元类;
由于友元有助于数据共享,使代码更加简洁,但又违背封装性和信息隐蔽,因此进行代码开发时需要做好权衡;

原文地址:https://www.cnblogs.com/jinxiang1224/p/8468237.html