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

目录

一、前言

 二、友元是什么?

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

🥝友元的感性理解 

🍋友元的三种分类

✨友元 --- 全局函数

✨友元 --- 成员函数

✨友元 --- 类

 四、友元函数的应用场景 

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

五、友元的注意事项

🍋 友元函数的注意事项

🍇 友元类的注意事项

 六、友元的优缺点

🍓友元 -- 优点

🍉友元 -- 缺点

七、共勉 


一、前言

       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++ 的理解,请持续关注我哦!!!   

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nacos的介绍和使用Docker、MySQL持久化挂载安装

文章目录 Nacos的介绍和使用Docker、MySQL持久化挂载安装一、Nacos的介绍二、使用Docker和MySQL进行持久化安装1、选择想要使用的MySQL服务器&#xff0c;创建一个数据库nacos-config&#xff0c;然后运行下面sql2、在linux下的opt文件夹下创建 /opt/nacos/data文件夹 和 /opt/…

“卫星-无人机-地面”遥感数据快速使用及地物含量计算的实现方法

原文链接&#xff1a;“卫星-无人机-地面”遥感数据快速使用及地物含量计算的实现方法https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601940&idx6&sn515e01666037570939aaf0eee56f46d1&chksmfa820ef3cdf587e5276eac181c890026b6ca4bc36ce0e4f80d89d…

测试的分类(2)

目录 按照执行方式分类 静态测试 动态测试 按照测试方法 灰盒测试 按照测试阶段分类 单元测试 集成测试 系统测试 冒烟测试 回归测试 按照执行方式分类 静态测试 所谓静态测试就是不实际运行被测软件,只是静态地检查程序代码, 界面或文档中可能存在错误的过程. 不以…

优先级队列(概念理解/底层模拟/时间复杂度分析)

目录 1.概念理解 2.优先级队列的底层模拟 2.1堆的概念 2.2优先队列的模拟实现 2.2.1把Heap类定义好 2.2.2初始化堆 2.2.3创建大堆 1.思路 以此二叉树为例&#xff1a; 图文理解&#xff1a; 2.思路转化为代码 2.2.4堆操作之offer&#xff08;进队列&#xff09; 1…

初识java——jdk?环境变量?及关于安装jdk的步骤

文章目录 JDK的安装在安装JDK时遇到的问题&#xff1a; 背景知识一 什么是jdkjdk简介jdk文件详解&#xff1a;1 bin目录&#xff1a;2 lib目录&#xff1a;3 include目录.exe文件是可执行的应用程序&#xff0c;这个我们都清楚&#xff0c;但.dll文件又是做什么的呢&#xff1f…

Advanced RAG 04:重排序(Re-ranking)技术探讨

编者按&#xff1a;重排序&#xff08;Re-ranking&#xff09;技术在检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;系统中扮演着关键角色。通过对检索到的上下文进行筛选和排序&#xff0c;可以提高 RAG 系统的有效性和准确性&#xff0…

查找算法之顺序查找

目录 前言一、查找算法预备知识二、顺序查找三、总结3.1 查找性能3.2 适用场景 前言 查找算法是一种用于在数据集合中查找特定元素的算法。在计算机科学中&#xff0c;查找算法通常被用于在数组、链表、树等数据结构中查找目标元素的位置或者判断目标元素是否存在。 查找算法的…

爱尔兰启动其首个量子技术国家战略“量子 2030”

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨王珩 排版丨沛贤 800字丨5分钟阅读 摘要&#xff1a;爱尔兰推出了“量子 2030”&#xff0c;这是爱尔兰第一个量子技术国家战略。“量子 2030”将爱尔兰量子技术界的努力重点放在爱尔兰可以…

电脑文件加密软件有哪些?文件加密软件哪个好?

某企业的一位员工因不慎将包含敏感客户数据的电脑丢失&#xff0c;导致企业面临巨大的法律风险和经济损失。 这一事件凸显了电脑文件加密的必要性。 如果该企业事先采用了文件加密软件对敏感数据进行保护&#xff0c;即使电脑丢失&#xff0c;攻击者也无法轻易获取到文件内容…

