在线精品99_中国九九盗摄偷拍偷看_91免费版在线观看_91.app_91高清视频在线_99热最新网站

C++中的RVO是什么

212次阅读
没有评论

共计 4132 个字符,预计需要花费 11 分钟才能阅读完成。

本篇内容主要讲解“C++ 中的 RVO 是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“C++ 中的 RVO 是什么”吧!

前言

考虑存在这样一个类如 HeavyObject,其拷贝赋值操作比较耗时,通常你在使用函数返回这个类的一个对象时会习惯使用哪一种方式?或者会根据具体场景选择某一种方式?

// style 1
HeavyObject func(Args param);
// style 2
bool func(HeavyObject* ptr, Args param);

上面的两种方式都能过到同样的目的,但直观上的使用体验的差别也是非常明显的:

style 1 只需要一行代码,而 style 2 需要两行代码

// style 1
HeavyObject obj = func(params);
// style 2
HeavyObject obj;
func(obj, params);

但是,能达到同样的目的,消耗的成本却未必是一样的,这取决于多个因素,比如编译器支持的特性、C++ 语言标准的规范强制性、多团队多环境开发等等。

看起来 style 2 虽然使用时需要写两行代码,但函数内部的成本却是确定的,只会取决于你当前的编译器,外部即使采用不同的编译器进行函数调用,也并不会有多余的时间开销和稳定性问题。比如 func 内部使用 clang+libc++ 编译,外部调用的编译环境为 gcc+gnustl 或者 vc++,除了函数调用开销,不用担心其它性能开销以及由于编译环境不同会崩溃问题。

因此这里我主要剖析一下 style 1 背后开发者需要关注的点。

RVO

RVO 是 Return Value Optimization 的缩写,即返回值优化,NRVO 就是具名的返回值优化,为 RVO 的一个变种,此特性从 C ++11 开始支持,也就是说 C ++98、C++03 都是没有将此优化特性写到标准中的,不过少量编译器在开发过程中也会支持 RVO 优化(如 IBM Compiler?),比如微软是从 Visual Studio 2010 才开始支持的。

仍然以上述的 HeavyObject 类为例,为了更清晰的了解编译器的行为,这里实现了构造 / 析构及拷贝构造、赋值操作、右值构造函数,如下

class HeavyObject
public:
 HeavyObject() { cout    Constructor\n  }
 ~HeavyObject() { cout    Destructor\n  }
 HeavyObject(HeavyObject const) { cout    Copy Constructor\n  }
 HeavyObject  operator=(HeavyObject const) { cout    Assignment Operator\n  return *this; }
 HeavyObject(HeavyObject) { cout    Move Constructor\n  }
private:
 // many members omitted...
};

编译环境:
AppleClang 10.0.1.10010046

第一种使用方式

HeavyObject func()
 return HeavyObject();
// call
HeavyObject o = func();

按照以往对 C ++ 的理解,HeavyObject 类的构造析构顺序应该为

Constructor

Copy Constructor
Destructor
Destructor

但是实际运行后的输出结果却为

Constructor

Destructor

实际运行中少了一次拷贝构造和析构的开销,编译器帮助我们作了优化。

于是我反汇编了一下:

0000000100000f60  __Z4funcv :
 100000f60: 55 push %rbp
 100000f61: 48 89 e5 mov %rsp,%rbp
 100000f64: 48 83 ec 10 sub $0x10,%rsp
 100000f68: 48 89 f8 mov %rdi,%rax
 100000f6b: 48 89 45 f8 mov %rax,-0x8(%rbp)
 100000f6f: e8 0c 00 00 00 callq 100000f80  __ZN11HeavyObjectC1Ev 
 100000f74: 48 8b 45 f8 mov -0x8(%rbp),%rax
 100000f78: 48 83 c4 10 add $0x10,%rsp
 100000f7c: 5d pop %rbp
 100000f7d: c3 retq 
 100000f7e: 66 90 xchg %ax,%ax

上述汇编代码中的__Z4funcv 即 func() 函数,__ZN11HeavyObjectC1Ev 即 HeavyObject::HeavyObject()。
不同编译器的 C ++ 修饰规则略有不同。

实际上这里就是先创建外部的对象,再将外部对象的地址作为参数传给函数 func,类似 style 2 方式。

第二种使用方式

HeavyObject func()
 HeavyObject o;
 return o;
// call
HeavyObject o = func();

运行上述调用代码的结果为

Constructor

Destructor

与第一种使用方式的结果相同,这里编译器实际做了 NRVO,来看一下反汇编

