类和对象——编译器自身对于拷贝的优化

一、优化实例

我们在调用函数来进行传参和返回返回值的时候,往往会面临多次的拷贝,创建多个临时变量,这毫无疑问会加重编译器的负担,所以编译器也会进行自身的优化。
接下来我们通过实例来好好理解一下优化的都有哪些地方。

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);
 cout << endl;
 // 四、一个表达式中,连续构造+拷贝构造
 f1(A(2));
 cout << endl;
 // 五、一个表达式中,连续拷贝构造+拷贝构造
 A aa2 = f2();
 cout << endl;
 // 六、一个表达式中,连续拷贝构造+赋值重载
 aa1 = f2();
 cout << endl;
 return 0;
}

我们来对这段代码进行分析。

  1. 先有了一个普通的构造,创建了aa1,然后调用了f1函数,f1函数的参数是一个传值类型,所以在这里还需要进行一次拷贝构造。传值输入的输出值
    所以传值传参就是一次构造加一次拷贝构造,并没有优化。

  2. 在进入f2函数之后,首先是创建了一个A类的对象,这是普通的构造,随后这个函数进行了一次传值返回,而传值返回需要进行一次拷贝构造。
    在这里插入图片描述
    所以传值返回也是一次构造加一次拷贝构造,也不存在优化。

  3. 首先我们要可以看出来这是一个隐式类型转换,是将int类型转换成自定义类型,在这个过程经历的是一次构造加一次拷贝构造,这次拷贝构造是直接将值赋给了f1的参数aa,然后这里就涉及到了编译器的自身优化。当在同一个表达式中,有连续的构造加拷贝构造时,编译器会自动将这几个步骤合并成一个直接构造。 特别需要注意的是,要在同一个表达式中,因为这个式子和第一个例子传值传参的区别就在于,第一个例子用了两个表达式来完成这个操作。我们同时还要注意,这种隐式类型转换只限于单参数的情况下进行。
    在这里插入图片描述

  4. 这种定义是使用了匿名对象的方法,先构造了一个值为2的类,本质上和上面这种没有区别,都**是构造然后加拷贝构造,**又因为在同一个表达式中,所以编译器也会将其进行优化,将其变为一个直接构造。
    在这里插入图片描述

  5. 在f2函数中,先是进行了一个构造,然后因为是传值返回所以这中间还有一个拷贝构造,在函数执行完毕之后,我们发现,等式的左边又定义了一个新的对象,所以这又是一次拷贝构造。在这个表达式中,我们可以发现,一共经历了一次构造加两次拷贝构造。于是,编译器又会进行自身的优化,就是将最后两次拷贝构造优化为一次拷贝构造
    在这里插入图片描述
    这里的构造加拷贝构造就不会再进行第二次优化了,因为f2函数中的aa的构造和后面两次拷贝构造并不在同一个表达式中,所以不能进行优化。

  6. 最后一个比较特殊,即有运算符重载的问题,又有传引用的问题。我们一点一点来看,首先是传引用,我们在传引用的时候,可以大致理解为c语言的指针,所以这里面并不存在类的拷贝构造,传过去的是类本身的地址,包括返回值也一样,都不涉及拷贝的事情。第六个跟第五个的区别就在于,上一个是在定义的时候使用的运算符重载,所以是属于拷贝构造,但是这个是先进行了构造,然后再进行的运算符重载,就是一个单纯的赋值重载而已。所以整体下来是一个构造然后加一个拷贝构造,最后来一个赋值重载。 但是拷贝构造加赋值重载的形式并不会让编译器对其进行优化。

二、总结

1.优化总结

  1. 首先应该注意所有连续的操作是否都在同一个表达式中,这是一切优化的前提,如果不在同一个表达式中,那么编译器不会进行任何优化。
  2. 当有连续构造时,后面是拷贝构造或者仍然是构造时,都会将其优化为一个直接构造。
  3. 当有连续拷贝构造时,会直接将其优化为一个拷贝构造。
  4. 当其最后是赋值重载时,编译器不会对其进行优化。

2.编程建议

  1. 传参的时候,尽量使用const加引用,这样可以减少很多拷贝,给编译器减少负担,并且还可以直接修改对象。但是需要注意传const对象时权限的放大问题。

  2. 在写返回值的时候,要尽量返回匿名对象,可以增加编译器的优化。

  3. 在接受返回值的时候,尽量使用拷贝构造的方式进行接收,这样可以直接进行优化,赋值重载的形式编译器不会进行优化。

创作不易,感谢阅读。

  • 59
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值