C++中的friend详细解析

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)); //a.x,b.x,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.   
  47. }  

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.   
  5. B::B(void)  
  6. {  
  7. }  
  8. B::~B(void)  
  9. {  
  10. }  
  11. int B::func(A xx)  
  12. {  
  13.     return xx.mx * xx.my;  
  14. }  
[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.   
  6. private:  
  7.     CMySingleton() {};  
  8.     CMySingleton(const CMySingleton &lxSington) {};  
  9.     ~CMySingleton(){};  
  10. };  
  11.   
  12. CMySingleton& InstanceMEC()  
  13. {  
  14.     //因为函数InstanceMEC()是类ClxSingletonMEC的友元函数,所以可以访问类所有的成员函数.所以不会有编译错误  
  15.     static CMySingleton Instance;   
  16.     return Instance;  
  17. }   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【VMCloud云平台】拥抱Docker(六)关于DockerFile(1)

之前我们说过通过Docker pull来下载Images创建容器&#xff0c;这一次我们来聊下如何通过DockerFile创建Images再创建容器&#xff0c;Dockerfile也是Docker中的重点&#xff0c;使用DockerFile能够更加便捷轻量的存储标准化环境&#xff0c;也是环境管理的重要手段&#xff0c…

C++中相对路径与绝对路径以及斜杠与反斜杠的区别 及 处理代码

转载自&#xff1a;http://blog.csdn.net/sszgg2006/article/details/8447176文件路径正斜杠和反斜杠正斜杠&#xff0c;又称左斜杠&#xff0c;符号是"/"&#xff1b;反斜杠&#xff0c;也称右斜杠&#xff0c;符号是"\"。文件路径的表示可以分为绝对路径…

01参考资料

《Visual C#经典游戏编程开发》 目标&#xff1a; 掌握小游戏开发的思路&#xff0c;在实际开发过程中&#xff0c;思考实践面向对象&#xff0c;为cocos2d-x开发打下基础转载于:https://www.cnblogs.com/sharpfeng/p/5181260.html

WPF的二维绘图(一)——DrawingContext

DrawingContext比较类似WinForm中的Graphics 类&#xff0c;是基础的绘图对象&#xff0c;用于绘制各种图形&#xff0c;它主要API有如下几种&#xff1a; 绘图API 绘图API一般形为DrawingXXX系列&#xff0c;常用的基础的绘图API有&#xff1a; DrawEllipseDrawGeometryDrawG…

Windows系统编程之进程间通信

Windows系统编程之进程间通信作者&#xff1a;北极星2003来源&#xff1a;看雪论坛&#xff08;www.pediy.com&#xff09;Windows 的IPC&#xff08;进程间通信&#xff09;机制主要是异步管道和命名管道。&#xff08;至于其他的IPC方式&#xff0c;例如内存映射、邮槽等这里…

前端知识体系收藏

1. 布局框架&#xff1a; Bootstrap: http://getbootstrap.com/ Foundation: http://foundation.zurb.com/ Uikit: http://www.getuikit.com/ Web Components:http://css-tricks.com/modular-future-web-components// 2. 构建工具及包管理器&#xff1a; Grunt: http://gruntjs…

20分钟快速了解Redis

Redis可以说是目前最火爆的NoSQL数据库&#xff01; 过去几年&#xff0c;Memcached很盛行&#xff0c;现在有很多公司已将Memcached替换成了Redis。当然&#xff0c;很多人替换并不清楚为什么&#xff0c;只是感觉不想让主流抛弃&#xff0c;这也充分反映了目前Redis的强势。 …

进程通信例子

from&#xff1a;https://msdn.microsoft.com/zh-cn/library/system.diagnostics.process.beginoutputreadline(vvs.80).aspx?cs-save-lang1&cs-langcsharp#code-snippet-4备注可同步或异步读取 StandardOutput 流。Read、ReadLine 和 ReadToEnd 等方法对进程的输出流执行…

IDEA15 下运行Scala遇到问题以及解决办法

为了让Scala运行起来还是很麻烦&#xff0c;为了大家方便&#xff0c;还是记录下来&#xff1a; 1、首先我下载的是IDEA的社区版本&#xff0c;版本号为15. 2、下载安装scala插件&#xff1a; 2.1 进入设置菜单。 2.2 点击安装JetBrains plugin 2.3 输入scala查询插件&#xff…

