題目二:
題目我作了下改變,使用了上篇文章(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的內存中,從而優化掉臨時對象和對「=」的調用。