【C++程序员的自我修炼】友元

news2025/1/16 16:18:05

心之所向

素履以往


目录

友元函数

cout 如何输出自定义类型

cin 如何输入自定义类型

总结

友元类

日期类Date 如何访问 时间类Time

内部类

概念:

总结:


契子✨ 

我们之前已经把类与对象的基础知识已经学完了,这些是只针对一个类的操作

那么两个类甚至多个类之间能不能有联系呢?

比如 日期类Date 想要访问 时间类Time ,配合的输出当下的具体时间

答案当然是可行的,这就要借助我们的友元来解决问题

友元的简介(百度百科):

友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以 关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。


友元函数

先举个小栗子~

cout 如何输出自定义类型

因为我们最近写的都是自定义类型 -- class,所以想用 cout 输出自定义类型的数据就用不了了

如果我们仍想用 cout 输出数据则需要符号重载 <<

#include<iostream>
#include<cstdlib>
using namespace std;
class Date
{
public:
	Date()
		: _year(2024)
		, _month(4)
		, _day(19)
	{}
	ostream & operator<<(ostream& _cout)
	{
		cout << this->_year << " 年 " << this->_month << " 月 " << this->_day << " 日 ";
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1<< cout<<endl;
	system("pause");
	return 0;
}

d1<< cout 等价于 d1.operator<<(&d1, cout)

现在尝试去重载 operator<< ,然后发现没办法将 operator<< 重载成成员函数。因为 cout 的输出流对象和隐含的 this 指针在抢占第一个参数的位置。this 指针默认是第一个参数也就是左操作数了。


总觉得这种写法很别扭,有没有办法变成 cout<< d1 呢?

实际使用中 cout 需要是第一个形参对象才能正常使用,也就是 cout.operator<<(cout, &d1)

所以要将 operator<< 重载成全局函数即可

但又会导致类外没办法访问成员,此时就需要友元函数来解决


friend 加以修饰的函数便是友元函数,也就是说在这个函数中可以访问到类的成员

~就好比与我是你的好兄弟,我可以用你的东西

#include<iostream>
#include<cstdlib>
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
public:
	Date()
		:_year(2024)
		, _month(4)
		, _day(19)
	{}

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

ostream& operator<<(ostream& _cout, const Date& d)
{
	cout << d._year << " 年 " << d._month << " 月 " << d._day << " 日 ";
	return _cout;
}

int main()
{
	Date d1;
	cout << d1 << endl;
	system("pause");
	return 0;
}

友元函数 可以 直接访问 类的 私有 成员,它是 定义在类外部 普通函数 ,不属于任何类,但需要在类的内部声明,声明时需要加 friend  关键字

 既然输出都写了,那我们在写一个输入吧

cin 如何输入自定义类型

#include<iostream>
#include<cstdlib>
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 2024,int month = 4,int day = 19)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

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

ostream& operator<<(ostream& _cout, const Date& d)
{
	cout << d._year << " 年 " << d._month << " 月 " << d._day << " 日 ";
	return _cout;
}

istream& operator>>(istream& _cin, Date& d)
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}

int main()
{
	Date d;
	cin >> d;
	cout << d << endl;
	system("pause");
	return 0;
}

那么有老铁会问友元函数能够复用吗,比如刚刚写的自定义类型的 cout cin
当然可以 -- 一个函数可以是多个类的友元函数

总结

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同

友元类

先举个小栗子~

日期类Date 如何访问 时间类Time

#include<iostream>
#include<cstdlib>
using namespace std;