通过扫码自定义链接安装iOS app,版本更新总结。

1、打包ipa&#xff0c;plist工具&#xff1a;xcode6证书&#xff1a;企业级开发证书 1.1&#xff09;xcode6开始企业级打包时不在生成plist&#xff0c;需要自己编写&#xff1a;模版见下&#xff1a; <?xml version"1.0" encoding"UTF-8"?><!…

安排与愿想

机器学习的理论学了好久了&#xff0c;对于回归&#xff0c;分类&#xff0c;聚类&#xff0c;数据的预处理的各种算法也有了深刻认识&#xff1b; 但纸上谈兵&#xff0c;没有意义&#xff1b; 从今天开始用起来吧&#xff0c;把各种算法的实践以及思路记录下来&#xff0c;附…

pdf破解版

网页说明http://aikongke.com/3754.html1118-1805-7284-0295-8107-3663您好 亲 看到您拍的宝贝了&#xff01; 链接&#xff1a;http://pan.baidu.com/s/1c0iE44C 密码&#xff1a;bnyc XI版转载于:https://www.cnblogs.com/computerMatainance/p/5192834.html

使用try-with-resources替代try finally释放资源

2019独角兽企业重金招聘Python工程师标准>>> 1、旧社会 Java里&#xff0c;对于文件操作IO流、数据库连接等开销非常昂贵的资源&#xff0c;用完之后必须及时通过close方法将其关闭&#xff0c;否则资源会一直处于打开状态&#xff0c;直至程序停止&#xff0c;增加…

MFC模态对话框和非模态对话框

MFC中对话框有两种形式&#xff0c;一个是模态对话框&#xff08;model dialog box&#xff09;&#xff0c;一个是非模态对话框&#xff08;modeless dialog box&#xff09;。一、模态对话框&#xff08;model dialog box&#xff09;在程序运行的过程中&#xff0c;若出现了…

平板电脑离寿终正寝还有多远?

近期有评论称&#xff0c;因为大尺寸智能手机越来越普及&#xff0c;小尺寸平板正遭受着越来越严重的冲击&#xff0c;在这样的背景下&#xff0c;平板厂商也纷纷转攻超大尺寸平板市场&#xff0c;以此避开大尺寸智能手机的竞争&#xff0c;只是。这样的策略转变是否能扭转平板…

Swift 与 JSON 数据

转载自&#xff1a; http://www.cnblogs.com/theswiftworld/p/4660177.html 我们大家平时在开发 App 的时候&#xff0c;相信接触最多的就是 JSON 数据了。只要你的 App 有读取网络数据的功能&#xff0c;你就免不了要与 JSON 打交道。比如你做一个新闻 App&#xff0c;你要读取…

TeamViewer - 最好用强大的免费跨平台远程桌面控制软件 (支持电脑和手机)

from&#xff1a;很早以前 LYcHEE 就提到过&#xff0c;家中的潮人爷爷奶奶每天摆弄着电脑&#xff0c;看看新闻发发邮件&#xff0c;安享晚年生活。只是意料之中的&#xff0c;电脑上莫名出现各种问题&#xff1f;不翼而飞的图标&#xff1f;照片又忘记怎么导出了&#xff1f;…

【设计模式】7、桥接模式

桥接模式就是对一个类的方法进行抽象化&#xff0c;吧不相关的因素提取出来&#xff0c;发展出第二个类 1 package com.shejimoshi.structural.Bridge;2 3 4 /**5 * 功能&#xff1a;桥接模式使用6 * 意图&#xff1a;将抽象部分与它的实现部分分离&#xff0c;使他们都…

hadoop HA 之 QJM

前言 本文主要通过对hadoop2.2.0集群配置的过程加以梳理&#xff0c;所有的步骤都是通过自己实际测试。文档的结构也是根据自己的实际情况而定&#xff0c;同时也会加入自己在实际过程遇到的问题。搭建环境过程不重要&#xff0c;重要点在于搭建过程中遇到的问题&#xff0c;解…

TeamViewer免费版和付费版有什么不同

提到远程控制软件 TeamViewer无疑是目前业内知名度比较高的一款&#xff0c;所以说到远程控制软件可能大部分人首先想到的就是TeamViewer。在使用功能上&#xff0c;它支持远程桌面控制、文件传输、远程计算机锁定、视频会话、主控方和被控方身份互换&#xff0c;远程管理无人执…