一、类的友元函数
分为友元全局函数和友元成员函数两种类型。
1.友元全局函数
形式如下图所示,使用friend关键字加普通函数声明的方式来声明一个类的友元函数,与普通函数的区别还在于参数上,必须传递当前这个类的对象或者引用或者指针,即必须使该友元函数能够访问到这个类的私有成员变量或者是保护的数据成员或者是私有或保护的成员函数。这样才能体现出友元的特性来。
2.友元全局函数的定义和使用
2.友元成员函数
声明形式如下图所示,因为友元函数是另一个类的成员函数,因此声明为一个类的友元函数的时候,必须加上类名::,以标识这个友元函数是哪一个类的成员函数,同时参数也必须是当前类的对象引用或指针,以便该友元函数能够访问到该类的私有 或保护成员变量和成员函数。
定义和使用
3.友元函数的风险
友元的特性会破坏Coordinate的封装性,不建议使用,除非特殊情况。
4.友元函数的声明位置没有有约束,但是建议放在类的最前面,先向别人暴露这个类是怎样向外面暴露的。
二、友元类
声明如下图所示,声明一个友元类的时候,直接用friend加类名的形式即可,但是需要注意的是,必须先在类前声明一下Circle类,只需要写上class Circle即可,即告诉计算机其实是有这个类的,现在这里声明的时候用一下,到后期编译的时候再把这个类的主体编译进来,这样就不会造成friend Circle;这句话产生未声明错误。
当我们把Circle类声明为Coordinate类的友元类以后,就可以在Circle类中定义一个Coordinate类的对象了,并且可以通过这个对象就可以任意的访问类Coordinate的私有成员变量和私有成员函数了。如下图所示,Circle类的任意成员函数都可以通过Coordinate类的对象访问该类的私有成员变量和私有成员函数了。
三、友元的特性
1、友元关系不可以传递,就像A是B的朋友,B是C的朋友,那么A不一定是C的朋友
2、友元关系的单向性,就像A是B的朋友,B不一定是A的朋友,所以一定要搞清楚两个类之间谁是谁的友元类。
3、友元声明的形式和数量不受限制,即声明的时候可以同时有 友元函数和友元类,而且在声明数量上不受限制。
4、友元只是对封装的补充,它并不是一个很好的语法,因为它破坏了封装性,体现了一种定向暴露的思想
四、实例代码:
必须写Time类的默认构造函数,不然Watch类的构造函数中声明参数为Time&t就会报错,如下所示;
/477/9676/BNIP/index.cpp: In constructor 'Watch::Watch(Time&)': /477/9676/BNIP/index.cpp:39:5: error: no matching function for call to 'Time::Time()' {
#include <iostream>
using namespace std;
class Watch;
/**
* 定义Time类
* 数据成员:m_iHour, m_iMinute,m_iSecond
* 成员函数:构造函数
* 友元类:Watch
*/
class Time
{
// 友元类
friend Watch;
public:
Time(){};//这里着重注意必须写默认构造函数不然Watch类的构造函数中声明参数为Time&t就会报错
Time(int hour, int min, int sec)
{
m_iHour = hour;
m_iMinute = min;
m_iSecond = sec;
}
private:
int m_iHour;
int m_iMinute;
int m_iSecond;
};
/**
* 定义Watch类
* 数据成员:m_tTime
* 成员函数:构造函数
* display用于显示时间
*/
class Watch
{
public:
Watch(Time & t)
{
m_tTime.m_iHour = t.m_iHour;
m_tTime.m_iMinute = t.m_iMinute;
m_tTime.m_iSecond = t.m_iSecond;
}
void display()
{
cout << m_tTime.m_iHour << endl;
cout << m_tTime.m_iMinute << endl;
cout << m_tTime.m_iSecond << endl;
}
public:
Time m_tTime;
};
int main()
{
Time t(6, 30, 20);
Watch w(t);
w.display();
return 0;
}