【C++】友元--最全解析(友元是什么?我们应该如何理解友元?友元可以应用在那些场景?)

news2024/11/16 22:23:51

目录

一、前言

 二、友元是什么?

 三、友元的感性理解和分类

🥝友元的感性理解 

🍋友元的三种分类

✨友元 --- 全局函数

✨友元 --- 成员函数

✨友元 --- 类

 四、友元函数的应用场景 

🍍操作符重载 :"<<"  与  ">>"

五、友元的注意事项

🍋 友元函数的注意事项

🍇 友元类的注意事项

 六、友元的优缺点

🍓友元 -- 优点

🍉友元 -- 缺点

七、共勉 


一、前言

       C++编程语言中,友元函数(Friend Function)是一种特殊的函数,具有访问类中私有成员和保护成员的权限,尽管它不是类的成员函数。友元函数的存在使得类的设计更加灵活,能够在需要时授予外部函数访问类的私有成员的能力。本文将详细介绍C++中的友元函数,包括其定义、使用场景、优缺点以及示例

 二、友元是什么?

       1️⃣: 友元是在一个类中声明的一个非成员函数,但在类的内部声明该函数为友元。这意味着该函数可以访问该类的私有成员,包括私有变量和私有函数
      2️⃣: 友元的声明通常位于类的声明中,但其实现则位于类外部。 

 三、友元的感性理解和分类

🥝友元的感性理解 

    上述对友元的描述可能比较抽象,大家难以理解,我们可以通过一个生活小案例来感性理解一下

  • 在我们的日常生活中,假设大家都住在别墅社区里面, 在每栋别墅里面都是房间的,像客厅、卧室、厨房、洗手间,每家每户基本都有,我们可以将这些私人的房屋称为你的 ---- 私人区域

  • 那在一个小区中,除了挨家挨户的的私人领域外,一定会存在公共区域,在这些公共区域中,会有一些公共场所,例如像篮球场、咖啡馆、游泳馆、小卖部或是健身器材等等,我们可以将这些所有人都可以访问的地方称为 -----  公共区域

  • 所以 篮球场、健身区域就相当于 ---- 公共区域,大家都可以来玩,你的家就相当于 ----- 私人领域,只有你能进去。
  •  但是千防万防你都很难防住 ---- 隔壁老王 ---- 去你家

      所以在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术隔壁老王

  • 友元的目的:就是让一个函数或者类 访问另一个类中的私有成员
  • 友元的关键字friend 

 🍋友元的三种分类

 ✨友元 --- 全局函数

 首先,我们要定义一个社区类公共成员变量为----公共区域,私有成员变量为---家

// 创建一个社区的类
// 一个社区里面有大家可以一起活动的 公共区域 也有自己的私人空间 家
class Community
{

public:

	// community 的构造函数 ,给成员变量 赋 初始值
	Community()
	{
		PublicAreas = "公共区域";
		home = "家";
	}

	string PublicAreas;  // 公共区域
private:
	string home;  // 家
};

然后定义一个全局函数 laoWang(),用来访问Community类中的私有成员

void laowang(Community& communtiy)
{

	// 访问了私人区域
	cout << "隔壁老王 全局函数 正在偷偷潜入 你家:(引用传递)" << communtiy.home << endl;
	// 访问了公有区域
	cout << "隔壁老王 全局函数 正在公共区域 打球:(引用传递)" << communtiy.PublicAreas << endl;
}

 此时我们来测试一个看是否可以成功

 此时就需要隔壁老王----友元函数出手啦

 关键代码

friend void laowang(Community& communtiy);

在Community类中声明友元函数,告诉编译器 laoWang 全局函数是 Community类 的好朋友,可以访问Community对象的私有成员

// 创建一个社区的类
// 一个社区里面有大家可以一起活动的 公共区域 也有自己的私人空间 家
class Community
{

	friend void laowang(Community& communtiy);

public:

	// community 的构造函数 ,给成员变量 赋 初始值
	Community()
	{
		PublicAreas = "公共区域";
		home = "家";
	}

	string PublicAreas;  // 公共区域
private:
	string home;  // 家
};

下面给出全局函数做友元访问类的私有成员的完整示例代码

// 全局函数做--- 友元

// 创建一个社区的类
// 一个社区里面有大家可以一起活动的 公共区域 也有自己的私人空间 家
class Community
{

