首页 > 编程笔记 > C++笔记

C++友元函数和友元类

类中的成员通过权限控制符实现了数据的封装,若对象要访问类中的私有数据,则只能通过成员函数实现。

这种方式实现了数据的封装却增加了开销,有时候需要通过外部函数或类直接访问其他类的私有成员,为此 C++ 提供了友元,使用友元可以访问类中的所有成员,函数和类都可以作为友元。

C++友元函数

友元函数可以是类外定义的函数或者是其他类中的成员函数,若在类中声明某一函数为友元函数,则该函数可以操作类中的所有数据。

接下来分别讲解类外定义的普通函数作为类的友元函数和类成员函数作为友元函数的用法。

1) 普通函数作为友元函数

将普通函数作为类的友元函数,在类中使用 friend 关键字声明该普通函数就可以实现,友元函数可以在类中任意位置声明。

普通函数作为类的友元函数的声明格式如下所示:

class 类名
{
friend 函数返回值类型 友元函数名(形参列表);
...   //其他成员
}


【示例1】通过案例演示普通函数作为友元函数的用法,C++ 代码如下:
#include<iostream>
using namespace std;
class Circle
{
friend void getArea(Circle &circle);    //声明普通函数getArea()为友元函数
private:
    float _radius;
    const float PI=3.14;
public:
    Circle(float radius);
    ~Circle();
};
Circle::Circle(float radius=0):_radius(radius)
{
    cout<<"初始化圆的半径为:"<<_radius<<endl;
}
Circle::~Circle(){}
void getArea(Circle &circle)
{
    //访问类中的成员变量
    cout<<"圆的半径是:"<<circle._radius<<endl;
    cout<<"圆的面积是"<<circle.PI*circle._radius*circle._radius<<endl;
    cout<<"友元函数修改半径:"<<endl;
    circle._radius=1;
    cout<<"圆的半径是:"<<circle._radius<<endl;
} 
int main()
{
    Circle circle(10);
    getArea(circle);
    return 0;
}
运行结果:

初始化圆的半径为:10
圆的半径是:10
圆的面积是314
友元函数修改半径:
圆的半径是:1

示例分析:
从运行结果 可以看出,普通函数作为友元函数访问了类中的私有成员,且具有修改私有成员的权限。

2) 其他类的成员函数作为友元函数

其他类中的成员函数作为本类的友元函数时,需要在本类中表明该函数的作用域,并添加友元函数所在类的前向声明,其语法格式如下:

class B;      //声明类B
class A
{
public:
    int func();     //声明成员函数func()
};
class B
{
    friend int A::func();   //声明类A的成员函数func()为友元函数
}


【示例2】通过案例演示类的成员函数作为其他类的友元函数的用法,C++ 代码如下:
#include<iostream>
#include<math.h>
using namespace std;
class Point;
class Circle
{
public:
    float getArea(Point &p1,Point &p2);   //声明计算面积的成员函数
private:
    const float PI=3.14;
};
class Point
{
//声明类Circle的成员函数getArea()为友元函数
friend float Circle::getArea(Point &p1,Point &p2);  
public:
    Point(float x,float y);
    ~Point();
private:
    float _x;
    float _y;
};
Point::Point(float x=0,float y=0):_x(x),_y(y)  //实现Point类的构造函数
{
    cout<<"初始化坐标点"<<endl;
}
Point::~Point(){}
float Circle::getArea(Point &p1,Point &p2)
{
    double x=abs(p1._x-p2._x);      //获取横轴坐标间的距离
    float y=abs(p1._y-p2._y);     //获取纵轴坐标间的距离
    float len=sqrtf(x*x+y*y);    //计算两个坐标点之间的距离
    cout<<"获取两个坐标点之间的距离是"<<len<<endl;
    return len*len*PI;       //友元函数访问私有成员变量PI
}
int main()
{
    Point p1(5,5);
    Point p2(10,10);
    Circle circle;
    float area=circle.getArea(p1,p2);
    cout<<"圆的面积是:"<<area<<endl;
    return 0;
}
运行结果:

初始化坐标点
初始化坐标点
获取两个坐标点之间的距离是7.07107
圆的面积是:157

示例分析:

C++友元类

除了可以声明函数为类的友元函数,还可以将一个类声明为友元类,友元类可以声明在类中任意位置。声明友元类之后,友元类中的所有成员函数都是该类的友元函数,能够访问该类的所有成员。

与声明友元函数类似,友元类也是使用关键字 friend 声明,其语法格式如下:

class B;         //类B前向声明
class A
{
};
class B
{
    friend class A;       //声明类A是类B的友元类
}


【示例3】通过案例演示友元类的用法,C++ 代码如下:
#include<iostream>
using namespace std;
class Time        //定义Time类,描述时分秒
{
public:
    Time(int hour, int minute, int second);   //声明有参构造函数
    friend class Date;        //声明类Date为友元类
private:
    int _hour, _minute, _second;
};
class Date        //定义Date类
{
public:
    Date(int year, int month, int day);   //声明有参构造函数
    void showTime(Time& time);      //声明显示时间的成员函数
private:
    int _year, _month, _day;
};
Date::Date(int year, int month, int day)    //实现Date类构造函数
{
    _year = year;
    _month = month;
    _day = day;
}
void Date::showTime(Time& time)
{
    cout << _year << "-" << _month << "-" << _day
    << " " << time._hour << ":" << time._minute
    << ":" << time._second << endl;
}
Time::Time(int hour,int minute,int second)   //实现Time类构造函数
{
    _hour = hour;
    _minute = minute;
    _second = second;
}
int main()
{
    Time time(17,30,20);      //创建Time对象
    Date date(2019,10,31);       //创建Date对象
    date.showTime(time);      //调用showTime()显示年月日、时分秒信息
    return 0;
}
运行结果:

2021-9-26 11:36:20

示例分析:

使用友元应注意的地方

从面向对象程序设计来讲,友元破坏了封装的特性。但由于友元简单易用,因此在实际开发中较为常用,如数据操作、类与类之间消息传递等,可以提高访问效率。

使用友元需要注意以下几点:
  1. 友元声明位置由程序设计者决定,且不受类中 public、private、protected 权限控制符的影响。
  2. 友元关系是单向的,即类 A 是类 B 的友元,但 B 不是 A 的友元。
  3. 友元关系不具有传递性,即类 C 是类 D 的友元,类 E 是类 C 的友元,但类 E 不是类 D 的友元。
  4. 友元关系不能被继承。

声明:《C++系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。