原文链接:https://blog.csdn.net/zhuhanyoua/article/details/61201007

C++中的友元机制允许类的非公有成员被一个类或者函数访问,友元按类型分为三种:普通非类成员函数作为友元,类的成员函数作为友元,类作为友元。友元包括友元的声明以及友元的定义。友元的声明默认为了extern,就是说友元类或者友元函数的作用域已经扩展到了包含该类定义的作用域,所以即便我们在类的内部定义友元函数也是没有关系的。

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。

友元函数的实现可以在类外定义,但必须在类内部声明

友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,

但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend。

我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率(即减少了类型检查和安全性检查等都需要的时间开销),但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

1.普通的非成员函数友元

[cpp] view plaincopy 
  1. #include "cmath"
  2. #include "iostream"
  3. using namespace std;
  4. class Point
  5. {
  6. public:
  7. Point(double xx,double yy)
  8. {
  9. x=xx;
  10. y=yy;
  11. }
  12. void GetXY();
  13. friend double Distance(Point &a,Point &b);
  14. protected:
  15. private:
  16. double x,y;
  17. };
  18. void Point::GetXY()
  19. {
  20. //cout<<"("<<this->x<<","<<this->y<<")"<<endl;
  21. cout<<"("<<x<<","<<y<<")"<<endl;
  22. }
  23. double Distance(Point &a,Point &b)
  24. {
  25. double length;
  26. length=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));     //它可以引用类中的私有成员
  27. return length;
  28. }
  29. int main(void)
  30. {
  31. Point p1(3.0,4.0),p2(6.0,8.0);
  32. p1.GetXY();    //成员函数的调用方法,通过使用对象来调用
  33. p2.GetXY();
  34. double d = Distance(p1,p2);     //友元函数的调用方法,同普通函数的调用一样,不要像成员函数那样调用
  35. cout<<d<<endl;
  36. system("pause");
  37. return 0;
  38. }

说明:在该程序中的Point类中说明了一个友元函数Distance(),它在说明时前边加friend关键字,标识它不是成员函数,而是友元函数。它的定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。但是,它可以引用类中的私有成员,函数体中的a.x,b.x,a.y,b.y都是类的私有成员,它们是通过对象引用的。在调用友元函数时,也是同普通函数的调用一样,不要像成员函数那样调用。本例中,p1.Getxy()和p2.Getxy()这是成员函数的调用,要用对象来表示。而Distance(p1, p2)是友元函数的调用,它直接调用,不需要对象表示,它的参数是对象。(该程序的功能是已知两点坐标,求出两点的距离。)

下面对上面的代码进行输入、输出流的重载:

[cpp] view plaincopy 
  1. #include <cmath>
  2. #include <iostream>
  3. using namespace std;
  4. class Point
  5. {
  6. public:
  7. Point(double xx,double yy)
  8. {
  9. x=xx;
  10. y=yy;
  11. }
  12. void GetXY();
  13. friend double Distance(Point &a,Point &b);
  14. friend ostream &operator <<(ostream &a,Point &b);
  15. protected:
  16. private:
  17. double x,y;
  18. };
  19. // friend ostream& operator<<(ostream& o,A& another);
  20. ostream &operator <<(ostream &out,Point &b)   //在类中声明的时候,可以是ostream &a,函数定义的时候也可以是ostream &out
  21. {
  22. out<<"("<<b.x<<","<<b.y<<")"<<endl;
  23. return out;
  24. }
  25. void Point::GetXY()
  26. {
  27. //cout<<"("<<this->x<<","<<this->y<<")"<<endl;
  28. //cout<<"("<<x<<","<<y<<")"<<endl;
  29. cout<<*this;
  30. }
  31. double Distance(Point &a,Point &b)
  32. {
  33. double length;
  34. length=sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
  35. return length;
  36. }
  37. int main(void)
  38. {
  39. Point p1(3.0,4.0),p2(6.0,8.0);
  40. p1.GetXY();
  41. p2.GetXY();
  42. double d = Distance(p1,p2);
  43. cout<<d<<endl;
  44. system("pause");
  45. return 0;
  46. }