	friend void laowang(Community& communtiy);

public:

	// community 的构造函数 ,给成员变量 赋 初始值
	Community()
	{
		PublicAreas = "公共区域";
		home = "家";
	}

	string PublicAreas;  // 公共区域
private:
	string home;  // 家
};

void laowang(Community& communtiy)
{

	// 访问了私人区域
	cout << "隔壁老王 全局函数 正在偷偷潜入 你家:(引用传递)" << communtiy.home << endl;
	// 访问了公有区域
	cout << "隔壁老王 全局函数 正在公共区域 打球:(引用传递)" << communtiy.PublicAreas << endl;
}

int main()
{
	// 创建一个社区对象
	Community community;
	laowang(community);
	return 0;
}

✨友元 --- 成员函数

 我们首先声明一个Community类,防止在下面的Laowang类中,编译器不认识Community

//前置类的声明
class Community;

然后定义Laowang类,采用成员函数在类内声明,类外定义的方式

class Laowang
{
public:
	void visit();
private:

};

void Laowang::visit()
{
	Community _community;
	cout << "隔壁老王类正在访问:" << _community.PublicAreas << endl;
	cout << endl;
	cout << "隔壁老王类正在访问:" << _community.home << endl;
}

下面给出Community类的定义 

class Community
{
public:
	// 构造函数
	Community()
	{
		PublicAreas = "公共区域";
		home = "你家";
	}

	string PublicAreas;

	// 私有成员
private:
	string home;
};

 同样的,现在还没有声明友元,因此类中的成员函数还不能访问另一个类的私有成员

 此时就需要隔壁老王----友元函数出手啦

 关键代码 

friend void Laowang::visit();

在Community类中声明友元函数告诉编译器, Laowang 类中的 visit()函数 是Community类的好友,可以访问 Community类的私有区域(你家)

class Community
{
	// 告诉编译器, Laowang 类中的 visit()函数 是Community类的好友,可以访问 Community类的私有区域(你家)
	friend void Laowang::visit();
public:
	// 构造函数
	Community()
	{
		PublicAreas = "公共区域";
		home = "你家";
	}

	string PublicAreas;

	// 私有成员
private:
	string home;
};

 下面给出成员函数做友元访问类的私有成员的完整示例代码

// 成员函数 做 友元

//前置类的声明
class Community;

class Laowang
{
public:
	void visit();
private:

};


class Community
{
	// 告诉编译器, Laowang 类中的 visit()函数 是Community类的好友,可以访问 Community类的私有区域(你家)
	friend void Laowang::visit();
public:
	// 构造函数
	Community()
	{
		PublicAreas = "公共区域";
		home = "你家";
	}

	string PublicAreas;

	// 私有成员
private:
	string home;
};

void Laowang::visit()
{
	Community _community;
	cout << "隔壁老王类正在访问:" << _community.PublicAreas << endl;
	cout << endl;
	cout << "隔壁老王类正在访问:" << _community.home << endl;
}

int main()
{
	Laowang lw;
	lw.visit();
	return 0;
}

✨友元 --- 类

 我们首先声明一个Laowang类,防止在下面的Community 类中,编译器不认识Laowang类 

// 前置类的声明
class Laowang;

 然后定义 Community 类

class Community
{
public:
	// 创建 构造函数
	Community()
	{
		// 初始化赋值
		PublicAreas = "公共区域";
		home = "你家";
	}

	string PublicAreas;  // 公共区域

private:
	string home;         // 家
};

 然后定义Laowang类,采用成员函数在类内声明,类内定义的方式。(采用成员函数在类内声明,类外定义也可以)

// 定义 一个老王类
class Laowang
{
public:
	// 构造函数
	Laowang()
	{}

	// 参观函数,用于老王进入你家-----访问 Community中的属性
	void visit()
	{
		cout << "隔壁老王类正在访问:" << _community.PublicAreas << endl;
		cout << endl;
		cout << "隔壁老王类正在访问:" << _community.home << endl;
	}

private:

	//自定义类
	Community _community;
};

 同样的,现在还没有声明友元,因此类中的成员函数还不能访问另一个类的私有成员

 此时就需要隔壁老王----友元函数出手啦

 关键代码

