C++从入门到起飞之——友元内部类匿名对象对象拷贝时的编译器优化 全方位剖析!

news2025/4/4 3:03:51

🌈个人主页:秋风起,再归来~
🔥系列专栏:C++从入门到起飞          
🔖克心守己,律己则安

目录

1、友元

2、内部类

3、 匿名对象

4、对象拷⻉时的编译器优化

5、完结散花


1、友元

• 友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数友元类,在函数声明或者类 声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯

友元类:

class A
{
public:
	//B是A的友元类
	friend class B;
private:
	int _a1 = 1;
	int _a2 = 2;
};

class B
{
public:
	B()
	{
		//......
	}
	void func(const A& aa)
	{
		//访问A的私有成员
		cout << aa._a1 << endl;
		cout << aa._a2 << endl;
	}
private:
	int _b1 = 3;
	int _b2 = 4;
};

int main()
{
	A aa1;
	B bb1;
	bb1.func(aa1);
	return 0;
}

 友元函数:

class A
{
public:
	//B是A的友元类(友元声明)
	friend class B;
	//func是A类的友元函数(友元声明)
	friend void func(const A& aa);
private:
	int _a1 = 1;
	int _a2 = 2;
};

void func(const A& aa)
{
	cout << aa._a1 << endl;
	cout << aa._a2 << endl;
}

外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数

• 友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制

• ⼀个函数可以是多个类的友元函数

// 前置声明,不然A的友元函数声明编译器不认识B
class B;

class A
{
public:
	//B是A的友元类(友元声明)
	friend class B;
	//func是A类的友元函数(友元声明)
	friend void func(const A& aa,const B& bb);
private:
	int _a1 = 1;
	int _a2 = 2;
};

class B
{
public:
	friend void func(const A& aa, const B& bb);
private:
	int _b1 = 3;
	int _b2 = 4;
};

void func(const A& aa, const B& bb)
{
	cout << aa._a1 << endl;
	cout << aa._a2 << endl;
	cout << bb._b1 << endl;
	cout << bb._b2 << endl;
}

int main()
{
	A aa1;
	B bb1;
	func(aa1,bb1);
	return 0;
}

友元类中的成员函数可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。 • 友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元(A可以访问B的私有或保护成员,但B不可以),但是B类不是A类的友元。

• 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是B的友元。

• 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤。

2、内部类

• 如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类定义在 全局相⽐,他只是受外部类类域限制访问限定符限制,所以外部类定义的对象中不包含内部类

• 内部类默认是外部类的友元类(即内部类可以访问外部类的私有和保护成员)。


class A
{
public:
	//内部类
	class B//默认是A的友元类
	{
	public:
		void func(A& aa)
		{
			A::a++;
			aa.b++;//访问A类的私有成员
		}
	private:
		int b1 = 1;
		int b2 = 2;
	};
private:
	static int a;
	int b = 1;
};

int main()
{
	A a;
	cout << sizeof(a) << endl;
	return 0;
}

a对象的大小是4说明B类是一个独立的类 ,外部类定义的对象中不包含内部类!

• 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考 虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其 他地⽅都⽤不了。

上篇文章的OJ题就可以用内部类来进行封装!

求1+2+3+...+n_⽜客题霸_⽜客⽹

描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围:0<n≤200
进阶: 空间复杂度 O(1) ,时间复杂度 O(n)

示例1

输入:

5

复制返回值:

15

示例2

输入:

1

复制返回值:

1

OJ链接

class Solution 
{
    class Sum
    {
    public:
        Sum()
        {
            ret+=i;
            ++i;
        }
    };
    static int i;
    static int ret;
public:
    int Sum_Solution(int n)
    {
        //Sum a[n];变长数组
        //创建n个对象来调用n次构造函数
        Sum* p=new Sum[n];
        return ret;
    }
   
};
//用静态的成员变量来记录结果
int Solution::i=1;
int Solution::ret=0;

3、 匿名对象

• ⽤类型(实参)定义出来的对象叫做匿名对象相⽐之前我们定义的类型对象名(实参)定义出来的 叫有名对象

• 匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象。

class A
{

};

int main()
{
	A();//定义的匿名对象,生命周期只存在当前这一行
}
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(1);
	A aa2(2);
	// 匿名对象在这样场景下就很好⽤,当然还有⼀些其他使⽤场景,这个我们以后遇到了再说
	Solution().Sum_Solution(10);
	return 0;
}

4、对象拷⻉时的编译器优化

• 现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传参 过程中可以省略的拷⻉

• 如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编 译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译还会进⾏跨 ⾏跨表达式的合并优化。

class A
{
public:
	A(int a=0)
	{
		cout << "调用构造!" << endl;
	}
	A(const A& a)
	{
		cout << "调用拷贝构造!" << endl;
	}
	~A()
	{
		cout << "调用析构!" << endl;
	}
private:
	int _a;
};

int main()
{
	//原来是先用1调用构造创建一个临时对象,再用临时对象拷贝构造对象a1
	//在VS2022上编译器优化为直接构造a1
	A a1 = 1;
	return 0;
}