2.类作为友元

类作为友元需要注意的是友元类和原始类之间的相互依赖关系,如果在友元类中定义的函数使用到了原始类的私有变量,那么就需要在友元类定义的文件中包含原始类定义的头文件。但是在原始类的定义中(包含友元类声明的那个类),就不需要包含友元类的头文件.
另外,不需要在类定义前去声明友元类,因为友元类的声明自身就是一种声明。

[cpp] view plaincopy 
  1. //A.h
  2. #pragma once
  3. #include <iostream>
  4. using namespace std;
  5. class A
  6. {
  7. //friend class B;  //如果不写这句话将会出现编译错误
  8. public:
  9. ~A(void);
  10. A();
  11. private:
  12. int m_nItem;
  13. };
  14. //A.cpp
  15. #include "A.h"
  16. A::A()
  17. {
  18. m_nItem =3;
  19. }
  20. A::~A(void)
  21. {
  22. }
  23. //B.h
  24. #pragma once
  25. class B
  26. {
  27. public:
  28. B(void);
  29. ~B(void);
  30. int func();
  31. };
  32. //B.cpp
  33. #include "StdAfx.h"
  34. #include "B.h"
  35. #include "A.h" //must include A.h
  36. #include <iostream>
  37. B::B(void)
  38. {
  39. }
  40. B::~B(void)
  41. {
  42. }
  43. int B::func()
  44. {
  45. cout<<"This is in B"<<endl;
  46. A a;
  47. return a.m_nItem;
  48. }

3.类成员函数作为友元函数

这个稍微有点复杂,因为你要类成员函数作为友元,你在声明友元的时候要用类限定符,所以必须先定义包含友元函数的类,但是在定义友元的函数时候,又必须事先定义原始类。通常的做法先定义包含友元函数的类,再定义原始类,这个顺序不能乱。(如果是友元类,则没有这种这种必须)如下面所示:

[cpp] view plaincopy 
  1. //A.h
  2. #pragma once
  3. #include "B.h"
  4. class A
  5. {
  6. friend int B::func(A xx);
  7. public:
  8. A(void):mx(20),my(30){}
  9. ~A(void){}
  10. private:
  11. int mx;
  12. int my;
  13. };
[cpp] view plaincopy 
  1. //B.h
  2. #pragma once
  3. class A;
  4. class B
  5. {
  6. public:
  7. B(void);
  8. ~B(void);
  9. int func(A xx);
  10. };
[cpp] view plaincopy 
  1. //B.cpp
  2. #include "B.h"
  3. #include "A.h"
  4. B::B(void)
  5. {
  6. }
  7. B::~B(void)
  8. {
  9. }
  10. int B::func(A xx)
  11. {
  12. return xx.mx * xx.my;
  13. }
[cpp] view plaincopy 
  1. //main.cpp
  2. #include "A.h"
  3. #include "B.h"
  4. #include <iostream>
  5. using namespace std;
  6. void main()
  7. {
  8. A a;
  9. B b;
  10. cout<<b.func(a)<<endl;
  11. system("pause");
  12. }

4. 友元不具有相互性,只具有单项性

若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。

5. 友元不能被继承

B是A的友元类,C是B的子类,推不出C是A的友元

6. 友元不具有传递性

B是A的友元,C是B的友元,推不出C是A的友元

7. 友元函数的使用技巧

在用C++实现单例模式时,可以利用友元函数实例化对象。然后把类的构造函数和析构函数都设计成私有函数。

[cpp] view plaincopy 
  1. class CMySingleton
  2. {
  3. public:
  4. friend CMySingleton& InstanceMEC();
  5. private:
  6. CMySingleton() {};
  7. CMySingleton(const CMySingleton &lxSington) {};
  8. ~CMySingleton(){};
  9. };
  10. CMySingleton& InstanceMEC()
  11. {
  12. //因为函数InstanceMEC()是类ClxSingletonMEC的友元函数,所以可以访问类所有的成员函数.所以不会有编译错误
  13. static CMySingleton Instance;
  14. return Instance;
  15. }