class Time
{
	friend class Date;
public:
	Time()
		:_hour(6)
		,_minute(3)
		,_second(0)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int hour = 6, int minute = 30, int second = 6)
		:_year(2024)
		,_month(4)
		,_day(19)
	{
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
	void Print() 
	{
		cout << this->_year << " 年 " << this->_month << " 月 " << this->_day << " 日 " << endl;
		cout << this->_t._hour << " 时 " << this->_t._minute << " 分 " << this->_t._second << " 秒 " << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d1(6, 30, 48);
	d1.Print();
	system("pause");
	return 0;
}

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员

以上我在 Time 类中声明了 Date 类的友元

说明 Time 已经把 Date 当成了友人,Date 想用 Time 的东西(成员)顺便用即可~

这浓浓的兄弟情便是友元的一大特点

这样老铁可能会有一个疑问?

那么 Time 可以访问 Date 的成员吗

以下代码是否正确呢?

class Time
{
	friend class Date;
public:
	Time(int year = 2024,int month =4,int day = 19)
		:_hour(6)
		,_minute(3)
		,_second(0)
	{
		_d._year = year;
		_d._month = month;
		_d._day = day;
	}

private:
	int _hour;
	int _minute;
	int _second;
	Date _d;
};

答案当然是 -- 否

你把我当兄弟,我用你的东西;但是我不把你当兄弟,我的东西你别想用

小总结:
友元关系是单向的,不具有交换性

比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接

访问  Time  类的私有成员变量,但想在  Time  类中访问  Date  类中私有的成员变量则不行

那么友元可以传递吗?

比如 A 和 B 是朋友,B 和 C 是朋友,那么 A 和 C 会成为朋友吗

这里举个小栗子~ 以下代码是否正确呢?

#include<iostream>
#include<cstdlib>
using namespace std;

class A
{
	friend B;
public:
	A()
		:_a(0)
	{
		_cc._c = 0;
	}
private:
	int _a;
	C _cc;
};

class B
{
	friend C;
private:
	int _b;
};

class C
{
private:
	int _c;
};

答案是 A 和 C 还不是朋友~
class C
{
	friend A;
private:
	int _c;
};
只要在 C 中声明 A 是 C 的友元才可以访问

小总结

友元关系不能传递

内部类

概念:

如果一个类定义在另一个类的内部,这个内部类就叫做内部类 。内部类是一个独立的类, 它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意: 内部类就是外部类的友元类 ,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

简单来讲:外部类就是内部类(女神)的舔狗,女神想对舔狗做什么,舔狗都得接受;而舔狗想要的连一点温馨的问候都没有,舔狗是没有尊严的,外部类也是如此~

举个栗子~
#include<iostream>
#include<cstdlib>
using namespace std;

class A
{
public:
	class B
	{
	public:
		void  Print(const A&a)
		{
			cout << a._a << endl;
		}
	private:
		int _b;
	};
private:
	int _a = 10;
};

int main()
{
	A::B b;
	b.Print(A());
	system("pause");
	return 0;
}

其实 B (内部类)是个独立的类,只是放在 A (外部类)里面仅仅受到类域的限制

比如在声明对象的时候还要先写外部域 (A::B)

其次 B 拥有 A 的所有特权,但是 A 没有 B 的一点特权

(B天生就是A的友元,内部类可以访问外部类的私有)

外部类A 却访问不到 内部类B


那么 A 不想当舔狗怎么办呢?

我们可以把内部类设置为私有,这样就不能被外界访问了

class A
{
private:
	class B
	{
	public:
		void  Print(const A&a)
		{
			cout << a._a << endl;
		}
	private:
		int _b;
	};
private:
	int _a = 10;
};


可能有老铁会问,两个类嵌套的大小是多少呢?

#include<iostream>
#include<cstdlib>
using namespace std;

class A
{
private:
	static int k;
	int h;
public:
	class B
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;
			cout << a.h << endl;
		}
	private:
		int _b;
	};
};
int A::k = 1;
int main()
{
	cout << sizeof(A) << endl;
	system("pause");
	return 0;
}

我们来计算一下~

 我们发现 sizeof 计算的是外部类,和内部类没有任何关系

		void foo(const A& a)
		{
			cout << k << endl;
		}

注意:内部类可以直接访问外部类中的 static 成员,不需要外部类的对象

总结:

内部类可以定义在外部类的publicprotectedprivate都是可以的
注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
sizeof计算的是外部类,和内部类没有任何关系

先介绍到这里啦~

有不对的地方请指出💞

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

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