friend class Laowang;

 在Community类中声明友元函数告诉编译器, Laowang类是Community类的好朋友,可以访问Community类的私有成员(你家)

class Community
{
	// 告诉编译器,Laowang类是Community类的好朋友,可以访问Community类的私有成员(你家)
	friend class Laowang;

public:
	// 创建 构造函数
	Community()
	{
		// 初始化赋值
		PublicAreas = "公共区域";
		home = "你家";
	}

	string PublicAreas;  // 公共区域

private:
	string home;         // 家
};

下面给出 类 做友元访问类的私有成员的完整示例代码

// 类 做 友元

// 前置类的声明
class Laowang;


class Community
{
	// 告诉编译器,Laowang类是Community类的好朋友,可以访问Community类的私有成员(你家)
	friend class Laowang;

public:
	// 创建 构造函数
	Community()
	{
		// 初始化赋值
		PublicAreas = "公共区域";
		home = "你家";
	}

	string PublicAreas;  // 公共区域

private:
	string home;         // 家
};


// 定义 一个老王类
class Laowang
{
public:
	// 构造函数
	Laowang()
	{}

	// 参观函数,用于老王进入你家-----访问 Community中的属性
	void visit()
	{
		cout << "隔壁老王类正在访问:" << _community.PublicAreas << endl;
		cout << endl;
		cout << "隔壁老王类正在访问:" << _community.home << endl;
	}

private:

	//自定义类
	Community _community;
};


int main()
{
	// 创建一个 老王 的对象
	Laowang lw;
	lw.visit();
	return 0;
}

 四、友元函数的应用场景 

🍍操作符重载 :"<<"  与  ">>"

当需要重载类的操作符(如<<、>>、+、-等)时,友元函数可以访问私有成员,实现合适的操作。 

接下来,就写个自定义类型的<< 和 >>的重载来演示友元函数:

  • >> 流提取
  • << 流插入

C++里cout和cin是全局的对象包含在<iostream>的,cinistream类型对象coutostream类型 对象 

C++中,内置类型是直接支持cout流插入<<和cin流提取>>的,并且其可以自动识别类型。其原因是库里面已经把这些内置类型的给重载了: 

 而自定义类型就不能直接用>>或<<,因此,我们需要手写这两个的运算符重载。

❓问题:现在我们尝试去重载 operator<< ,然后发现我们没办法将 operator<< 重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this 指针默认是第一个参数也就是左操作数了。但是实际使用中 cout 需要是第一个形参对象,才能正常使用。所以我们要将 operator<< 重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。operator>>同理。

  •  我们拿 -- 日期类 -- 来举例说明
// 输出输入 流  <<  >>  
class Date
{
	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);//流插入 <<
	friend istream& operator>>(istream& in, Date& d);//流提取 >>

public:
	Date(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& out, const Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day << endl;
	return out;
}
//流提取 >>
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1;
	cin >> d1;
	cout << endl;
	cout << "输出今天的日期:"<<d1;
}

五、友元的注意事项

🍋 友元函数的注意事项

1️⃣:友元函数可访问类的私有和保护成员,但不是类的成员函数

2️⃣:友元函数不能用const修饰

  •         因为友元函数只是一个全局函数,不属于类的成员函数,所以它没有隐藏的this指针,而const修饰的就是this指针,只有非静态的成员函数才能用const修饰

3️⃣:友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4️⃣:一个函数可以是多个类的友元函数

  • 比如说一个函数需要访问多个类中的私有成员,那可以在那几个类中设置这个函数为他们的友元函数,这样就都可以访问了

🍇 友元类的注意事项

1️⃣:友元关系是单向的,不具有交换性

  • 比如上述Community类和Laowang类,在Community类中声明Laowang类为其友元类,那么可以在Laowang类中直接访问Community类的私有成员变量,但想在Community类中访问Laowang类中私有的成员变量则不行。

2️⃣:友元关系不能传递

  • 如果B是A的友元,C是B的友元,则不能说明C时A的友元。

 六、友元的优缺点

 🍓友元 -- 优点

1、访问私有成员:
主要作用是允许外部函数或类访问另一个类的私有成员,从而实现对类的细粒度控制。
2、操作符重载:
当需要重载类的操作符(如<<、>>、+、-等)时,友元函数可以访问私有成员,实现合适的操作。
3、提高效率:
在某些情况下,使用友元函数可以提高程序的执行效率,因为它可以直接访问类的私有成员,而不需要通过访问器函数(getter和setter)。