原来是先用1调用构造创建一个临时对象,再用临时对象拷贝构造对象a1,在VS2022上编译器优化为直接构造a1!

跨 ⾏跨表达式的没有优化:

class A
{
public:
	A(int a=0)
	{
		cout << "调用构造!" << endl;
	}
	A(const A& a)
	{
		cout << "调用拷贝构造!" << endl;
	}
	~A()
	{
		cout << "调用析构!" << endl;
	}
private:
	int _a;
};

void f(A aa)
{
	//......
}

int main()
{
	A a(1);//构造
	f(a);//传值传参,拷贝构造
	return 0;
}

 构造与拷贝构造并没有在一个连续的步骤当中,所以编译器并没有进行优化!

匿名对象触发优化&隐式类型转换触发优化:

int main()
{
	//A a(1);//构造
	//f(a);//传值传参,拷贝构造

	f(A(1));//匿名对象触发优化
	cout << endl;
	f(1);//隐式类型转换触发优化
	return 0;
}

 传值返回进行优化:

class A
{
public:
	A(int a=0)
	{
		_a = a;
		cout << "调用构造!" << endl;
	}
	A(const A& a)
	{
		cout << "调用拷贝构造!" << endl;
	}
	~A()
	{
		cout << "调用析构!" << endl;
	}
	void Print()
	{
		cout << "_a->" << _a << endl;
	}
private:
	int _a;
};

void f(A aa)
{
	//......
}

//传值返回
A f2()
{
	A aa(1);//调用构造
	return aa;//调用拷贝构造创建临时对象
}

int main()
{
	f2().Print();
	return 0;
}

在VS2019debug模式下,f2函数体内,先调用构造函数初始化aa, 再用aa调用拷贝构造创建临时对象作为返回值,函数调用结束后,aa生命周期结束,调用析构函数。临时对象作为返回值其生命周期在当前一行,调用Print函数后,生命周期结束,再次调用析构。

 而在在VS2022debug模式下,其优化更为激进,编译器并没有构造aa,而是直接构造临时对象。为什么是没有构造aa呢?原因就在于析构函数的调用是在Print函数调用之后才调用的!

 既然编译器敢这么优化,就不怕出bug吗?下面我们来写一个程序测试一下!

class A
{
public:
	A(int a=0)
	{
		_a = a;
		cout << "调用构造!" << endl;
	}
	A(const A& a)
	{
		cout << "调用拷贝构造!" << endl;
	}
	~A()
	{
		cout << "调用析构!" << endl;
	}
	void Print()
	{
		cout << "_a->" << _a << endl;
	}
	//这里重载一个前置++
	A& operator++()
	{
		_a += 100;
		return *this;
	}
private:
	int _a;
};

void f(A aa)
{
	//......
}

//传值返回
A f2()
{
	A aa(1);//调用构造
	++aa;//如果编译器还像之前那样优化,并且打印的_a是101,就算他牛逼!
	return aa;//调用拷贝构造创建临时对象
}

int main()
{
	f2().Print();
	return 0;
}

 这里没有构造aa,却在构造临时对象时根据程序员的想法,对_a进行了++操作,我们可以看到编译器敢这么优化,他还是敢作敢当的!

5、完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

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

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

相关文章

springAOP理解及事务

AOP&#xff1a; springAOP是什么&#xff1a; AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实就是面向特定方法编程。 使用场景&#xff1a; 比如你想统计业务中每个方法的执行耗时&#xff0c;那我们最…

基于bert的自动对对联系统

目录 概述 演示效果 核心逻辑 使用方式 1.裁剪数据集 根据自己的需要选择 2.用couplet数据集训练模型 模型存储在model文件夹中 3.将模型转换为ONNX格式 4.打开index.html就可以在前端使用此自动对对联系统了。 本文所涉及所有资源均在传知代码平台可获取。 概述 这个生成器利用…

什么是婚恋聊天交友源码?今天大家讲解一下。源码交付,支持二开,可打包APP小程序H5。

婚恋交友APP开发前景 对于现代的年轻人来说&#xff0c;社恐已经是深入骨子里不可别除的&#xff0c;除了每天上班下班&#xff0c;许多人宁愿宅在家里&#xff0c;面对线下的相亲机构&#xff0c;家里长辈介绍的会都是饭度抗柜的。而这几年疫情的影响更是大大的限制了正常的社…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

7. 运行时数据区-栈

栈的分类 栈分为Java虚拟机栈还有本地方法栈&#xff1a; Java虚拟机栈&#xff1a;用于保存Java中的方法相关的内容本地方法栈&#xff1a;用于保存在Java中使用native 标记的用C来实现方法 由于hotspot的作者发现使用一个栈就可以保存以上两个部分的内容&#xff0c;所以在…

图像生成中图像质量评估指标—PSNR的详细介绍

