C++函数返回值优化(RVO)


一 函数返回值优化的引出

   在说C++函数返回值之前我们来看这样一份代码,代码如下:

class A {
public :
    A() {
        cout << "constructor" << endl;
    }
    A(const A &obj) {
        cout << "copy constructor" << endl;
    }
};

A func() {
    A temp;
    /* do some operations to temp */
    return temp;
}

int main() {
    A a(func());
    return 0;
}

  观测代码可知:我们定义了一个类A, 该类有两个构造函数,一个是无参构造函数、一个是复制构造函数(或称为拷贝构造函数),现在我们跟着代码执行的流程,确定一下代码的输出。
  当然,在这之前我们先来说一说,对象创建的过程,即对象的初始化!

1. 对象的初始化

普通对象初始化
  一般来说,对象的初始化分为三步。第一步:在内存中为对象开辟内存空间;第二步:匹配相应的构造函数;第三步:完成对象的初始化。在这三步中,小编认为最重要的就是前两步。之前在学习C++的时候,书中关于构造函数的功能介绍中有一句话:“为对象成员开辟内存空间”,但现在看来显然和上面所说的对象初始化三步曲是矛盾的。试问,若先开辟对象成员内存空间,后匹配构造函数,则开辟对象成员内存区域时,连选用哪个构造函数都不确定,又何来用构造函数为对象成员开辟内存空间一说???

  为此小编认为,从定义该对象到执行对象的构造函数之前,对象成员的内存中空间已经开辟,构造函数仅仅是对对象成员进行初始化赋值罢了(当然了,若对象的成员变量中存在一个指针,该指针是数组的首地址,在构造函数中malloc()或者new出来一片数组内存空间,就不仅仅是初始化成员变量了,也是开辟对象的内存空间)。

除了上面的成员变量初始化,还有下面的两种,思想和上面都是一样的。
复制构造函数
在这里插入图片描述

2. 代码实际输出和理论输出

了解对象初始化过程后,我们一起猜测程序的输出是什么?

  1. 主函数中,对于对象a,首先开辟对象a的内存空间,之后匹配a的构造函数,这里显然是复制构造函数,但注意,此时复制构造函数还没有执行,需要等func()函数执行完成之后才行。所以接下来执行函数func()。
  2. 进入func()函数后,实例化对象temp,这里调用了无参构造函数,所以代码输出 constructor并换行;之后函数返回temp,故从temp到匿名变量之间存在一次复制构造函数(这个匿名对象程序员看不见摸不着,是透明的),所以代码输出 copy constructor并换行.
  3. 之后从匿名对象到对象a存在一次复制,之前卡住没有执行的复制构造函数终于执行了,故此时代码再一次输出copy constructor并换行。

在这里插入图片描述

但是很遗憾,运行代码发现,程序的输出并不是我们所想的那样。

// 猜想输出
constructor
copy constructor
copy constructor

// 运行代码的实际输出
constructor

之所以会有这样的差异,就是因为 C++中的返回值优化 (Return Value Optimization)

二 返回值优化(RVO)

1. 优化方法一

上面的初始化流程我们可以得到下面的图:

在这里插入图片描述
  买二手车去哪里?当然是瓜子二手车! 为啥?因为没有中间商赚差价!
  同理,去掉这里的中间商——匿名对象,是不是更好一些呢?为此我们直接在返回temp对象的时候,用temp对象调用对象a的复制构造函数。这样就没有了匿名对象,更不会执行从匿名对象到对象a的复制构造函数。 (好像在微软系列的IDE中采用的返回值优化就是这种,但是小编没有亲手试过,要是小编说错了,烦请各位看官少点骂,轻些打,在评论区帮小编指出来,谢谢大家了)

在这里插入图片描述

2. 优化方法二

  观察第一种优化,感觉还是不彻底,举个浅显的例子:假设主函数中的a对象就是看官自己,而func()函数中的temp对象是看官的朋友小明,而func()函数就是班主任;若班主任给了小明一拳,小明马上就以相同的方式给你一拳,班主任给了小明一巴掌,小明马上就以相同的方式给你一巴掌。那么对你而言,反正老师怎么揍小明的,小明就怎么揍你,是不是干脆就让老师揍你自己就行了,反正对于你自己而言,挨的打都是一样多。
  所以func()函数中的temp对象其实就是对象a的引用,temp和a都对应一个对象、指向同一片内存空间。所以才再回头看代码的输出,没有了两次复制构造,剩下的输出自然就只剩下了 constructor。
在这里插入图片描述

在这里插入图片描述
为此,我们也可以进行验证,在代码中我们可以分别输出对象a和对象temp的地址,结果显示是同一个地址,由于代码量很小,这里小编就不再展示了,各位看官自己操作即可。

三 如何关闭函数返回值优化

  想要关闭函数返回值优化,仅需在编译.cpp文件时加上参数 -fno-elide-constructors即可,如g++ -fno-elide-constructors RVO.cpp,关闭返回值优化后,运行代码,此时代码输出和我们之前的猜想是一致的,并且输出temp对象的地址和a对象的地址是不一致的,这一点看官自己操作即可。


  至此,关于C++函数返回值优化,小编就讲完了(是不是觉得明明几段话就可以说的明白,非要写这么多,觉得小编很啰嗦。没办法,可能小编就是一个啰嗦的人吧,怕自己没有说明白,这也算是对自己和看官负责 !敲字不易,这篇博文对您有帮助的话,请帮忙点个赞吧!你的鼓励是小编最大的动力!)
  加油!路漫漫其修远兮,吾将上下而求索,与君共勉!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值