深入篇【C++】类与对象:友元函数与友元类

news2024/10/7 13:20:39

深入篇【C++】类与对象:友元函数与友元类

  • ①.提出问题:重载operator<<
  • ②.解决问题:友元
    • Ⅰ.友元函数
      • 【特点】
    • Ⅱ.友元类
      • 【特点】
  • ③.总结问题

①.提出问题:重载operator<<

如果我们尝试去重载运算符operator<<,你将会发现没有办法将operator<<重载成成员函数。
为什么呢?我们需要了解一下流插入cout和流提取cin的一些知识,我们知道流插入cout对于内置类型可以直接打印出来,那么对于自定义类型是否可以打印呢?
答案是可以的,为什么呢?
1.cout<<可以直接支持内置类型,是因为库里实现了对于内置类型的运算符重载。
2.cout<<可以直接支持自定义识别类型,是因为库里也实现了对于自定义类型的运算符重载,而这两个运算符重载函数又构成函数重载。
但流插入cout<<不能写成成员函数。
因为cout的输出流对象和隐藏的this在抢占第一个形参的位置。this指针默认是第一个参数也就是左操作数,但实际上使用cout<<时,第一个参数应该是流插入cout,这样才正常,而成员函数第一个参数必须是this指针,所以operator<<无法写成成员函数,只能写成全局函数。

//尝试重载<<运算符,让它可以输出对象的数据
class  Data
{
public:
	
	Data(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
	}
	//按照成员函数的规定,第一参数必须是this指针,第二参数才可以是cout
	//但这样写不符合cout<<写法 cout应该在左边,cout右边是要输出的数据
	ostream& operator<<(ostream& _cout)
	{
		_cout << _year << "-" << _month << "-" << _day << endl;
		return _cout;
	}
	//但如果要写成ostream& operator<<(ostream&_cout,&d),这样又会出现一个问题,第一参数必须是隐藏的this指针,也就是必须是调用该函数的对象的指针,那调用函数时就变成这样了 d1<<cout,d1.operator<<(&d1,ostream&_cout)
	
	//矛盾的是正常写法是cout<<d1.但在类里面无法实现这样的形式,所以必须到类外面写
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Data& d)// cout<<d1
{
	_cout << d._year << "-" << d._month << "-" << d._day << endl;//问题无法访问类私有成员
	return _cout;
}
int main()
{
	Data d1;
	//d1 << cout;//虽然可以打印出来,但不符合常规调用。
	cout << d1;//正常应该是这样使用
}

但写到类外面又会存在问题:在类外面无法访问类的私有成员。
这个问题该如何解决呢?在这里插入图片描述
这时候就需要友元来解决。

②.解决问题:友元

友元提供了一种突破封装的方式,有时候提供了便利,可以在类外面访问类的私有成员。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类。

Ⅰ.友元函数

友元函数可以直接分为类的私有成员,它是定义在类外的普通函数,不属于任何类,但是要在类里面声明,这样才可以成为友元函数,声明时需要加friend关键字

class  Data
{
public:
//将函数变成友元函数后就可以访问类的私有成员了。
//要注意在类外定义在类里声明,声明时要使用friend关键字
	friend ostream& operator<<(ostream& _cout, const Data& d);
	Data(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{

	}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Data& d)// cout<<d1
{
	_cout << d._year << "-" << d._month << "-" << d._day << endl;//变成友元函数后,就可以通过d对象来访问对象中的私有数据。也就是类的私有成员。
	return _cout;
}
int main()
{
	Data d1;
	cout << d1;
}

注意:要支持插入流可以连续打印,所以要使该重载函数的返回值仍然为cout类型即ostream,这样就可以支持连续打印了。

【特点】

1.友元函数可以直接访问类的私有和保护成员,不是类的成员函数。
2.友元函数不能用const修饰

3.友元函数的声明可以在类的任意地方声明,不受访问限定符的限制。
4.一个函数可以是多个类的友元函数。
5.友元函数的调用和普通函数的调用原理相同。

Ⅱ.友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元类就是在A类中将B类声明为友元,那这个B类的任意成员函数都是A类的友元函数了,都可以访问这个A类的私有成员了。


class Time
{
	friend class Data;//声明日期类是时间类的友元类。
	//则在日期类中,所有的成员函数都可以随意访问时间类的成员变量。
public:
	Time(int year = 2, int month = 2, int day = 2)
		:_year(year)
		, _month(month)
		, _day(day)
	{

	}
	
private:
	int _year;
	int _month;
	int _day;
};

class  Data
{
public:

	Data(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{

	}
	//因为Data类是Time类的友元类,所以Data的所以成员函数都可以访问Time类的成员变量。
	void ChangeTime(int year = 2023, int month = 5, int day = 20)
	{
		_t._year = year;
		_t._month = month;
		_t._day = day;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
int main()
{
	Data d1;
	d1.ChangeTime();
}

在这里插入图片描述

【特点】

1.友元关系是单向的,不具有交换性。
比如上面的Date类是Time类的友元类,Date在Time里被声明为友元后,那么在Data类中就可以直接访问Time类的成员变量,但想在Time类中去访问Data类的成员变量是不可以的。
2.友元关系不能传递。
比如A是B的友元类,B是C的友元类,不可以说A是C的友元类喔。
3.友元关系是不可以继承的。

③.总结问题

对于一些重载函数比如operator<<和operator>>因为成员函数的特性无法写进类里,不得不写成全局函数,而遇到的的统一问题:无法访问类的私有成员。
这时就必须得使用我们的友元函数来解决这样的问题:当不得不访问一个封装类的数据时,可以使用友元来处理。
而使用友元时需要注意友元的使用技巧。
1.在类外定义,类里声明,使用关键字friend。
2.友元无法使用const修饰。
3.友元声明不受访问限定符限制。
4.尽量少使用友元,因为友元会破坏封装,增加耦合度。

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

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

相关文章

【JavaSE】Java基础语法(十八):接口

文章目录 1. 接口的概述2. 接口的特点3. 接口的成员特点4. 类和接口的关系5. 抽象类和接口的关系 1. 接口的概述 接口就是一种公共的规范标准&#xff0c;只要符合规范标准&#xff0c;大家都可以通用。Java中接口存在的两个意义 用来定义规范用来做功能的拓展 2. 接口的特点…

SpringBoot3.0升级遇到关于Invalid bound statement (not found)处理方案

前言 今天升级SpringBoot3时尝试兼容Mybatis和MybatisPlus出现多个异常。顺带写写排查方案&#xff0c;springboot2其实也一样用 排查方向&#xff1a;mapper接口中的方法名和mapper.xml中的id标签不一致 仔细核对抛出异常的接口和xml文件名。方法名排查方向&#xff1a;.map…

基于docker容器化的jenkins2.406升级迁移(jdk8升级jdk11)

查看基础配置 查看jenkins的home路径: 查看磁盘占比: 发现占比比较大的是: 主要子目录&#xff1a; jobs&#xff1a;包含Jenkins管理的构建作业的配置细节、构建产物和数据&#xff1b;logs&#xff1a;Jenkins的日志目录&#xff1b;plugins&#xff1a;包含所有已经安装了…

低代码开发平台助力门店管理创新,提升用户体验

随着信息技术的飞速发展&#xff0c;低代码开发成为了近年来热门的开发方式。同时&#xff0c;在零售业中&#xff0c;门店管理也成为了一个重要的议题。本文将结合低代码开发和门店管理两个主题&#xff0c;探讨如何应用低代码技术优化门店管理。 一、门店管理的挑战 门店管…

Ansible从入门到精通【二】

大家好&#xff0c;我是早九晚十二&#xff0c;目前是做运维相关的工作。写博客是为了积累&#xff0c;希望大家一起进步&#xff01; 我的主页&#xff1a;早九晚十二 专栏名称&#xff1a;Ansible从入门到精通 立志成为ansible大佬 文章目录 ansible常用命令ansibleansible-d…

Revit幕墙:用幕墙巧做屋面瓦及如何快速幕墙?

一、Revit中用幕墙巧做屋面瓦 屋面瓦重复性很高&#xff0c;我们如何快速的创建呢?下面我们来学会快速用幕墙来创建屋面瓦的技巧。 1.新建“公制轮廓-竖挺”族&#xff0c;以此来创建瓦的族(以便于载入项目中使用) 2.在轮廓族中绘制瓦的轮廓(轮廓需要闭合)&#xff0c;将族名称…

淘宝天猫618预售活动时间是从几号什么时候开始2023天猫淘宝618预售定金能退吗?

2023年淘宝天猫618预售什么时候开始&#xff1f; 2023年5月26日20:00淘宝天猫618预售活动开始截至到5月31日18:00结束&#xff1b; 2023年淘宝天猫618预售定金支付后可退吗&#xff1f; 淘宝天猫618预售定金支付后如不想要该预售商品了&#xff0c;可以在5月31日20:00后完成尾…

如何获得高清、4K无水印视频素材?教你轻松拥有高清视频

随着短视频越来越火爆&#xff0c;大家也都加入到了视频创作的行业中&#xff0c;平时也会喜欢剪辑一些视频发布到平台上&#xff0c;那高清的短视频肯定是最受欢迎的&#xff0c;我们自己又如何获得高清的视频呢&#xff1f; 一、为什么需要高清的视频素材&#xff1f; 1. 视…

基于非靶向和靶向代谢组学分析婴幼儿血管瘤的氨基酸代谢

文章标题&#xff1a;Integrated nontargeted and targeted metabolomics analyses amino acids metabolism in infantile hemangioma 发表期刊&#xff1a;Frontiers in Oncology 影响因子&#xff1a;5.738 作者单位&#xff1a;四川大学华西医院 百趣提供服务&#xf…

prometheus 部署安装

prometheus 部署安装 监控系统硬件&#xff08;node-exporter&#xff09;监控mysql &#xff08;mysql_exporter&#xff09;监控redis&#xff08;redis_exporter&#xff09;监控docker &#xff08;cadvisor&#xff09;监控可视化展示 (Grafana)监控报警 &#xff08;Ale…

Adams几何元素

简单学了一下Adams的几何元素&#xff0c;记录一下面板。 1.几何点 Point Add to Ground 添加到大地 Add to Part 添加到现有构件 Attach Near 关联构件 Don’t Attach 不关联构件 创建添加行&#xff0c;输入点坐标。创建好的点可以在导航栏右键删除和修改。 2. 坐标系Mar…

ROS学习——利用电脑相机标定

一、 安装usb-cam包和标定数据包 sudo apt-get install ros-kinetic-usb-cam sudo apt-get install ros-kinetic-camera-calibration 要把kinetic改成你自己的ros版本 。 二、启动相机 roslaunch usb_cam usb_cam-test.launch 就会出现一个界面 可以通过下面命令查看相机…

leetcode 1140. Stone Game II(石头游戏II)

涉及game的问题&#xff0c;2个player, 现有几堆石头&#xff0c;每堆石头个数为piles[i], 刚开始M1, player1先拿石头&#xff0c;可以拿走前 x 堆&#xff08;假设从第 i 堆开始拿&#xff0c;可以拿 i ~ ix-1 堆&#xff09;&#xff0c;1 < x < 2M, 拿完之后&#xf…

2023.5.19Hadoop具体操作(四种)

大作业 1、ens33没有地址 查看虚拟机的NAT8网段 使用ip a显示ens33的ip ip a设置静态ip 编辑网络接口配置文件&#xff1a;输入以下命令来编辑网络接口的配置文件&#xff1a; sudo vi /etc/network/interfaces在打开的文件中&#xff0c;找到要设置为静态IP的网络接口&am…

来领走你的AI老师

现在很多大学生不上课&#xff0c;在b站学习。 有没有想过有一天&#xff0c;你的大多数时间都在跟AI学习&#xff1f; 未来已来&#xff0c;这里有一份万能提示词&#xff0c;让你立马拥有一位AI导师。 这位导师可了不得&#xff0c;除了啥都知道之外&#xff0c;还能&…

C# 队列(Queue)

目录 一、概述 二、基本的用法 1.添加元素 2.取出元素 1&#xff09;Dequeue 方法 2&#xff09;Peek 方法 3.判断元素是否存在 4.获取队列的长度 5.遍历队列 6.清空容器 7.Queue 泛型类 三、结束 一、概述 表示对象的先进先出集合。 队列和其他的数据结构一样&a…

Ros2中MoveItConfigsBuilder的功能作用说明

文章目录 前言MoveItConfigsBuilder的功能作用机器人resource文件样例总结 前言 在学习moveit2的样例时发现加载机器人配置参数多使用MoveItConfigsBuilder&#xff0c;它具体的功能和使用方法是什么呢。 这篇博文用来记录说明该函数的使用方法、作用和调用逻辑。 MoveItConfi…

Tomcat的讲解与安装

文章目录 前言一.Tomcat是什么二.Tomcat的原理三.Tomcat的安装和说明**1.下载****2.解压安装**bin目录conf目录lib目录log目录temp目录webapps目录work目录 3.配置环境变量 四.验证安装 前言 Tomcat 是一个 HTTP 服务器. 前面我们已经学习了 HTTP 协议, 知道了 HTTP 协议就是 …

共享电单车RFID停车技术分析

近段时间&#xff0c;某地主城区运营商信号基站受严重干扰&#xff0c;造成300多个基站&#xff0c;超过5万的用户受到影响。据无线电监测站的调查确认干扰源来自共享电单车&#xff0c;是共享电单车加装的RFID停车标签惹的祸&#xff0c;而该地区RFID终端选用的是超高频&#…

考研C语言复习第二章

考研C语言第二节课 2.1 #include <stdio.h> //符号常量 #define PI 32 int main() {int iPI*2;printf("i%d\n",i); // printf("Hello, World!\n");return 0; }在这里的define相当于一个替换的作用&#xff0c;是直接将PI是32整个搬到下面 所以…