0000000100000f40  __Z4funcv : // func()
 100000f40: 55 push %rbp
 100000f41: 48 89 e5 mov %rsp,%rbp
 100000f44: 48 83 ec 20 sub $0x20,%rsp
 100000f48: 48 89 f8 mov %rdi,%rax
 100000f4b: c6 45 ff 00 movb $0x0,-0x1(%rbp)
 100000f4f: 48 89 7d f0 mov %rdi,-0x10(%rbp)
 100000f53: 48 89 45 e8 mov %rax,-0x18(%rbp)
 100000f57: e8 24 00 00 00 callq 100000f80  __ZN11HeavyObjectC1Ev  // HeavyObject::HeavyObject()
 100000f5c: c6 45 ff 01 movb $0x1,-0x1(%rbp)
 100000f60: f6 45 ff 01 testb $0x1,-0x1(%rbp)
 100000f64: 0f 85 09 00 00 00 jne 100000f73  __Z4funcv+0x33 
 100000f6a: 48 8b 7d f0 mov -0x10(%rbp),%rdi
 100000f6e: e8 2d 00 00 00 callq 100000fa0  __ZN11HeavyObjectD1Ev  // HeavyObject::~HeavyObject()
 100000f73: 48 8b 45 e8 mov -0x18(%rbp),%rax
 100000f77: 48 83 c4 20 add $0x20,%rsp
 100000f7b: 5d pop %rbp
 100000f7c: c3 retq 
 100000f7d: 0f 1f 00 nopl (%rax)

从上面的汇编代码可以看到返回一个具名的本地对象时,编译器优化操作如第一种使用方式一样直接在外部对象的指针上执行构造函数,只是如果构造失败时还会再调用析构函数。

以上两种使用方式编译器所做的优化非常相近,两种方式的共同点都是返回本地的一个对象,那么当本地存在多个对象且需要根据条件选择返回某个对象时结果会是如何呢?

第三种使用方式

HeavyObject dummy(int index)
 HeavyObject o[2];
 return o[index];
// call
HeavyObject o = dummy(1);

运行后的结果为

Constructor

Constructor
Copy Constructor
Destructor
Destructor
Destructor

从运行的结果可以看到没有做 RVO 优化,此时调用了拷贝构造函数。

从上述三种实现方式可以看到,如果你的函数实现功能比较单一,比如只会对一个对象进行操作并返回时,编译器会进行 RVO 优化;如果函数实现比较复杂,可能会涉及操作多个对象并不确定返回哪个对象时,编译器将不做 RVO 优化,此时函数返回时会调用类的拷贝构造函数。

但是,当只存在一个本地对象时,编译器一定会做 RVO 优化吗?

第四种使用方式

HeavyObject func()
 return std::move(HeavyObject());
// call
HeavyObject o = func();

实际运行输出的结果是

Constructor

Move Constructor
Destructor
Destructor

上述的函数实现直接返回临时对象的右值引用,从实际的运行结果来看调用了 Move 构造函数,与第一种使用方式运行的结果明显不同,并不是我期望的只调用一次构造函数和析构函数,也就是说编译器没有做 RVO。

第五种使用方式

HeavyObject func()
 HeavyObject o;
 return static_cast HeavyObject 
// call
HeavyObject o = func();

实际运行输出的结果是

Constructor

Copy Constructor
Destructor
Destructor

上述的函数实现直接返回本地对象的引用,实际运行结果仍然调用了拷贝构造函数,并不是期望的只调用一次构造和析构函数,也就是说编译器并没有做 RVO。

到此,相信大家对“C++ 中的 RVO 是什么”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-25发表,共计4132字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)
主站蜘蛛池模板: 精品国产免费观看久久久 | 成人人免费夜夜视频观看 | 欧美在线亚洲国产免m观看 欧美在线一级va免费观看 | 国产三级在线看 | 少妇被粗大的猛烈进出69影院一 | 无限资源视频手机在线观看 | 天堂最新版www在线观看 | 最近2019中文字幕大全视频1 | 人妻丰满熟av无码区hd | 日本中文字幕有码在线视频 | 无码视频一区二区三区在线观看 | 91色综合久久 | 少妇精品导航 | 久草热在线观看 | 亚洲av成人午夜福利在线观看 | 国产在线播放网站 | 无码一卡二卡三卡四卡 | 精品一区久久 | 亚洲无限观看 | 久久成人免费视频 | 亚洲暴爽av天天爽日日碰 | 国产精品视频一区二区三区经 | 无码少妇a片一区二区三区 无码少妇精品一区二区免费 | 在线亚洲免费 | 久久久久国产午夜 | 国内精品自产拍在线观看 | 久久久久亚洲av成人人电影 | 日韩亚洲欧美性感视频影片免费看 | 久草这里只有精品 | 亚洲国产成人久久精品图片 | 日本真人边吃奶边做爽电影 | 亚洲小说综合 | 亚洲一区二区在线视频 | 性夜影院爽黄e爽 | 一级特黄aaa大片在线观看 | 日韩视频中文字幕 | 全亚洲最大的免费影院 | 国产精品久久久久免费a∨ 国产精品久久久久免费视频 | 亚洲国产精品综合久久久 | 四虎影院视频在线观看 | 黑色丝袜美女自安慰视频 |