性能翻倍!揭秘编译器如何偷偷加速你的C++代码 - RVO/NRVO详解
转载自:跟着小康学编程
你有没有好奇过,为什么有时候 C++ 代码明明应该很慢,却跑得飞快?今天咱们就一起来扒一扒编译器背后的那些小动作!
前言:编译器背后的"暗箱操作"
嘿,大家好,我是小康。
前段时间我在调试一段代码时,发现了一个有趣的现象:我写了一个函数,它返回了一个超大的对象(几G那种),按理说这玩意复制一次得花不少时间,可实际运行起来却快得出奇。
当时我就纳闷了:这不科学啊!
直到我深究了"RVO"和"NRVO",这才恍然大悟。原来编译器早就偷偷帮我们做了优化,只是我们不知道而已!
今天,就让我们一起来扒一扒这些编译器背后的小动作,看看它们是如何在你不经意间就帮你的代码提速的。不管你是刚入门的小白,还是已经写了几年代码的老鸟,相信都能从中有所收获。
一、什么是返回值优化(RVO)?
先来聊聊没有优化时会发生什么
想象一下这个场景:你写了一个函数,它需要返回一个大对象,比如说这样:
class BigObject {
// 假设这个类很大,有一大堆数据
char *data;
// ...其他成员
public:
BigObject() {
cout << "构造函数被调用" << endl;
}
BigObject(const BigObject& other) {
cout << "复制构造函数被调用" << endl;
// 复制数据
}
~BigObject() {
cout << "析构函数被调用" << endl;
}
};
BigObject createBigObject() {
// 直接返回一个临时对象
return BigObject(); // 返回一个无名临时对象
}
int main() {
BigObject myObj = createBigObject(); // 调用函数并接收返回值
// 使用myObj...
return 0;
}
按照 C++ 的基本规则,这段代码的执行过程应该是这样的:
1、在createBigObject()函数内部创建一个临时的BigObject对象
2、当函数返回时,把这个临时对象复制一份到main()函数的myObj变量中
3、销毁函数内的临时对象
所以按道理说,这里至少会调用一次构造函数和一次复制构造函数,对吧?
但是!如果你实际运行这段代码并打印出构造和复制构造的调用情况,你很可能会惊讶地发现:复制构造函数根本没被调用!
这是为什么呢?这就是今天的主角——返回值优化(Return Value Optimization, RVO)在默默发挥作用。
RVO是什么鬼?
RVO,全称 Return Value Optimization,中文叫"返回值优化",是一种编译器优化技术。简单来说,它可以消除函数返回时的对象复制操作。
回到刚才的例子,使用 RVO 后,编译器会直接在main()函数的myObj变量所在的内存位置上构造对象,而不是先在createBigObject()函数内构造,再复制出来。这样就完全省去了复制的开销!
是不是很神奇?明明我们写的代码逻辑上需要复制,但编译器却偷偷帮我们优化掉了。这种优化在 C++11 标准中被称为"复制省略"(copy elision),是少数几个允许编译器改变程序可观察行为的优化之一。