🍉友元 -- 缺点

 1、破坏封装性:
友元函数可以突破类的封装性,使得类的私有成员可以被外部函数直接访问,可能会降低代码的安全性和可维护性。(友元也破环了类的隐藏与封装,所以必须慎用 (牺牲安全,提高效率))
2、难以维护:
当程序变得复杂时,友元函数的使用可能会导致代码变得难以理解和维护。

3、友元不能 继承,交换,传递

七、共勉 

     以下就是我对 友元--最全解析 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 C++ 的理解,请持续关注我哦!!!   

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

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

相关文章

【苍穹外卖】HttpClient-快速理解入门

目录 HttpClient-快速理解&入门1. 需求2. 如何使用3. 具体示例4. 大致优点5. 大致缺点 HttpClient-快速理解&入门 1. 需求 在平常访问服务器里面的资源的时候&#xff0c;我们通常是通过浏览器输入网址&#xff08;或者在浏览器点击某个连接&#xff09;这种方式&…

更换本地yum源的步骤

更换本地yum源的流程与命令&#xff1a;

Idea热部署插件JRebel,修改代码无需重启

1. 介绍 JRebel是一个实用的插件&#xff0c;它可以让你在不重启服务器的情况下&#xff0c;修改Java程序并即时生效。相信大家都有这样的经历&#xff1a;每次修改代码后都需要重新编译、打包、部署&#xff0c;然后重启服务器&#xff0c;这个过程非常耗时且繁琐。但是&…

二叉树的实现(前序、中序、后序)(全面)

上一篇我们学习的二叉树的理论&#xff0c;知道了什么是二叉树之后&#xff0c;我们来实现一棵二叉树&#xff0c;二叉树经常考的是前中后序的遍历&#xff0c;这里我们多实现一些功能。 1.二叉树功能 二叉树的实现充分利用了分治思想 1.前序遍历 2.中序遍历 3.后序遍历 4.树的…

Vision Pro零基础教程专栏:校准、对比与结果分析工具

文章目录 CogCalibCheckerboardTool 校准工具介绍校准的基本方法与过程校准的基本作用标定片标定板定义标定板分类基础版标定板校准版标定板DataMatrix代码标定板特殊功能标定板材质分类 使用步骤注意 CogPatInspectTool 对比工具介绍CogPatInspectTool示例图使用场景使用步骤参…

JumpServer搭建堡垒机实战

文章目录 第一步、下载安装第二步、访问异常处理【1】docker方式拉取失败 JumpServer是运维人员可连接内部服务器上进行操作&#xff0c;支持Linux等操作系统的管理工具。 第一步、下载安装 curl -sSL https://resource.fit2cloud.com/jumpserver/jumpserver/releases/latest/…

Opencv Python图像处理笔记一:图像、窗口基本操作

文章目录 前言一、输入输出1.1 图片读取显示保存1.2 视频读取保存1.3 文件读取保存 二、GUI2.1 窗口2.2 轨迹条2.3 画图2.4 鼠标回调 三、图像入门操作3.1 颜色空间转化3.2 通道分离合并3.3 添加边框3.4 算数操作 四、二值化4.1 普通4.2 自适应4.3 Otsu 参考 前言 随着人工智能…

227基于matlab的作业调度问题

基于matlab的作业调度问题。采用遗传算法&#xff0c;解决作业调度问题。一共三个作业&#xff0c;每个作业有不同的时间长度和紧急程度&#xff0c;超过时间会有惩罚措施。通过遗传算法计算出最好的作业安排&#xff0c;使得惩罚最小&#xff0c;获益最大。最终结果通过GUI用甘…

了解IPS和IDS:这5个差异将改变你的安全观念!

IPS 代表 入侵防御系统&#xff08;Intrusion Prevention System&#xff09;&#xff0c;它是 IDS 的进一步发展&#xff0c;不仅具备检测攻击的能力&#xff0c;还能在检测到攻击后主动采取措施阻止攻击。IPS 通常部署在防火墙和网络设备之间&#xff0c;能够深度感知并检测流…

第八周学习笔记DAY.1-异常

