您的位置:首页 > 编程语言 > C语言/C++

通过一道面试题来看 C++的RVO 优化

2013-02-18 00:00 190 查看
题目二:

题目我做了下改变,使用了上篇文章(http://my.oschina.net/u/90679/blog/109042)中提到的那个类X,代码如下:

1 class X

2 {

3 public:

4 X(){cout<<"default construct"<<endl;}

5 X(int a):i(a){ cout<<"construct "<<i<<endl;}

6 ~X(){ cout<<"desconstruct "<<i<<endl;}

7 X(const X& x):i(x.i)

8 {

9 cout<<"copy construct "<<i<<endl;

10 }

11 X& operator++()

12 {

13 cout<<"operator ++(pre) "<<i<<endl;

14 ++i;

15 return *this;

16 }

17 const X operator++(int)

18 {

19 cout<<"operator ++(post) "<<i<<endl;

20 X x(*this);

21 ++i;

22 return x;

23 }

24 X& operator=(int m)

25 {

26 cout<<"operator =(int)"<<endl;

27 i = m;

28 return *this;

29 }

30 X& operator=(const X& x)

31 {

32 cout<<"operator =(X)"<<endl;

33 i=x.i;

34 return *this;

35 }

36 /////////////////////////

37 friend ostream& operator<<(ostream& os,const X& x)

38 {

39 os<<x.i;

40 return os;

41 }

42 friend X operator+(const X& a,const X& b)

43 {

44 cout<<"operator +"<<endl;

45 return X(a.i+b.i);

46 }

47 //////////////////////////

48 public:

49 int i;

50 };

请问以下代码的输出是什么?

1
X a(
10
),b(
20
);

2
X c
=
a
+
b;

我们来看一下使用GCC4.5(默认编译选项)以及MSVC9.0(BOTH DEBUG AND RELEASE)编译后的实际运行结果:

construct 10

construct 20

operator +

construct 30

desconstruct 30

desconstruct 20

desconstruct 10

简单分析下这个输出:

construct 10

construct 20 //对应 X a(10),b(20);

operator + //调用“+”操作符

construct 30 //调用X(int){...},44行处

desconstruct 30 //变量c 的析构

desconstruct 20 //变量b 的析构

desconstruct 10 //变量a 的析构

从结果可以看出,整个执行过程中没有输出“operator=”,说明压根没有调用“=”操作符,而且整个过程比我想象的要简洁高效,没有临时对象,没有拷贝构造。

结果为什么会是这样呢?这主要归功于编译器的返回值优化的能力。

有关返回值优化的知识,限于篇幅我就不仔细介绍了,但是需要特别指出的是MSVC9.0只在RELEASE模式下默认开启NRVO,即对具名对象的返回值优化,以及返回值优化里面的一个重要的细节,体现在本例里就是:为什么中整个输出中没有出现"opeartor=",即为什么没调用"="操作符。

现在我们将代码稍微改变一下,改成下面的样子:

X a(
10
),b(
20
),c;
c
=
a
+
b; //这里我们将c的构造和赋值分开了

执行的结果如下:

construct 10 //构造a

construct 20 //构造b

default construct //构造 c

operator + //调用“+”操作符

construct 30 //调用X(int){...},44行处

operator =(X) //调用“=”操作符

desconstruct 30 //代码45行所建立的临时对象的析构

desconstruct 30 //变量c的析构

desconstruct 20 //变量b的析构

desconstruct 10 //变量c的析构

对比前后的输出结果,可以发现多出以下三行

default construct

operator =(X)

desconstruct 30

出现这种差异的原因在于:

定义c的时候会调用默认的构造函数进行初始化,因此第一条语句执行完之后,c已经是一个存在的对象,所以第二条语句并没有权利去直接修改c的内容,必须要通过调用赋值操作符”=“,因此必须要产生一个临时对象。而在第一个例子中,因为执行到第二条语句之前c并没有被创建,所以编译器可以将 表达式a+b的返回值直接构建在c的内存中,从而优化掉临时对象和对“=”的调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息