当前位置: 首页 > news >正文

C++友元:跨墙访问的三种姿势

目录

友元

友元之普通函数形式

友元之成员函数形式

友元类

友元的特点


友元

  • 什么叫友元?

一般来说,类的私有成员只能在类的内部访问,类之外是不能访问它们的。但如果将其他类/函数设置为类的友元,那么友元类/函数就可以在前一个类的类定义之外访问其私有成员了。用friend关键字声明友元

将类比作一个家庭,类的private 成员相当于家庭的秘密,一般的外人当然不允许探听这些秘密的,只有 friend 才有资格探听这些秘密。

友元的三种形式:普通函数、成员函数、友元类

友元之普通函数形式

示例:程序中有Point类,需要求取两个点的距离。按照设想,我们定义一个普通函数distance,接收两个Point对象作为参数,通过公式计算这两个点之间的距离。但Point的_ix和 _iy是私有成员,在类外不能通过对象访问,那么可以将distance函数声明为Point类的友元函数,之后就可以在distance函数中访问Point的私有成员了。

class Point{
public:Point(int x, int y): _ix(x), _iy(y){}
​friendfloat distance(const Point & lhs, const Point & rhs);
private:int _ix;int _iy;
};
​
float distance(const Point & lhs, const Point & rhs){return sqrt((lhs._ix - rhs._ix)*(lhs._ix - rhs._ix) +(lhs._iy - rhs._iy)*(lhs._iy - rhs._iy));
}

image-20240312161218053

友元之成员函数形式

假设类A有一个成员函数,该成员函数想去访问另一个类B类中的私有成员变量。这时候则可以在第二个类B中,声明第一个类A的那个成员函数为类B的友元函数,这样第一个类A的某个成员函数就可以访问第二个类B的私有成员变量了。

我们试验一下,以另一种方式实现上面的需求,如果distance函数不再是一个普通函数,而是Line类的一个成员函数,也就是说需要在一个类(Line)的成员函数中访问另一个类(Point)的私有成员,那么又该如何实现呢?

  • 如果将Point类定义在Line类之前,Line类的成员函数要访问Point类的私有成员,需要在Point类中将Line的这个成员函数设为友元函数——此时编译器并不认识Line类;

  • 如果将Line类定义在Point类之前,那么distance函数需要接受两个const Point &作为参数——此时编译器不认识Point类;

解决方法:

——在Line前面做一个Point类的前向声明;

——但如果将distance的函数体写在Line类中,编译器虽然知道了有一个Point类,但并不知道Point类具体有什么成员,所以此时在函数体中访问_ix、 _iy都会报错,编译器并不认识它们;

思考一下,有什么办法可以解决这个问题呢?

class Point;//前行声明
//只有前向声明,只知道有piont这个类,不知道point是怎么实现的,但不能访问point的内容,那么我们在line类中
//只做函数的声明,不做定义
class Line
{//需要前向声明,使编译器知道有piont这个类public:float destance(const Point &lhs,const Point &rhs);
};
class Point
{public:Point(int x,int y):_ix(x),_iy(y){}//friend friend float Line::destance(const Point &lhs,const Point &rhs);
​private:int _ix;int _iy;
};
​
//point 有什么东西编译器已经知道,现在对destance做定义就可以了,在外面是普通函数,就上作用域
float Line::destance(const Point &lhs,const Point &rhs)
{return sqrt(pow(lhs._ix - rhs._ix ,2)+pow(lhs._iy - rhs._iy,2));
}
 

image-20240312162223639

补充:

前向声明的用处:进行了前向声明的类,可以以引用或指针的形式作为函数的参数,只要不涉及到对该类对象具体成员的访问,编译器可以通过。

(让编译器认识这个类,但是注意如果只进行前向声明,这个类的具体实现没有的话,无法使用这个类的对象,无法创建)

注意:友元的声明要注意和函数的形式完全对应上。

友元类

如上的例子,假设类 Line 中不止有一个 distance 成员函数,还有其他成员函数,它们都需要访问Point 的私有成员,如果还像上面的方式一个一个设置友元,就比较繁琐了,可以直接将 Line 类设置为 Point 的友元类,在工作中这也是更常见的方法。

class Point {//...friend class Line;//...
};

在Point类中声明Line类是本类的友元类,那么Line类中的所有成员函数中都可以访问Point类的私有成员。一次声明,全部解决。

image-20240312163304583

不可否认,友元将类的私有成员暴露出来,在一定程度上破坏了信息隐藏机制,似乎是种“副作用很大的药”,但俗话说“良药苦口”。好工具总是要付出点代价的,拿把锋利的刀砍瓜切菜,总是要注意不要割到手指的。

友元的存在,使得类的接口扩展更为灵活,使用友元进行运算符重载从概念上也更容易理解一些,而且, C++ 规则已经极力地将友元的使用限制在了一定范围内。

友元的特点

  1. 友元不受类中访问权限的限制——可访问私有成员

  2. 友元破坏了类的封装性

  3. 不能滥用友元 ,友元的使用受到限制

  4. 友元是单向的——A类是B类的友元类,则A类成员函数中可以访问B类私有成员;但并不代表B类是A类的友元类,如果A类中没有声明B类为友元类,此时B类的成员函数中并不能访问A类私有成员

  5. 友元不具备传递性——A是B的友元类,B是C的友元类,无法推断出A是C的友元类

  6. 友元不能被继承——因为友元破坏了类的封装性,为了降低影响,设计层面上友元不能被继承


http://www.mrgr.cn/news/95699.html

相关文章:

  • C/C++蓝桥杯算法真题打卡(Day10)
  • Android 系统进程启动Activity方法说明
  • C++——引用
  • 【前端工程化】
  • (UI自动化测试web端)第二篇:元素定位的方法_name定位
  • 快速部署Samba共享服务器作为k8s后端存储
  • 3. 轴指令(omron 机器自动化控制器)——>MC_SetPosition
  • Python中json和jsonify的使用
  • 2025前端面试题记录
  • RabbitMQ八股文
  • 【解决方法】VMwareWorkstation无法连接到虚拟机。请确保您有权运行该程序、访问该程序使用的所有目录以及访问所有临时文件目录。
  • Ubuntu部署Docker搭建靶场
  • 练习用Jupyter使用selenium【疑问未解决版】
  • 【MySQL】基本查询(表的增删查改+聚合函数)
  • PostgreSQL_数据使用与日数据分享
  • 网络层之IP协议
  • Pytorch中的torch.utils.data.Dataset 类
  • 开发中常用的设计模式 用法及注意事项【面试题】
  • Flink启动任务
  • 常考计算机操作系统面试习题(四)