本课目标 了解异常概念 理解Java异常处理机制 会捕捉异常 会抛出异常 了解Java异常体系结构 什么是异常 异常是指在程序的运行过程中所发生的不正常的事件&#xff0c;它会中断正在运行的程序 生活中&#xff0c;根据不同的异常进行相应的处理&#xff0c;而不会就此中断…

支持中文繁体,支持同时配置并启用飞书和Lark认证,JumpServer堡垒机v3.10.8 LTS版本发布

2024年4月22日&#xff0c;JumpServer开源堡垒机正式发布v3.10.8 LTS版本。JumpServer开源项目组将对v3.10 LTS版本提供长期的支持和优化&#xff0c;并定期迭代发布小版本。欢迎广大社区用户升级至v3.10 LTS最新版本&#xff0c;以获得更佳的使用体验。 在v3.10.8 LTS版本中&…

【js】解决读取文件源内容总是得到默认index.html

在项目开发中&#xff0c;资源的获取都可以通过网络&#xff0c;所以获取文件内容&#xff0c;只需要将文件地址作为请求发送即可 读取文件源内容 const path 资源地址&#xff08;必须是绝对路径&#xff09;fetch(path).then((response) > {if (!response.ok) {throw ne…

我与C++的爱恋:日期计算器

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 朋友们大家好啊&#xff0c;在我们学习了默认成员函数后&#xff0c;我们通过上述内容&#xff0c;来实现一个简易的日期计算器。 ​ ​ 头文件的声明 #pragma once #incl…

计算机服务器中了locked勒索病毒怎么办,locked勒索病毒解密工具流程步骤

随着网络技术的不断应用与发展&#xff0c;越来越多的企业离不开网络&#xff0c;网络大大提升了企业的办公效率水平&#xff0c;也为企业的带来快速发展&#xff0c;对于企业来说&#xff0c;网络数据安全成为了大家关心的主要话题。近日&#xff0c;云天数据恢复中心接到多家…

Spring - 2 ( 14000 字 Spring 入门级教程 )

一&#xff1a;Spring Web MVC⼊⻔ Spring Web MVC 是⼀个 Web 框架&#xff0c;简称之为: Spring MVC 要真正的理解什么是 Spring MVC&#xff1f;我们⾸先要搞清楚什么是 MVC&#xff1f; 1.1 MVC 定义 MVC 是 Model View Controller 的缩写&#xff0c;它是软件⼯程中的…

丰田是如何用精益理念改变制造业的?

丰田&#xff0c;这个全球知名的汽车制造商&#xff0c;不仅以其高质量的产品赢得了消费者的信赖&#xff0c;更以其独特的精益理念深刻改变了整个制造业的面貌。那么&#xff0c;丰田究竟是如何用精益理念引领制造业变革的呢&#xff1f;天行健精益管理培训公司解析如下&#…

了解BACnet的对象模型 (三)

文章目录 前言18个对象BACnet 对象的属性设备对象&#xff08;Device&#xff09;的属性输入输出值对象类型及其属性 在代码中的表达Device对象的属性模拟输入对象的属性 小结 前言 在楼宇自控网络中&#xff0c;各种设备之间要进行数据交换&#xff0c;为了能够实现设备的互操…

[C++][算法基础]求组合数(II)

给定 &#x1d45b; 组询问&#xff0c;每组询问给定两个整数 &#x1d44e;&#xff0c;&#x1d44f;&#xff0c;请你输出 的值。 输入格式 第一行包含整数 &#x1d45b;。 接下来 &#x1d45b; 行&#xff0c;每行包含一组 &#x1d44e; 和 &#x1d44f;。 输出格…

【Java框架】SpringMVC(二)——SpringMVC数据交互

目录 前后端数据交互RequestMapping注解基于RequestMapping注解设置接口的请求方式RequestMapping注解的常用属性一个方法配置多个接口method属性params属性headers属性consumes属性produces属性 SpringMVC中的参数传递默认单个简单参数默认多个简单参数默认参数中有基本数据类…

机器学习-11-基于多模态特征融合的图像文本检索

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中图像文本检索技术。此技术把自然语言处理和图像处理进行了融合。 参考 2024年&#xff08;第12届&#xff09;“泰迪杯”数据挖掘挑战赛 图像特征提取&#xff08;VGG和Resnet特征提取卷积过程详解&…