C++中的friend详细解析相关推荐

  1. Android开发中的WMS详细解析

    /   今日科技快讯   / 近日,小冰公司宣布对旗下人工智能数字员工产品线启动年度升级.本次升级加强的技术包括大模型对话引擎.3D神经网络渲染.超级自然语音及AIGC人工智能内容生成.小冰公司计划将 ...

  2. Python中的self详细解析

    1. 前言 我们总会在class里面看见self,但是感觉他好像也没什么用处,就是放在那里占个位子. 如果你也有同样的疑问,那么恭喜你,你的class没学明白. 所以,在解释self是谁之前,我们先明 ...

  3. JavaScript异步编程【中】 -- Promise 详细解析

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在ES6中,新增加了一种异步编程的解决方案Promise,它是一种规范,是一套处理JavaScript异步的机制. Promise的含义 简单来说, ...

  4. python 获取json中最大值_详细解析 Python 爬取 bilibili 的视频、弹幕以及封面

    本文使用 Zhihu On VSCode 创作并发布 环境 用到的 Python 库: Python 3.7 requests moviepy json re os 浏览器:Firefox/ 83.0 ...

  5. Java中的enum详细解析------全面掌握Java的enum类

    枚举类型是JDK5.0的新特征.Sun引进了一个全新的关键字enum来定义一个枚举类.下面就是一个典型枚举类型的定义: Java代码  public enum Color{        RED,BL ...

  6. WebService中的WSDL详细解析

    WebService中的WSDL详解 有人在WebService开发的时候,特别是和第三方有接口的时候,走的是SOAP协议,然后用户(或后台)给你一个WSDL文件(或网址),说按照上面的进行适配, 这 ...

  7. python里面self_Python中的self详细解析

    ​ 在介绍Python的self用法之前,先来介绍下Python中的类和实例-- 我们知道,面向对象最重要的概念就是类(class)和实例(instance),类是抽象的模板,比如学生这个抽象的事物, ...

  8. Java中的enum详细解析------Java enum 枚举还可以这么用

    在大部分编程语言中,枚举类型都会是一种常用而又必不可少的数据类型,Java中当然也不会例外.然而,Java中的Enum枚举类型却有着许多你意想不到的用法,下面让我们一起来看看. 1.可以在enum中添 ...

  9. Java中的enum详细解析------Java 语言中 Enum 类型的使用介绍

    Enum 类型的介绍 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中.而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常 ...

最新文章

  1. 中介分析 相对直接效应 相对简介效应_中介效应分析方法和流程
  2. mybatis学习(53):构造方法映射
  3. 微课|中学生可以这样学Python(6.5节):lambda表达式
  4. CSS的float和position
  5. Java进阶:Maven高级
  6. R 语言求正态分布的极大似然估计
  7. CronTrigger说明
  8. ubuntu 无法定位软件包
  9. 华为云计算论述题解析(1)
  10. Win10自带超级截屏利器
  11. Progressive Layered Extraction (PLE)
  12. 力扣刷题 DAY_84 贪心
  13. <a>标签 禁止点击 button禁止点击
  14. Flutter 制霸全平台?这事儿我看有戏。
  15. 配置windows系统中 PHP的环境变量
  16. hsgcc:面试笔记
  17. 菜鸡记录之初试自动更新,源码及出现的问题
  18. YOLO系列 --- YOLOV7算法(二):YOLO V7算法detect.py代码解析
  19. osg 三维gis开发_3D GIS与BIM的美丽邂逅(艾三维BIM分享)
  20. winwods 10 移动硬盘无法弹出

热门文章

  1. 祝各位节日快乐!20151111
  2. Java_Web使用简单的批处理操作
  3. WSDL中文版——详解
  4. day18 正则表达式
  5. 五、curator recipes之选举主节点Leader Latch
  6. EIGRP协议邻居详解及故障实战分析
  7. MySQL系列:性能优化
  8. PHP实现定时任务的几种方法
  9. LeetCode - 28. Implement strStr()
  10. Cisco交换机中的flash,Rom,RAM,nvram的区别