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 简单分析下这个输出: 从结果可以看出,整个执行过程中没有输出“operator=”,说明压根没有调用“=”操作符,而且整个过程比我想象的要简洁高效,没有临时对象,没有拷贝构造。 结果为什么会是这样呢?这主要归功于编译器的返回值优化的能力。 有关返回值优化的知识,限于篇幅我就不仔细介绍了,但是需要特别指出的是MSVC9.0只在RELEASE模式下默认开启NRVO,即对具名对象的返回值优化,以及返回值优化里面的一个重要的细节,体现在本例里就是:为什么中整个输出中没有出现"opeartor=",即为什么没调用"="操作符。 现在我们将代码稍微改变一下,改成下面的样子:
construct 10
construct 20 //对应 X a(10),b(20);
operator + //调用“+”操作符
construct 30 //调用X(int){...},44行处
desconstruct 30 //变量c 的析构
desconstruct 20 //变量b 的析构
desconstruct 10 //变量a 的析构
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的内存中,从而优化掉临时对象和对“=”的调用。