【C++初阶】友元(友元函数友元类)、内部类、匿名对象、拷贝对象时的优化

news2024/11/18 23:44:40

🌟hello,各位读者大大们你们好呀🌟

🍭🍭系列专栏:【C++学习与应用】

✒️✒️本篇内容:友元函数和友元类的概念和基础应用,简单介绍内部类、匿名对象、拷贝对象时的部分编译器优化情况

🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-

📡📡同期文章:【C++初阶】初始化列表、static成员(静态成员变量 & 静态成员函数)

目录

一、友元

1.友元函数

(1)友元函数的引出

(2)友元函数的说明 

2.友元类

二、内部类

三、匿名对象

四、拷贝对象时的部分编译器优化情况


一、友元

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

1.友元函数

(1)友元函数的引出

问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。

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

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
ostream& operator<<(ostream& _cout)
{
	_cout << _year << "-" << _month << "-" << _day << endl;
	return _cout;
}
private:
	int _year;
	int _month;
	int _day;
};

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

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _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;
	return 0;
}

(2)友元函数的说明 

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制(可访问private)
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

2.友元类

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

  • 友元关系是单向的,不具有交换性。

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

  • 友元关系不能传递如果C是B的友元

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

  • 友元关系不能继承,在继承位置再给大家详细介绍。

理解上有问题,没关系,直接上代码

class Time
{
	friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
	中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}

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


二、内部类

概念如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

注意内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访
问外部类中的所有成员。但是外部类不是内部类的友元。

特性

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系
class A
{
public:
	class B // B天生就是A的友元
	{
	public:
		void function(const A& a)
		{
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	};
private:
	static int k;
	int h;
};
int A::k = 1;

int main()
{
	A::B b;//使用上要注意
	b.function(A());

	return 0;
}


三、匿名对象

下面我们一起来看一段长代码来理解匿名对象的含义和作用,我们可以直接从 main() 看起

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	~A()//析构函数
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
class Solution {
public:
	int Sum_Solution(int n) {
		//...
		return n;
	}
};
int main()
{
	A aa1;
	// 不能像下面这样定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	//A aa1();
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,下一行他就会自动调用析构函数
	A();
	A aa2(2);//在()中加上数值,就可以让编译器识别出它是对象定义

	// 匿名对象在这样场景下就很好用 - 两行代码优化成一行直接构造
	//Solution aa3;
	//aa3.Sum_Solution(10);
	Solution().Sum_Solution(10);
	return 0;
}


四、拷贝对象时的部分编译器优化情况

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

class A
{
public:
	A(int a = 0)//构造函数
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)//拷贝构造
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)//运算符重载
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
	~A()//析构函数
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
void f1(A aa)
{}
A f2()
{
	A aa;
	return aa;
}
int main()
{
	// 传值传参
	A aa1;
	f1(aa1);
	cout << endl;
	// 传值返回
	f2();
	cout << endl;

	// 隐式类型,连续构造+拷贝构造->优化为直接构造
	f1(1);

	// 一个表达式中,连续构造+拷贝构造->优化为一个构造
	f1(A(2));
	cout << endl;

	// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
	A aa2 = f2();
	cout << endl;

	// 一个表达式中,连续拷贝构造+赋值重载->无法优化
	aa1 = f2();
	cout << endl;

	return 0;
}


🌹🌹友元(友元函数&友元类)、内部类、匿名对象、拷贝对象时的优化的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪 

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

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

相关文章

Java守护线程简述

Java守护线程简述前言前置知识线程JVM退出代码测试查看子线程是否继承父线程的类型守护线程在程序退出时的表现普通线程在程序退出时的表现总结前言 最近再看《Java并发编程实战》&#xff0c;正好有一小节关于守护线程的知识&#xff0c;这里做一点小总结。 前置知识 这里只…

云原生之Dockerfile简介和基础实践

