• 关于编译器对连续的构造+拷贝构造/连续的拷贝构造的优化


    一道很坑的题_(:з」∠)_:

    以下代码共调用多少次拷贝构造函数

    // 《深度探索C++对象模型》
    Weight f(Weight u)
    {
    	Weight v(u);
    	Weight w = v;
    
    	return w;
    }
    
    int main()
    {
    	Weight x;
    	Weight ret = f(f(x));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1 匿名对象

    为了让大家更好地理解上面那道题,我们先来介绍一下什么是匿名对象?
    匿名对象可以理解为是一个临时对象,一般系统自动生成的,如你的函数返回一个对象,这个对象在返回时会生成一个临时对象。
    语法:类名();
    注意:匿名对象,生命周期只在它定义的那一行

    我借助以下代码调试向大家说明:

    #include <iostream>
    using std::cout;
    using std::cin;
    using std::endl;
    
    class Weight
    {
    public:
    	Weight()
    	{
    		cout << "Weight()" << endl;
    	}
    
    	Weight(const Weight& w)
    	{
    		cout << "Weight(const Weight& w)" << endl;
    	}
    	
    	~Weight()
    	{
    		cout << "~Weight()" << endl;
    	}
    	
    	Weight& operator=(const Weight& w)
    	{
    		cout << "Weight& operator=(const Weight& w)" << endl;
    		return *this;
    	}
    	
    	void print()
    	{
    		cout << "2022-5-25" << endl;
    	}
    };
    
    // 《深度探索C++对象模型》
    Weight f(Weight u)
    {
    	Weight v(u);
    	Weight w = v;
    
    	return w;
    }
    
    int main()
    {
    	Weight x;
    	Weight();
    	f(x);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    在这里插入图片描述
    我们发现44行这个匿名对象调了构造函数之后,马上又调了析构函数

    2.匿名对象的用处

    我们平时调用类的成员函数,是不是先定义一个对象,通过对象去调用。
    匿名对象还可以这样调用:
    在这里插入图片描述
    还有,如果你只是单纯的想要传参,也可以用匿名对象:
    在这里插入图片描述
    但是我们发现左边传参的方式调用了4次拷贝构造,而右边传参的方式调用了3次拷贝构造。
    为什么?因为编译器优化了,编译器会在一个步骤里面,比如说一个表达式里面,如果出现连续的构造+拷贝构造或者连续的拷贝构造,编译器会把它们优化。
    左边没的说,因为是分开的,先构造了一个对象,然后把这个对象传参又调用了拷贝构造。
    右边是构造了匿名对象之后立马作为参数传参,这个时候编译器就会把它们合二为一,直接构造传参的对象。

    还记得我们以前说的拷贝构造的两种方式吗?

    class A 
    {
    public:
    	A(int x)
    	{}
    };
    
    int main()
    {
    	A a1(1);
    	A a2 = 2; // A(2)  -> A a2(A(2))   优化:直接构造
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    关于 A a2 = 2;这行代码,它会先拿2构造一个匿名对象(也叫临时对象),然后才会拿这个对象去拷贝构造一个a2。这个时候胆大的编译器也会优化,直接构造a2.

    注意:C++标准并没有规定是否优化,完全取决于编译器,不过新一点的编译器一般都会做这个优化。

    3.题解

    3.1 铺垫

    如果是以下的代码,我们的程序会调用几次拷贝构造?
    在这里插入图片描述

    ok,我们先自己来算一下。
    x传参拷贝构造1次,33、34行各拷贝构造1次,传值返回拷贝构造1次,41返回的结果又拷贝构造给ret,又是1次。是不是应该总共5次呀!

    ok,我们看运行结果:
    在这里插入图片描述
    怎么回事?
    在这里插入图片描述
    本来是不是应该这样啊,但是大家注意,41行是不是连续的拷贝构造啊,编译器会把它合二为一,相当于直接用w去拷贝构造ret了。

    如果大家还是不能确信,请看下图:
    在这里插入图片描述
    这个返回来的w的临时对象就是赋值给ret,因为ret对象已经存在。阻断了编译器的优化。

    3.2 解题

    在这里插入图片描述
    所以这道题总共一个调用了7次拷贝构造函数,我们运行一下看是不是:
    在这里插入图片描述
    哈哈,你答对了吗?(:з」∠)
    在这里插入图片描述

  • 相关阅读:
    @NotNull注解不生效,全局异常处理
    2022各大厂Java面试题最新汇总,收获满意offer
    qrcode.min.js下载
    截止2021年企业公众号开通数据(60万+记录)
    JAVA调用lua脚本
    R语言移除data.frame数据框中的NA值:使用na.omit移除包含任何NA值的行
    Redisson分布式锁学习
    电力电子转战数字IC20220630day36——路科实验3
    Windows系统使用Nginx部署Vue
    人工智能之机器学习
  • 原文地址:https://blog.csdn.net/iwkxi/article/details/124967000