文章目录 1. 背景介绍2. 实际应用3. 总结和讨论 1. 背景介绍 峰值信噪比&#xff08;Peak Signal-to-Noise Ratio&#xff0c;简称PSNR&#xff09;是一种广泛应用于图像和视频处理领域的客观图像质量评价指标。它主要用于衡量图像的噪声水平和图像质量&#xff0c;可以用来评…

HttpClient初学

介绍&#xff1a; HttpClient 是Apache Jakarta Common 下的子项目&#xff0c;可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新的版本和建议。 这里阿里云的oss依赖底层是httpclient&#xff0c;所以这里不再重…

用f-string+sys.stdout.write定制“自己的writer”

f-stringsys.stdout.write&#xff0c;在python中“随意”我的输出。 (笔记模板由python脚本于2024年07月29日 08:09:35创建&#xff0c;本篇笔记适合喜欢python并有一定基础的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&am…

dotnet开发编译之争:Ahead-of-Time(AOT) vs Just-in-Time(JIT)谁才是未来最佳编译选择?

1. 前言 编译技术的选择对于现代应用程序的性能至关重要。在.Net开发平台下&#xff0c;选择合适的编译策略对于提升应用程序的响应速度、资源利用率以及最终用户体验有着不可忽视的影响。其中&#xff0c;Ahead-of-Time (AOT) 编译和 Just-in-Time (JIT) 编译是两种广泛采用的…

【编程工具使用技巧】VS如何显示行号

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《编程工具与技巧探索》 期待您的关注 目录 引言 一、VS编译器行号显示的基本步骤 1.打开VS与项目 2.进入选项设置 3.找到并…

Open3D 计算点到平面的距离

目录 一、概述 1.1原理 1.2实现步骤 1.3原理 二、代码实现 1.1关键函数 1.2完整代码 三、实现效果 3.1原始点云 3.2计算距离后赋色的点云 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#…

【更新2022】省级农业科技活动人员数测算 2009-2022无缺失

省级农业科技活动人员数测算数据在农业经济学、政策研究和农村发展规划等领域的论文研究中具有重要应用价值。首先&#xff0c;这些数据可用于分析省级农业科技活动的规模和结构变化&#xff0c;揭示不同地区在农业科技投入和产出方面的差异&#xff0c;为政府制定农业发展政策…

锅总浅析系统设计

如何进行系统设计&#xff1f;系统设计最佳实践有哪些&#xff1f;系统设计和软件工程有何区别&#xff1f;如何避免过度设计&#xff1f;学习书籍及软件工具推荐有哪些&#xff1f;前后端语言选型有哪些原则&#xff1f;考虑政策因素的系统设计步骤是怎样的&#xff1f; 带着这…

三维推:二维码生成与修改、加logo、设置有效期

进入后台&#xff0c;找到【二维码工具】下的【二维码在线生成】&#xff0c;可以看到&#xff0c;三维推支持网址、图片、音频、视频、文件以及模板生成二维码。 这里&#xff0c;我们以网址生成二维码为例来演示下&#xff0c;如何给二维码加logo、更改颜色等。 首先&#x…

操作系统---进程调度算法相关习题

例题1&#xff1a;某系统采用基于优先权的非抢占式进程调度策略&#xff0c;完成一次进程调度和进程切换的系统时间开销为1us。在T时刻就绪队列中有3个进程P1、P2和P3&#xff0c;其在就绪队列中的等待时间、需要的 CPU 时间和优先权如下表所示。 若优先权值大的进程优先获得CP…

Git基础概念一览:仓库、协议、原理、服务器搭建全解析

Git基础概念详解 一、引言 二、基础概念概览 三、安全通信与协议 四、Git 服务器搭建与操作 五、Git 工作流程与原理 六、Git 日志与操作 七、总结 一、引言 “ 掌握Git的基本概念是高效进行版本控制的第一步。本文将详细介绍如何创建本地和远程仓库&#xff0c;G…

【HarmonyOS4+NEXT】新建项目指南

&#x1f64b;‍ 一日之际在于晨 ⭐本期内容&#xff1a;新建项目指南 &#x1f3c6;系列专栏&#xff1a;鸿蒙HarmonyOS4NEXT&#xff1a;探索未来智能生态新纪元 文章目录 创建项目项目结构概述运行项目Preview预览模拟器运行真机运行 ArkUI框架简介总结 创建项目 点击创建一…

撰写LabVIEW开发方案时,怎么结构清晰、内容详实?

撰写LabVIEW开发方案时&#xff0c;结构清晰、内容详实是至关重要的。以下是一些步骤和技巧&#xff0c;帮助你组织和编写一个高质量的LabVIEW开发方案&#xff1a; 1. 封面 项目名称公司名称日期作者 2. 目录 列出各章节及其页码 3. 摘要 项目概述&#xff1a;简要介绍项…

vue、react前端框架实现TodoList页面案例

原始TodoList网页&#xff08;主要就是链接里网页应用ndex.html、styles.css、script.js &#xff09;&#xff1a; https://blog.csdn.net/weixin_42357472/article/details/140657576 node、npn安装参考&#xff1a; https://blog.csdn.net/weixin_42357472/article/details/…