C++ friend详解
私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。
C++ 是从结构化的C语言发展而来的,需要照顾结构化设计程序员的习惯,所以在对私有成员可访问范围的问题上不可限制太死。
C++ 设计者认为, 如果有的程序员真的非常怕麻烦,就是想在类的成员函数外部直接访问对象的私有成员,那还是做一点妥协以满足他们的愿望为好,这也算是眼前利益和长远利益的折中。因此,C++ 就有了友元(friend)的概念。打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私。
友元分为两种:友元函数和友元类。
友元函数
在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。
将全局函数声明为友元的写法如下:
friend 返回值类型 函数名(参数表);
将其他类的成员函数声明为友元的写法如下:
friend 返回值类型 其他类的类名::成员函数名(参数表);
但是,不能把其他类的私有成员函数声明为友元。
关于友元,看下面的程序示例。
#include<iostream>
using namespace std;
class CCar; //提前声明CCar类,以便后面的CDriver类使用
class CDriver
{
public:
void ModifyCar(CCar* pCar); //改装汽车
};
class CCar
{
private:
int price;
friend int MostExpensiveCar(CCar cars[], int total); //声明友元
friend void CDriver::ModifyCar(CCar* pCar); //声明友元
};
void CDriver::ModifyCar(CCar* pCar)
{
pCar->price += 1000; //汽车改装后价值增加
}
int MostExpensiveCar(CCar cars[], int total) //求最贵气车的价格
{
int tmpMax = -1;
for (int i = 0; i<total; ++i)
if (cars[i].price > tmpMax)
tmpMax = cars[i].price;
return tmpMax;
}
int main()
{
return 0;
}
这个程序只是为了展示友元的用法,所以 main 函数什么也不做。
第 3 行声明了 CCar 类,CCar 类的定义在后面。之所以要提前声明,是因为 CDriver 类的定义中用到了 CCar 类型(第7行),而此时 CCar 类还没有定义,编译会报错。
不要第 3 行,而把 CCar 类的定义写在 CDriver 类的前面,是解决不了这个问题的,因为 CCar 类中也用到了 CDriver 类型(第14行),把 CCar 类的定义写在前面会导致第 14 行的 CDriver 因没有定义而报错。C++ 为此提供的解决办法是:可以简单地将一个类的名字提前声明,写法如下:
class 类名;
尽管可以提前声明,但是在一个类的定义出现之前,仍然不能有任何会导致该类对象被生成的语句。但使用该类的指针或引用是没有问题的。
第 13 行将全局函数 MostExpensiveCar 声明为 CCar 类的友元,因此在第 24 行可以访问 cars[i] 的私有成员 price。同理,第 14 行将 CDriver 类的 ModifyCar 成员函数声明为友元,因此在第 18 行可以访问 pCar 指针所指向的对象的私有成员变量 price。
友元类
一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:
friend class 类名;
来看如下例程:
class CCar
{
private:
int price;
friend class CDriver; //声明 CDriver 为友元类
};
class CDriver
{
public:
CCar myCar;
void ModifyCar() //改装汽车
{
myCar.price += 1000; //因CDriver是CCar的友元类,故此处可以访问其私有成员
}
};
int main()
{
return 0;
}
第 5 行将 CDriver 声明为 CCar 的友元类。这条语句本来就是在声明 CDriver 是一个类,所以 CCar 类定义前面就不用声明 CDriver 类了。第 5 行使得 CDriver 类的所有成员函数都能访问 CCar 对象的私有成员。如果没有第 5 行,第 13 行对 myCar 私有成员 price 的访问就会导致编译错误。
一般来说,类 A 将类 B 声明为友元类,则类 B 最好从逻辑上和类 A 有比较接近的关系。例如上面的例子,CDriver 代表司机,CCar 代表车,司机拥有车,所以 CDriver 类和 CCar 类从逻辑上来讲关系比较密切,把 CDriver 类声明为 CCar 类的友元比较合理。
友元关系在类之间不能传递,即类 A 是类 B 的友元,类 B 是类 C 的友元,并不能导出类 A 是类 C 的友元。“咱俩是朋友,所以你的朋友就是我的朋友”这句话在 C++ 的友元关系上 不成立。
C++ friend详解的更多相关文章
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
- Git初探--笔记整理和Git命令详解
几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...
- Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)
Android XML shape 标签使用详解 一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...
- Node.js npm 详解
一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...
- .NET应用和AEAI CAS集成详解
1 概述 数通畅联某综合SOA集成项目的统一身份认证工作,需要第三方系统配合进行单点登录的配置改造,在项目中有需要进行单点登录配置的.NET应用系统,本文专门记录.NET应用和AEAI CAS的集成过 ...
随机推荐
- loadRunner12 设置关联 获取服务端动态数据
关联:服务器返回给客户端一些动态变化的值,客户端用这些值去访问服务器,不能把这些值写死在脚本里面,而应该存放在一个变量里面. 在脚本回放过程中,客户端发出请求,通过关联函数所定义的左右边界值(也就是关 ...
- [源码解析] PyTorch 分布式(2) ----- DataParallel(上)
[源码解析] PyTorch 分布式(2) ----- DataParallel(上) 目录 [源码解析] PyTorch 分布式(2) ----- DataParallel(上) 0x00 摘要 0 ...
- mybatis插入数据时处理为null的属性
在做项目的时候,数据库中的所有字段被设置为全都不能为null,但是在我们开发过程中,插入一些记录的时候,实体类中的一些字段如果页面没有传入,则默认就会被设置为null,这样的话,在执行插入语句的时候, ...
- CCCC-exercise
CCCC-exercise 1.L1 总结L1 1-27里面我觉得有东西可以总结的题目 贴了部分的代码 L1-006(20) 一个正整数 N 的因子中可能存在若干连续的数字.例如 630 可以分解为 ...
- (一)初识MySQL
JavaEE:企业级Java开发 Web 前端(页面:展示,数据) 后台(连接点,连接数据库JDBC,链接前端(控制,控制视图跳转和给前端传递数据)) 数据库(存数据,Txt,Excel,word) ...
- Mac下Shell脚本使用学习笔记(二)
参考文献 Shell 教程 MAC常用终端命令行 Mac下Shell脚本使用 (7)Shell echo命令: 命令格式:echo string ①显示普通字符串:echo "It is a ...
- 博主日常工作中使用的shell脚本分享
前言: 今天给大家分享一篇在我工作中常用的一个shell脚本,里面有一些我们常用到的shell操作.该脚本用于本地电脑和服务器交互上,实现以下功能: 自动拉取自己个人电脑上的源码到服务器上yocto包 ...
- 【linux系统】命令学习(五)linux三剑客 grep \ awk \ sed
grep----基于正则表达式查找满足条件的行 1.内容检索 获取行 grep pattern file 获取内容 grep -o pattern file 获取上下文grep -A -B -C pa ...
- 记一次 IIS 站点配置文件备份和还原,物理路径文件批量备份
前言 上一篇文章实现了数据库的批量备份和还原,当然部署在服务器中的IIS站点备份也是一个十分繁琐的事,三四个数量不多的还好,像有一些服务器用了许久,承载几十个站点甚至更多,一个一个备份,再一个一个还原 ...
- OWASP-Top5-(Security Misconfiguration 安全配置错误)
概述 从上一版的第 6 位开始,90% 的应用程序都经过了某种形式的错误配置测试.随着更多转向高度可配置的软件,看到这一类别上升也就不足为奇了.值得注意的CWE包括CWE-16 Configurati ...