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

🌈个人主页:秋风起,再归来~
🔥系列专栏: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上编译器优化为直接构造a1A 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.mzph.cn/news/876885.shtml

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

相关文章

基于爬虫和机器学习的招聘数据分析与可视化系统,python django框架,前端bootstrap,机器学习有八种带有可视化大屏和后台

在现代招聘领域&#xff0c;数据驱动的决策已成为提升招聘效率和质量的关键因素。基于爬虫技术和机器学习算法&#xff0c;结合Django框架和Bootstrap前端技术&#xff0c;我们开发了一套完整的招聘数据分析与可视化系统。该系统旨在帮助企业从海量招聘信息中提取有价值的数据&…

学习Numpy的奇思妙想

学习Numpy的奇思妙想 本文主要想记录一下&#xff0c;学习 numpy 过程中的偶然的灵感&#xff0c;并记录一下知识框架。 推荐资源&#xff1a;https://numpy.org/doc/stable/user/absolute_beginners.html &#x1f4a1;灵感 为什么 numpy 数组的 shape 和 pytorch 是 tensor 是…

等保2.0测评 — 容器测评对象选取

之前有小伙伴提问到&#xff0c;关于容器到底要测评哪些内容&#xff0c;也就是测评对象的选取。 首先要区分的是容器与容器集群这两个概念。容器集群概念可参考该篇文章。 不使用容器扩展要求情况 当仅使用容器技术时&#xff0c;采用安全通用要求&#xff0c;无需使用容器…

昇思25天学习打卡营第15天|探索 Diffusion 扩散模型:从构建到应用的全过程

目录 环境配置 构建Diffusion模型 位置向量 ResNet/ConvNeXT块 Attention模块 组规一化 条件U-Net 正向扩散 数据准备与处理 训练过程 推理过程 环境配置 首先进行环境配置、库的导入和一些设置操作&#xff0c;具体代码如下&#xff1a; %%capture captured_output …

土体的有效应力原理

土体的有效应力原理 有效应力原则1. 总应力的测定2. 孔隙水压力的测定3. 有效应力的确定 有效应力的重要性 土体中的有效应力原理是卡尔太沙基在1936年提出的重要理论之一。它是总应力和孔隙水压力之间的差值。下面简要说明土壤中有效应力的更多特征和测定。 有效应力原则 有…

人工智能入门第一篇:简单理解GPU和CPU

目录 1&#xff0c;GPU就是显卡吗2&#xff0c;CPU和GPU到底是什么区别3&#xff0c;CUDA是什么4&#xff0c;为什么人工智能离不开GPU 1&#xff0c;GPU就是显卡吗 ‌不是&#xff0c;显卡和‌GPU是两个相关但不完全相同的概念&#xff0c;GPU是显卡的核心部分&#xff0c;但…

Google Test 学习笔记(简称GTest)

文章目录 一、介绍1.1 介绍1.2 教程 二、使用2.1 基本使用2.1.1 安装GTest &#xff08;下载和编译&#xff09;2.1.2 编写测试2.1.3 运行测试2.1.4 高级特性2.1.5 调试和分析 2.2 源码自带测试用例2.3 TEST 使用2.3.1 TestCase的介绍2.3.2 TEST宏demo1demo2 2.3.3 TEST_F宏2.3…

wincc 远程和PLC通讯方案

有 5个污水厂 的数据需要集中监控到1个组态软件上,软件是WINCC。每个污水厂监控系统都是独立的&#xff0c;已经投入运行了&#xff0c; 分站也是WINCC 和西门子PLC 。采用巨控远程模块的话&#xff0c;有两种方式&#xff1a;一种是从现场的PLC取数据&#xff0c;一种是从分站…

2019数字经济公测大赛-VMware逃逸

文章目录 环境搭建漏洞点exp 环境搭建 ubuntu :18.04.01vmware: VMware-Workstation-Full-15.5.0-14665864.x86_64.bundle 这里环境搭不成功。。patch过后就报错&#xff0c;不知道咋搞 发现可能是IDA加载后的patch似乎不行对原来的patch可能有影响&#xff0c;重新下了patch&…