dockerfile简介和基础实践一、Dockerfile简介1.1、Dockerfile解决的问题1.2、docker build 构建流程1.3、关键字介绍二、Dockerfile 实践2.1、基本语法实践 --- golang问题检查2.2、基本语法实践 --- gcc总结后言一、Dockerfile简介 Dockerfile是一个创建镜像所有命令的文本文…

为行业赋能 助力行业客户业务大放异彩

近日&#xff0c;2022亚马逊云科技re:Invent全球大会已完美落幕&#xff0c;在大会上发布了很多重磅新品&#xff0c;包括云原生数据战略、硬件创新、高性能计算等等在各行各业中的创新应用&#xff0c;下面就来看看医疗与生命科学、市场调研和数据分析、汽车行业&#xff0c;他…

如何理解UML2.5.1(04篇)

第一步&#xff1a; 这里发现UML2.5.1中的一处错误&#xff1a; 图四、Figure9.10中的一处错误。 错误就在于最下面一个关联右端点处的标记redefines&#xff0c;有了这个标记&#xff0c;就应该意味着此关联特化了某个关联&#xff0c;但是如果我们用“A_ownedAttribute_class…

在Android端集成OpenCV的三种方式

1.Opencv Android SDK 基于Opencv C本地代码&#xff0c;通过Java语言接口使用JNI技术调用C本地方法的SDK开发包。 &#xff08;1&#xff09;etc:各类模型文件存储地址 &#xff08;2&#xff09;java:Java版本的Android SDK相关文件 &#xff08;3&#xff09;native:JNI层…

【QGIS入门实战精品教程】3.4:QGIS创建GeoPackage地理数据库及数据入库案例详解

GeoPackage(以下简称gpkg),内部使用SQLite实现的一种单文件、与操作系统无关的地理数据库。在QGIS中可以很方便的实现GeoPackage的创建与连接等操作。 一、QGIS创建GeoPackage 1. 创建数据库 QGIS创建GeoPackage的方法与ArcGIS中创建File GDB的类似,选择一个目标文件夹,…

Adobe Acrobat XI 一进去就闪退;解决Acrobat的闪退问题

一、原因分析 闪退是因为网络中校验版权时出现问题 二、解决办法 域名欺骗&#xff0c;添加伪造的host条目 1. 进入C:\Windows\System32\drivers\etc 找到hosts文件 2. 右机hosts&#xff0c;选择属性&#xff0c;在安全选项里&#xff0c;点击高级&#xff1a; 3. 在ho…

Hive+Spark离线数仓工业项目实战--数仓设计及数据采集(1)

数仓设计及数据采集 1. **数据仓库设计** - 建模&#xff1a;维度建模&#xff1a;【事实表、维度表】 - 分层&#xff1a;ODS、DW【DWD、DWM、DWS】、APP - **掌握本次项目中数仓的分层** - ODS、DWD、DWB、DWS、ST、DM 2. 业务系统流程和数据来源 - 数据源…

Webpack5搭建Vue环境 | Webpack

文章目录webpack打包其他资源图片资源file-loader文件的命名规则url-loaderwebpack5 asset方式字体文件的打包PluginCleanWebpackPluginHtmlWebpackPluginDefinePluginCopyWebpackPluginmode配置webpack打包其他资源 图片资源 虽然此时我未安装file-loader 但是我正常显示了图片…

SHOP++ V9.1商城系统:可视化装修,0基础也能打造高颜值商城!

SHOP B2B2C商城系统新版本V9.1 新增的店铺装修功能是一款针对电商的DIY装修工具。可以快速装修店铺。能同时满足不同用户的使用需求。 一、页面可视化编辑组件 电商大潮异军突起的今天&#xff0c;如何让你的商品页面快速的抓住顾客的眼球&#xff0c;是促成订单转化的重要因素…

项目实战之旅游网(二)后台用户管理(上)

