题目二:
题目我作了下改变,使用了上篇文章(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的内存中,从而优化掉临时对象和对“=”的调用。