【8月EI会议推荐】第四届区块链技术与信息安全国际会议

一、会议信息 大会官网&#xff1a;http://www.bctis.nhttp://www.icbdsme.org/ 官方邮箱&#xff1a;icbctis126.com 组委会联系人&#xff1a;杨老师 19911536763 支持单位&#xff1a;中原工学院、西安工程大学、齐鲁工业大学&#xff08;山东省科学院&#xff09;、澳门…

科大讯飞语音转写demo go语言版

上传了一个语音文件&#xff0c;识别效果。 package audioimport ("bytes""crypto/hmac""crypto/md5""crypto/sha1""encoding/base64""encoding/json""fmt""io/ioutil""net/http"…

【图文详解】Spring是如何解决循环依赖的?

Spring是如何解决循环依赖的呢&#xff1f; 很多小伙伴在面试时都被问到过这个问题&#xff0c;刷到过这个题的同学马上就能回答出来&#xff1a;“利用三级缓存”。面试官接着追问&#xff1a;“哪三级缓存呢&#xff1f;用两级行不行呢&#xff1f;” 这时候如果没有深入研究…

Vs2022+QT+Opencv 一些需要注意的地方

要在vs2022创建QT项目&#xff0c;先要安装一个插件Qt Visual Studio Tools&#xff0c;根据个人经验选择LEGACY Qt Visual Studio Tools好一些&#xff0c;看以下内容之前建议先在vs2022中配置好opencv&#xff0c;配置方式建议以属性表的形式保存在硬盘上。 设置QT路径 打开v…

清华计算几何-算法LowBound和ConvexHull(凸包)-GrahamScan

算法复杂度最低界限LowBound 算法求解复杂度是否存在一个最低界限&#xff0c;有时候想尽一切办法优化一个算法&#xff0c;去优化其复杂度&#xff0c;比如 清华计算几何-ConvexHull(凸包)-求极点InTriangle/ToLeft Test-CSDN博客 清华计算几何-ConvexHull(凸包)-求极边_计…

DeFi革命:揭秘去中心化金融的核心技术与实操指南

目录 DeFi&#xff08;去中心化金融&#xff09;综述 基本特点 第一&#xff0c;DeFi 是无许可的金融 第二&#xff0c;DeFi 是无门槛的金融 第三&#xff0c;DeFi 是无人驾驶的金融 典型商业模式 闪电贷 MakerDAO 面临的挑战 DeFi技术要点 椭圆曲线签名 EIP-712:…

模拟依赖关系和 AI 是Vue.js测试的下一个前沿领域

Vue.js 是一个流行的 JavaScript 框架&#xff0c;因此&#xff0c;确保其组件按预期工作至关重要&#xff1a;有效&#xff0c;更重要的是&#xff0c;可靠。模拟依赖项是最有效的测试方法之一&#xff0c;我们将在本文中发现。 模拟依赖项的必要性 模拟依赖项是一种对测试施加…

个人定制化形象生成,FaceChain最新模型部署

FaceChain是阿里巴巴达摩院推出的一个开源的人物写真和个人数字形象的AI生成框架。 FaceChain利用了Stable Diffusion模型的文生图功能&#xff0c;并结合人像风格化LoRA模型训练及人脸相关感知理解模型&#xff0c;将输入的图片进行训练后推理输出生成个人写真图像。 FaceCh…

Live555源码阅读笔记:哈希表的实现(C++)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

算法日记day 20(中序后序遍历序列构造二叉树|最大、合并、搜索二叉树)

一、中序后序序列构造二叉树 题目&#xff1a; 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,…

使用SpringEvent解决WebUploader大文件上传解耦问题

目录 前言 一、SpringEvent涉及的相关组件 1、 事件&#xff08;Event&#xff09; 2、事件监听器 3、事件发布器 二、WebUploader大文件处理的相关事件分析 1、事件发布的时机 2、事件发布的代码 三、事件监听器及实际的业务处理 1、文件上传处理枚举 2、文件上传监…