C/C++培训
达内IT学院
400-996-5531
在这篇文章里用到了以下编译器和操作系统,大家请自行安装
首先我们先来看一道题,下面的代码运行之后会输出什么结果?
我想大多数人会选A,对吗?因为foo函数在返回C类的对象时会调用拷贝构造函数来创建一个临时对象。
现在让我们编译并运行这个程序,看看输出结果是否如我们所料
$ clang++ -std=c++11 foo.cpp
$ ./a.out
Constructor
Destructor
然而,遗憾的是,事实与课本里说的并不一样,那么,为什么会这样呢?从实际编译运行的输出来看,C类的拷贝构造函数并没有被调用。
这是因为在实际工程中大多数时候C++构造对象的开销巨大,编译器为了生成高效的代码,在foo函数返回时并没有调用拷贝构造函数去生成一个临时对象,而是直接使用在foo函数内初始化的对象c1作为返回值传出去。这就是C++中的返回值优化(Return Value Optimization, RVO)。
由于较新版本的gcc/clang均已默认开启了返回值优化(即使没有加上优化选项),我们想看到书上所说的现象只能在编译的时候加上特殊选项,让编译器不要做返回值优化,操作步骤如下:
$ clang++ -std=c++11 -fno-elide-constructors foo.cpp
$ ./a.out
Constructor
Copy Constructor
Destructor
Destructor
返回值优化能在一定程度上提高程序的运行效率,但是我们在实际工程中最好不要依赖这个编译器优化特性,因为它要求的条件非常苛刻(或者说,编译器还不够聪明:P)。让我们在foo函数中加上一个无用的条件判断语句,试试编译器是否能继续应用返回值优化。
可以发现,在上面的代码中,如果编译器足够聪明,它应该能推断出foo函数只会返回c1,所以可以对foo函数应用返回值优化,那么让我们来实际编译运行一下,看看执行情况。
$ clang++ -std=c++11 -O3 foo.cpp
$ ./a.out
Constructor
Constructor
Copy Constructor
Destructor
Destructor
Destructor
$ g++ -std=c++11 -O3 foo.cpp
$ ./a.out
Constructor
Constructor
Copy Constructor
Destructor
Destructor
Destructor
可以看到,clang 5.0.0和gcc 6.3.0即使把优化选项开到最大也不能对foo函数进行返回值优化。
返回值优化还带来了一个问题,那就是拷贝构造函数和析构函数的调用变得不可预测。如果在拷贝构造函数和析构函数中存在有副作用(side-effect)的语句,就会造成不同的编译器配置编译出来的程序运行结果不一致。事实上,这是被C++11标准所允许的,下面这段话引用自C++11标准Sec 12.8 Copying and moving class objects [class.copy]
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects.
是否调用拷贝构造函数和析构函数的决定交给了编译器实现去判断,所以在实际工程中为了写出可移植的代码,就需要避免在构造函数和析构函数中加入有副作用的语句,并且应该尽量把复杂的逻辑剥离出来,放在类的其它成员函数中实现。
原文链接:#/s/BynCMibnW
填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved