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

<think>嗯,用户问的是关于C++函数返回值的相关内容。我需要先理清楚这个问题涉及的知识点。首先,函数返回值型可以是基本型、型、指针、引用,甚至void。然后,要考虑返回机制,比如返回值优化RVO)和命名返回值优化(NRVO)。此外,返回局部变量时的注意事项,特别是返回局部变量的引用或指针会导致悬垂引用的问题。还有右值引用和移动语义在返回值中的应用,比如使用std::move提升效率。另外,可能还要提到C++17以后的强制复制消除。还有,用户可能需要知道如何处理返回大对象时的性能问题,以及返回型推导(C++14的auto返回型)。这些点都要组织好,分点详细说明,同时给出示例代码。最后,要提出相关问题,确保覆盖返回值的各个方面,比如临时对象的生命周期、RVO的工作原理、移动构造函数的作用等。检查是否有遗漏,比如是否提到了返回型为void的情况,或者返回引用时的有效性保证。确保每个问题点都有对应的解释和代码示例,这样用户能更好地理解。</think>C++函数返回值机制详解及代码示例: 1. 基本返回机制: 当函数返回非引用型时,会创建值的副本(可能触发拷贝构造函数)。对于小型(如int)直接通过寄存器返回,大对象通常通过隐藏指针传递。 示例: ```cpp int add(int a, int b) { return a + b; // 返回值的副本 } std::string createGreeting() { return "Hello World"; // 可能触发拷贝构造 } ``` 2. 返回值优化RVO): 编译器优化技术,允许直接在调用者的内存空间构造返回值,避免不必要的拷贝。 示例: ```cpp std::vector<int> createVector() { return std::vector<int>{1,2,3}; // 可能应用RVO } ``` 3. 返回引用: - 返回左值引用:必须确保引用对象在函数返回后仍然有效 - 返回右值引用:C++11起支持,用于移动语义 示例: ```cpp // 返回静态变量的引用(安全) const std::string& getDefaultName() { static std::string defaultName = "Guest"; return defaultName; } // 危险示例:返回局部变量引用 const std::string& dangerous() { std::string local = "temp"; return local; // 悬垂引用! } ``` 4. 移动语义(C++11): 当返回局部对象时,编译器会优先使用移动构造函数而不是拷贝构造函数 示例: ```cpp std::unique_ptr<int> createResource() { auto res = std::make_unique<int>(42); return res; // 使用移动构造,无拷贝开销 } ``` 5. 结构化绑定(C++17): 支持返回元组或结构体并解包 示例: ```cpp std::tuple<int, std::string> getData() { return {42, "answer"}; } auto [num, str] = getData(); // 结构化绑定 ``` 6. [[nodiscard]]属性(C++17): 强制要求必须处理返回值 示例: ```cpp [[nodiscard]] int criticalValue() { return 42; } // 调用未接收返回值会产生警告 ``` 7. 返回型推导(C++14): 使用auto自动推导返回型 示例: ```cpp auto multiply(double a, double b) -> decltype(a*b) { return a * b; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值