当前位置: 首页 > 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)
    {}
​
    friend
    float 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. 友元不能被继承——因为友元破坏了类的封装性,为了降低影响,设计层面上友元不能被继承

相关文章:

  • MySQL小练习
  • 高速电路中的时序设计
  • 哪吒汽车:一边熬夜蹦迪,一边找药投医
  • easyExcel2.2.10中为0数据显示为空
  • 基于深度学习的行人人脸识别系统的设计与实现
  • 【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的日志管理:Logback 的集成
  • LeetCode-215. 数组中的第K个最大元素
  • 解决MediaMetadataRetriever.finalize()超时问题
  • CUDA与GPU架构:解锁并行计算的终极奥义
  • 2025中国AI Agent 行业研究报告|附文件下载
  • 《TCP/IP网络编程》学习笔记 | Chapter 21:异步通知 I/O 模型
  • Charles抓HTTPS包
  • 调用百度api实现语音识别(python)
  • CLion Debug查看指针数组元素
  • Kubernetes(k8s)-Pod亲和性(Affinity)和反亲和性(Anti-affinity)
  • PAQ压缩算法
  • 自动驾驶系统的车辆动力学建模:自行车模型与汽车模型的混合策略及自动驾驶分层控制架构
  • 3.24-3 接口测试断言
  • 面试中如何回答性能优化的问题
  • C语言 【实现电脑关机小游戏】非常好玩
  • 关税风暴下,3G资本拟94亿美元私有化美国鞋履巨头斯凯奇,溢价30%
  • 同为“东部重要中心城市”后交出首份季报:宁杭苏表现如何?
  • 金价大反攻,国内金饰价格涨回千元,能否重返巅峰?
  • 特朗普称不会为了和中国谈判而取消对华关税,外交部回应
  • “两高”出台司法解释,严打破坏黑土地资源犯罪
  • 思政课也精彩,“少年修齐讲堂”开讲《我的中国“芯”》