从零开始的C++生活——第二讲——05

在第三讲开始之前,先补充一点第二讲的尾巴,先为大家介绍一下对象拷贝时的编译器优化知识。

之前我们介绍拷贝构造函数的时候也提到过,当你函数传参传一个自定义类型,理论上讲会调用拷贝构造,当你返回自定义类型对象,也会调用拷贝构造,当然,你利用内置类型使用某个构造方法隐式类型转换为相应的自定义类型对象的时候也会调用拷贝构造,其实这三者都有一个共性,也就是临时对象的生成,比如,你隐式类型转换,虽然只有一行代码,但系统会调用构造函数,初始化一个临时对象,然后调用拷贝构造函数拷贝给创建的对象,那你想想,你调用构造函数初始化的这个临时对象,也就只用于拷贝构造的参数传给拷贝构造函数了吧,其他也没啥用啊,那何必这么麻烦分两步,其实编译器也意识到了这一点,而这也正是我要跟大家讲的对象拷贝时的编译器优化相关。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>


using namespace std;

class A
{
public:
	A(int a = 10)
		:_a(a)
	{
		cout << "A(int a = 10)" << endl;
	}
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}

private:
	int _a = 1;
};



int main()
{
	A a1 = 2;


	return 0;
}

来看这一段代码的运行结果,显然编译器省略了拷贝构造,你也可以理解成,直接拿2去构造了a1对象。

cpp 复制代码
void f1(A aa)
{

}

我这里写这样一个函数,众所周知,传参穿自定义类型会调用拷贝构造,那假如我给他传一个内置类型,按理来说是构造加拷贝构造,编译器会对此优化吗

显而易见,编译器优化掉了拷贝构造,也就是说,编译器直接拿着1去f1里构造aa对象去了,并没有走繁琐的构造加拷贝构造逻辑。

再来看这种情况,即使我们显式的传一个构造函数,编译器也会优化,这段代码的逻辑是我们本身要调用构造函数创建一个A的匿名对象,然后将匿名对象传给f1的局部变量调用拷贝构造,这里依然是省略了拷贝构造,直接利用4去构造aa。

总结一下在vs2022等绝大多数编译器中,一条语句中的构造加拷贝构造会被优化为直接构造。

接下来看一下传值返回的情况。

cpp 复制代码
A f2()
{
	A aa;
	return aa;
}

看一下这两段代码,右边在我们的分析下,它应该是调用aa的构造返回时调用拷贝构造,左边应该是调用aa的构造,返回时调用拷贝构造给临时对象,再次调用拷贝构造给a2,感觉这就比上述情况复杂不少,但是对于vs2022这种相对比较晚的编译器来说,他的优化相当激进,所以,这两段代码你都可以认为是调用一次直接构造,对于右边,即使我们没有接收返回值,也是如此,左边则是调用了a2的直接构造。

这两种优化已经很夸张了其实,但是vs2022还有更夸张的

看到这段代码你是不是都懵了,欸?谁是a1啊?

看到了吗?a1是我们先前就创建的对象,中间经历了这么多逻辑,竟然给他赋值,vs2022还会优化,但是,这里一定不要理解成a1调用了两次构造函数,这是不正确的,这只是编译器的一个激进优化,并不能违背C++的语法。

至此,第二讲有关类和对象的知识已然彻底收尾,我们即将开始第三讲内存管理的学习。

相关推荐
greencherry_2 天前
芷桥迷宫社:看得见的图面,不应独自代表整本迷宫册
生活
杜 硕2 天前
从零开始的C++生活——第二讲——03
c++·生活
老木避暑研究所2 天前
如何定义一个可量化的“宜居指数”?——从云澜栖康养数据评估说起
生活
杜 硕2 天前
从零开始的C++生活——第二讲——04
生活
青木灵溪3 天前
中原地区“治未病”康养实践观察——以河南郑州康养业态为例
生活
2601_967287573 天前
浚县自建房室外排水管网:坡度、基础、闭水和回填
生活
jiuzhouxinghu4 天前
脊柱侧弯支具定制时,为什么身体尺寸不是唯一依据?
生活
2603_965148117 天前
商品价格监控 API 接口:从原理到实战的完整指南
java·前端·数据库·python·生活
2601_967287578 天前
浚县自建房外墙外保温,粘结锚固抹面三个环节的关键数字
生活