相关文章

endnote21从安装到使用!文献引用!Mac版

视频学习和资源获取 新建库 选择上方导航栏处的File下的New 软件 软件界面可以分成四个部分 2是个人图书馆 3是对某一分类中文献的展示 最右侧是对具体一篇文献的摘要、编辑以及PDF 有回形针标志意味着这篇有全文&#xff0c;也就是有pdf 如果没有回形针代表它只有引文信…

【电力工程】电力大数据和云架构智能AI服务平台研发建设项目可行性研究报告范例

1、项目概况 本项目拟进行基于电力大数据和云架构的智能 AI 服务平台的研究,具体包括电力多元大数据中心、技术中台、数据中台和智能 AI 中台,基于电力大数据云平台基础构建 BI 可视化开发平台和智能 AI 服务平台。 该项目的实施旨在引领公司在大数据领域发展的新趋势,从功…

【第三版 系统集成项目管理工程师】 十五至尊图

持续更新。。。。。。。。。。。。。。。 【第三版】十五至尊图 十五至尊图【必会】1.整合&#xff08;7&#xff09;2.范围 &#xff08;6&#xff09;3.进度 &#xff08;6&#xff09;4.成本 &#xff08;4&#xff09;5.质量&#xff08;3&#xff09;6.资源&#xff08;6&…

el-table使用show-summary合计,但只需要合并某一列

el-table使用show-summary合计&#xff0c;但只需要合并某一列 这里有两种方法&#xff0c;一种是网上的&#xff0c;我会引用他的链接给你们看。 一种是我自己看源码发现的 方法一 这个就是方法一的链接 点击我跳转方法一 方法二 不需要计算的列 去掉prop 然后用插槽显示即可…

利用动态规划在有向图上实现高效语音识别算法

在现代语音识别系统中&#xff0c;动态规划是一种非常关键的技术。它能够帮助我们将复杂的语音信号转换为可理解的文字信息。在本文中&#xff0c;我们将探讨如何使用动态规划方法在有向图上实现语音识别。我们将首先介绍问题的背景和基本概念&#xff0c;然后提供一个高效的算…

【路径规划】局部路径规划算法——DWA算法(动态窗口法)|(含python实现 | c++实现)

文章目录 参考资料1. DWA算法原理1.1 简介1.2 算法原理1. 速度采样2. 轨迹预测&#xff08;轨迹推算&#xff09;3. 轨迹评价 2. Python实现2.1 参数配置2.2 机器人运动学模型2.3 DWA算法类实现2.4 画图2.5 主函数 3. c实现4. 总结 参考资料 The Dynamic Window Approach to C…

如何在Windows 10中启用和使用上帝模式,这里有详细步骤

序言 上帝模式&#xff08;God Mode&#xff09;是一个特殊的文件夹&#xff0c;只在一个窗口中显示所有可用的操作设置。它可以节省搜索命令的时间&#xff0c;而无需知道通过“开始”菜单或“控制面板”查找命令的步骤。上帝模式默认情况下是隐藏的&#xff0c;所以我们需要…

ROS 2边学边练(29)-- 使用替换机制

前言 启动文件用于启动节点、服务和执行流程。这组操作可能有影响其行为的参数。替换机制可以在参数中使用&#xff0c;以便在描述可重复使用的启动文件时提供更大的灵活性。替换是仅在执行启动描述期间评估的变量&#xff0c;可用于获取特定信息&#xff0c;如启动配置、环境变…

链表带环问题——leetcode环形链表1 2

证明链表带环 链表的带环问题指的是本该指向NULL的最后一个节点指向了之前的节点&#xff0c;导致链表成环&#xff0c;找不到尾结点的情况&#xff0c;那么我们该如何证明链表带环呢&#xff1f; 我们可以类比物理中的追及问题&#xff0c;让快慢指针同时走&#xff0c;两者相…

在wsl下安装QT