STL_List与萃取

List 参考文章: https://blog.csdn.net/weixin_45389639/article/details/121618243 List源码 List中节点的定义&#xff1a; list是双向列表&#xff0c;所以其中节点需要包含指向前一节点和后一节点的指针&#xff0c; data是节点中存储的数据类型 template <class _Tp&g…

HCIP——MPLS(笔记)

MPLS--多协议标签交换技术 包交换 数据组成数据包&#xff0c;之后&#xff0c;在各个网络节点中不断传递&#xff0c;最终到达目标。包交换转发效率不高的问题所在&#xff1a;1&#xff0c;在整个包交换的过程中&#xff0c;需要先查询路由表之后再查看ARP缓存表两张表来完…

Java:内部类

目录 1.内部类介绍2.实例内部类3.静态内部类4.局部内部类5.匿名内部类 1.内部类介绍 当一个事物的内部&#xff0c;还有一个部分需要一个完整的结构进行描述&#xff0c;而这个内部的完整的结构又只为外部事物提供服务&#xff0c;那么这个内部的完整结构最好使用内部类。在 J…

KDD‘23 | AlphaMix: 高效专家混合框架(MoE)显著提高上证50选股表现

KDD23 | AlphaMix: 高效专家混合框架&#xff08;MoE&#xff09;显著提高上证50选股表现 原创 QuantML QuantML 2024-04-18 09:17 上海 Content 本文提出了一个名为AlphaMix的新型三阶段专家混合&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;框架&#xff0c;…

信息流广告大行其是,微博回望“原生”的初心

摘要&#xff1a;有流量的地方&#xff0c;就当有原生信息流广告 信息流广告&#xff0c;自2006年Facebook推出后就迅速火遍全球数字营销界&#xff0c;被誉为实现了广告主、用户、媒体平台三赢。特别是随着OCPM/OCPX大放异彩&#xff0c;信息流广告几乎成为广告主的必选项&…

达梦数据库的AWR报告

达梦数据库的AWR报告 数据库快照是一个只读的静态的数据库。 DM 快照功能是基于数据库实现的&#xff0c;每个快照是基于数据库的只读镜像。通过检索快照&#xff0c;可以获取源数据库在快照创建时间点的相关数据信息。 为了方便管理自动工作集负载信息库 AWR&#xff08;Auto…

数据结构实验(二)

单链表的基本操作 一、总的设计思路(c++实现) 1、首先定义一个包含name、gender、student_number、hobbies的学生信息结构体。 2、接着一一写出:链表初始化(initialize)函数、后插法插入(insert)函数、打印信息(output)函数、对链表结点进行排序(sortList)函数、…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Check Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Check Box的使用及说明 文章编号&#xff…

华为ensp中MSTP多网段传输协议(原理及配置命令)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月22日15点29分 在华为ENSP中&#xff0c;MSTP&#xff08;多段传输协议&#xff09;是重要的生成树协议&#xff0c;它扩展了STP&#xff08;生成树协议&#xff09…

跨境电商日报:Tk使用时长全美第一;Shopee发布Z世代购物调查报告

# 平台资讯 PART 1 电商 Shopee调查&#xff1a;六成Z世代购物者看重平台功能多样性 日前&#xff0c;据外媒报道&#xff0c;Shopee发布了《在数字时代吸引Z世代购物者》调查报告。数据显示&#xff0c;60%的Z世代购物者优先考虑搜索简单、具有比较功能和有用评论的平台。据…

代码随想录算法训练营第三十六天|435. 无重叠区间,763.划分字母区间,56. 合并区间

题目&#xff1a;435. 无重叠区间 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi]。返回需要移除区间的最小数量&#xff0c;使剩余区间互不重叠。 题目链接/讲解链接&#xff1a; https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0…