返回值优化是什么

2023-08-01 10:34:00 生活常识 投稿:玖玖言

返回值优化(RVO)是C++的一项编译优化技术,即删除保持函数返回值的临时对象。可能会省略两次复制构造函数,即使复制构造函数有副作用。C++标准允许省略这些复制构造函数,即使这导致程序的不同行为,即使编译器把两个对象视作同一个具有副作用。

返回值优化(Return value optimization,缩写为 RVO)是 C++的一项编译优化技术,即删除保持函数返回值的临时对象。这可能会省略两次复制构造函数,即使复制构造函数有副作用。典型地,当一个函数返回一个对象实例,一个临时对象将被创建并通过复制构造函数把目标对象复制给这个临时对象。C++标准允许省略这些复制构造函数,即使这导致程序的不同行为,即使编译器把两个对象视作同一个具有副作用。

返回值优化是什么

简介

返回值优化,是一种属于编译器的技术,它通过转换源代码和对象的创建来加快源代码的执行速度。当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy Constructor)以及一个析构函数(Destructor)的调用的代价。而如果稍微做一点优化,就可以将成本降低到一个构造函数的代价,这样就省去了一次拷贝构造函数的调用和依次析构函数的调用。

构造函数与析构函数

构造函数,是一种特殊的方法。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与 new 运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的不同来区分它们即构造函数的重载。多数编程语言允许构造函数重载,一个类被允许拥有多个接受不同参数种类的构造函数同时存在。一些编程语言允许某些特殊种类的构造函数。使用单个类来具体地建立和返回新实例的构造函数,时常被抽象为工厂方法 – 一种同样用来建立新对象,但会同时使用多个类,或者一些诸如对象池的分配方案来完成这一过程的子程序。构造函数类型有参数化构造函数、默认构造函数。

接收参数的构造函数被称为参数化构造函数。参数的数量可以大于或等于一。如果在编写一个可实例化的类时没有专门编写构造函数,多数编程语言会自动生成缺省构造函数。

缺省构造函数的特性依不同语言而定。某些情况下它会将所有的实例变量同时初始化到 0,或者任何其他别的值;某些缺省构造函数什么也不会做。某些语言 (Java, C#, VB .NET) 会缺省构造由该类类型定义的数组,使其充满空值引用。没有空值引用的语言一般会禁止缺省构造包含不可缺省构造对象的数组,或者要求在建立时专门初始化这些数值。

析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用 new 开辟了一片内存空间,delete 会自动调用析构函数后释放内存)。对于构造函数应注意:不能在结构中定义析构函数、只能对类使用析构函数、一个类只能有一个析构函数、无法继承或重载析构函数、无法调用析构函数,它们是被自动调用的,析构函数既没有修饰符,也没有参数。

以 C++语言为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括 void 类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

优化示例

对于函数返回类对象,一种实现办法是在函数调用语句前在 stack frame 上声明一个隐藏对象,把该对象的地址隐蔽传入被调用函数,函数的返回对象直接构造或者复制构造到该地址上。

可能产生的代码如下:

这引起了 Data 对象被复制两次。

另一种技术是命名返回值优化(Named return value optimization,NRVO)。NRVO 去除了基于栈的返回值的构造与析构。虽然这会导致优化与未优化的程序的不同行为。

大部分 C++编译器均支持返回值优化。在某些环境下,编译器不能执行此优化。一个常见情形是当函数依据执行路径返回不同的命名对象,或者命名对象在 asm 内联块中被使用:

编译技术

计算机语言之所以能由单一的机器语言发展到现今的数千种高级语言,就是因为有了编译技术。编译技术是计算机语言发展的支柱,也是计算机科学中发展最迅速、最成熟的一个分支,它集中体现了计算机发展的成果与精华。 编译技术的核心思想就是把同样的逻辑结构和思想从一种语言表示转化为另外一种语言表示。从高级语言,甚至是运行于虚拟平台的高级语言,到机器语言,最终到硬件执行的物理信号,这一层层转化,无一不涉及到“编译”这个概念的应用。编译程序是一个足够复杂的程序,语言功能的完善,硬件结构的发展,环境的友好要求,都对编译程序提出了更高的要求。因此一个编译系统的构造并非易事,对完全想用手工方法来构造编译器来说更是如此。要构造一个完全独立的全新的编译器可能性很小,大部分可在现有编译器的基础上扩展,有的采用自展的方式,有的则用自编译的方式。编译优化常用方法有:常量传播,在编译优化时,能够将计算出结果的变量直接替换为常量。常量折叠,在编译优化时,多个变量进行计算时,而且能够直接计算出结果,那么变量将有常量直接替换。复写传播,两个相同的变量可以用一个代替。公共子表式消除, 如果一个表达式 E 已经计算过了,并且从先前的计算到 E 中的变量都没有发生变化,那么 E 的此次出现就成为了公共子表达式。无用代码消除,永远不能被执行到的代码或者没有任何意义的代码会被清除掉。

数组范围检查消除,数组边界检查不是必须在运行期间一次不漏的检查,而是可以协商的。如果及时编译器能根据数据流分析出变量的取值范围在[0,max_length]之间,那么在循环期间就可以把数组的上下边界检查消除。方法内联 ,编译器最终要的优化手段,可减少方法调用的成本,并未其他优化做基础。

逃逸分析,分析对象动态作用域,一旦确定对象不会发生方法逃逸和线程逃逸,就可以对这个变量进行高效的优化,比如栈上分配、同步消除、标量替换等。

标签: # 返回值
声明:犀牛文库所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请联系admin@qq.com