目录 一.管理员列表 二. 新增管理员 三.修改管理员 四.管理员详情 一.管理员列表 后台用户也称为管理员&#xff0c;每个管理员能在后台进行的操作不同,所以不同的管理员有不同的权限。在项目中&#xff0c;权限表的设计为用户-角色多对多&#xff0c;角色权限多对多&…

四、网络层(四)IPv6

目录 4.1 IPv6的主要特点 4.2 IPv6地址 4.3 从IPv4到IPv6过渡 4.1 IPv6的主要特点 解决IP地址耗尽问题的措施有以下3种 采用无分类域间路由&#xff0c;使IP地址的分配更加合理。采用网络地址转换NAT以节省全球IP地址。采用具有更大地址空间的新版本的IP协议IPv6。&am…

力扣(LeetCode)199. 二叉树的右视图(C++)

迭代 按照层序遍历&#xff0c;每一层最右边的元素就是二叉树右视图的元素。迭代算法需要队列保存二叉树每一层的所有结点&#xff0c;并且在遍历下一层时&#xff0c;上一层的所有结点已经出队。在遍历下一层之前&#xff0c;记录队列大小&#xff0c;即为二叉树当前层的结点…

三.keepalived介绍及工作原理

keepalived介绍及工作原理keepalived介绍及工作原理一、keepalived的介绍二、Keepalived服务的重要功能1、管理LVS负载均衡软件2、实现对LVS集群节点健康检查功能&#xff08;healthcheck&#xff09;3、作为系统网络服务的高可用功能&#xff08;failover&#xff09;三、Keep…

Redis 集合(Sorted Set)方法使用详解

目录一、简介二、常用方法2.1、ZADD2.2、ZREM2.3、ZSCORE2.4、ZINCRBY2.5、ZCARD2.6、ZRANK、ZREVRANK2.7、ZRANGE、ZREVRANGE2.8、ZRANGEBYSCORE、ZREVRANGEBYSCORE2.9、ZCOUNT2.10、ZREMRANGEBYRANK2.11、ZREMRANGEBYSCORE2.12、ZINTERSTORE、ZUNIONSTORE2.13、ZRANGEBYLEX、…

day 9 模拟和高精度

P4924 [1007]魔法少女小Scarlet 题目描述 Scarlet 最近学会了一个数组魔法&#xff0c;她会在 nn 二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 90∘。 首先&#xff0c;Scarlet 会把 1 到 n^2 的正整数按照从左往右&#xff0c;从上至下的顺序填入初始的二维数组中…

带您认识spreadsheet专属的数据仓库,助力报表开发好帮手

业务主题是针对业务的一个概念&#xff0c;它将同一数据源中属于同一个分析主题的表或表中的字段组合在一起&#xff0c;为进一步的可视化数据集提供基本元素。 业务主题我们看作是 Smartbi 产品中的数据仓库&#xff0c;主要应用于可视化数据集等操作。 业务主题的应用场景有如…

佳缘线上超市网站

成品详细信息 开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 基于SPRINGBOOTE的佳源线上超市管理系统的设计与实现 模块划分&#xff1a;公告类型模块、公告信息模块…

【Pycharm教程】在 PyCharm 中使用 Emacs 作为外部编辑器

Emacs是一个高级的文本编辑器&#xff0c;可以让你全面地操作你的代码。使用 Emacs 意味着可以根据您用于编码的语言进行完全可调整的编辑。 本文介绍了如何将 Emacs 添加到 PyCharm 并使用它来修改您的代码。 最新版本的 Emacs 可以从官网下载安装。 将 Emacs 配置为外部编…

介绍在全志A40i Ubuntu系统上定制文件系统的操作过程

为了给客户的测试和开发提供便利&#xff0c;飞凌嵌入式的开发板产品一般都支持多种操作系统&#xff0c;以基于全志T507H处理器设计研发的OKT507-C开发板为例&#xff0c;它支持三种操作系统&#xff0c;分别是&#xff1a;Linux、Android 和 Forlinx Desktop&#xff08;Ubun…