效率:条款20 协助完成“返回值优化(RVO)”

本文探讨了C++中返回对象时的效率问题,提出了通过返回构造函数参数而非对象来减少临时对象产生的优化方法,并详细解释了返回值优化(Return Value Optimization)的工作原理。

函数如果返回对象,对效率狂而言是一个严重的挫折,因为以by-value方式返回对象,背后隐藏的constructor和destructor都将无法消除。

有的人会返回指针,于是导致下列这种拙劣的语法形式:

const Rational* operator*(const Rational* lhs,const Rational* rhs)
{
    return new Rational(lhs*rhs);
}
她会引出一个重要的问题,导致内存泄漏的问题。
另一些程序员可能会返回reference,于是到处一个危险的做法:

const Rational& operator*(const Rational* lhs,const Rational* rhs)
{
    Rational result(lhs.a*rhs.a,lhs.b*rhs.b);
    return result;
}
此函数返回一个reference,指向一个不再存在的对象,更明确的说,它返回一个reference指向局部对象result,但该局部对象在返回时自动被销毁了,导致返回一个reference指向一个不存在的对象。
总的来说:如果函数一定得以by-value方式返回一个对象,你绝对无法消除之。这是一场错误的战争从效率的眼光来看,你不应该在乎函数返回了一个对象,你应该在乎的是那个对象的成本几何。你需要做的事女里找出某种方法来降低返回对象的成本,而不是想尽办法消除对象本身。

我们可以用某种特殊写法来撰写函数,使它在返回对象时,能够让编译器消除临时对象的成本。我们的伎俩是:返回所谓的constructor arguments(构造函数参数)一取代对象。如:

函数一:const Rational operator*(const Rational* lhs,const Rational* rhs)
{
    Rational temp(lhs.a*rhs.a,lhs.b*rhs.b);
    return temp;
}<pre name="code" class="cpp">函数二:const Rational& operator*(const Rational* lhs,const Rational* rhs)
{
 
    return Rational(<span style="font-family: Arial, Helvetica, sans-serif;">lhs.a*rhs.a,lhs.b*rhs.b);</span>
}
以上两者的区别在于函数一多创建了一个临时对象。而函数二仅仅创建了一个临时对象。函数二具体的做法是以constructor arguments取代局部对象,当作返回值。


如果你这样调用时:

Rational a=10;
Rational b(1,2);
Rational c=a*b;
C++则会允许编译器将临时对象优化使它们不存在。如上面公式所写,比的编译器会消除operator*内的临时对象及被operator*返回的临时对象。它们可以将return表达式所定义的对象构造于c的内存内。如果编译器这样做,你调用operator*时的临时对象总成本为0也就是说没有任何临时对象需要被产生出来,取而代之的是,您只需要付出一个constructor(用以产生c)的代价。你无法做的比这更好了,因为c是一个命名对象,而命名对象是不能被消除的。

结论:此特殊的优化行为有一个专属的名词:return value optimization。利用函数的return点消除一个局部临时对象(并可能利用函数调用端的某个对象取代),“拥有专属名称”说明它拥有很广泛。



### 返回值优化 (RVO) 的概念 返回值优化(Return Value Optimization, RVO)是一种由编译器执行的技术,旨在减少或消除函数返回对象时不必要的拷贝操作。当一个函数返回局部创建的对象而不是指针或引用时,通常会产生临时对象并触发复制构造函数。然而,在许多情况下,这种额外的复制是没有必要的。 通过实施 RVO,编译器可以在特定条件下直接将目标位置作为构建最终结果的地方,从而跳过中间步骤中的显式副本动作[^1]。 ### RVO 的工作原理 具体来说,对于那些返回新实例而非现有实体的情况,编译器可能会采取措施让被调用方直接在其外部预期接收该值的位置上建立所需的数据结构。这意味着: - 函数内部不会真正创建独立于外界存在的临时对象; - 调用者指定的目标地址成为实际存储空间所在之处; - 构造过程发生在最终目的地处,避免了后续转移带来的开销。 例如,在下面的例子中 `func()` 方法内声明了一个名为 `temp` 的变量,并将其作为返回值传递给外面的世界;但是由于启用了 RVO 机制,则实际上并没有发生任何真正的“返回”。 ```cpp A func() { A temp(69); return temp; } ``` 这里展示的是如何利用 C++ 中的语法特性来帮助实现更高效的内存管理策略[^5]。 ### 应用场景与限制条件 尽管大多数现代编译器都能够很好地识别何时适合应用此优化方法,但在某些复杂情形下仍可能存在局限性。例如,如果涉及多态行为或是虚继承关系的话,那么即使是最先进的工具也可能无法安全地省略掉所有的潜在复制活动。此外,当面对含有异常处理逻辑或者依赖于副作用表达式的语句块时,同样难以保证完全去除所有形式上的数据传输环节[^4]。 值得注意的是,虽然 NRVORVO 是两种不同类型的优化方式,但它们共同作用于提升整体效率方面有着重要意义,特别是在资源受限环境中尤为明显[^3]。 ### 编译器的支持情况 随着 C++ 标准的发展以及各版本之间的演进,有关 RVO 的支持程度也在不断变化之中。早期的标准如 C++98 并未强制规定此类特性的存在与否及其表现形态;而到了后来诸如 C++17 这样的更新则引入了一些改进措施使得更多场合下的无损转换成为了可能。不过无论如何,始终建议开发者们关注所使用的具体开发环境所提供的文档说明以获取最新信息。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值