文章目录 一、前言二、安装QT1、安装依赖 2、安装qt1、先下载到window中&#xff0c;复制到wsl上2、执行命令 三、命令行打开QT1、打开~/.bashrc,在里面添加命令2、测试 四、mysql驱动 一、前言 本方案可以在wsl下正常安装QT&#xff0c;但是QT菜单栏的字体大小调整不了&#…

【开源】使用Python+Flask+Mysql快速开发一个用户增删改查系统

项目演示 项目本身很简单&#xff0c;增删改查是几乎所有系统的骨架。正所谓万丈高楼平地起&#xff0c;学会了增删改查&#xff0c;航母就指日可待了&#xff1a;&#xff09;&#xff0c;光速入门&#xff0c;直接看演示图&#xff1a; 项目地址 https://github.com/mudf…

[C++][算法基础]欧拉函数(常规求质数)

给定 n 个正整数 &#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 1∼N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为 ϕ(N)。 若在算数基本定理中&#xff0c;N…&#xff0c;则&#xff1a; ϕ(N) N… 输入格式 第一行包含整数 n。 接下来 n 行&#xf…

雨云:让你的服务器体验不再“阴霾”

引言 在当今数字化的时代&#xff0c;服务器已经成为了我们生活中不可或缺的一部分。无论是个人网站、企业应用还是游戏服务器&#xff0c;都需要一个稳定可靠的平台来运行。然而&#xff0c;在选择服务器提供商时&#xff0c;很多人常常陷入选择困难&#xff0c;不知道哪家更适…

IO进程(线程Thread)

线程Thread 1.什么是线程 1.1 概念 线程是一个轻量级的进程&#xff0c;为了提高系统的性能引入线程。 线程和进程都参与统一的调度。 在同一个进程中可以创建的多个线程, 共享进程资源。 &#xff08;Linux里同样用task_struct来描述一个线程&#xff09; 1.2 进程和线程的区别…

精益思维驱动人工智能革新:理论到实践的跃迁之旅

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已成为引领未来的关键力量。在这个变革的时代&#xff0c;如何将精益思维与人工智能相结合&#xff0c;推动AI从理论走向实践&#xff0c;成为行业内外关注的焦点。本文&#xff0c;天行健精益生产顾问将分享…

拷贝构造函数与运算符重载

目录 一、拷贝构造函数 1.概念 2.特性 二、运算符重载 1.运算符重载 2.运算符重载实现的形式 3.赋值运算符重载 一、拷贝构造函数 1.概念 拷贝构造函数是一种特殊的构造函数&#xff0c;它在创建对象时&#xff0c;使用同一类中之前创建的对象来初始化新创建的对象…

Vitis HLS 学习笔记--scal 函数-探究

目录 1. Vitis HLS重器-Vitis_Libraries 2. 初识scal() 3. 函数具体实现 3.1 变量命名规则 3.2 t_ParEntries解释 3.3 流类型详解 3.4 双重循环 4. 总结 1. Vitis HLS重器-Vitis_Libraries 在深入探索Vitis HLS&#xff08;High-Level Synthesis&#xff09;的旅程中&…

【单调栈】力扣85.最大矩形

好久没更新了 ~ 我又回来啦&#xff01; 两个好消息&#xff1a; 我考上研了&#xff0c;收到拟录取通知啦&#xff01;开放 留言功能 了&#xff0c;小伙伴对于内容有什么疑问可以在文章底部评论&#xff0c;看到之后会及时回复大家的&#xff01; 前面更新过的算法&#x…

kafka的概念以及Zookeeper集群 + Kafka集群 +elfk集群

目录 zookeeper同步过程 分布式通知和协调 zookeeper同步过程 分布式通知和协调 准备 3 台服务器做 Zookeeper 集群 192.168.68.5 192.168.68.6 192.168.68.7 安装前准备 //关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 node1服务器&a…

了解8大Python小陷阱,深入理解Python

学习了解python常见的使用陷阱&#xff0c;避免二次踩坑 Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识&#xff0c;阅读代码几无障碍。 然而&#xff0c;就像其他语言一样&#xff0c;Python确实有一些古怪特殊的地方。本文将介…