程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++中按值返回和返回值優化代碼

C++中按值返回和返回值優化代碼

編輯:關於C++

C++和C語言相比,最為人诟病的就是其性能問題,通常一條C語言經編譯器解釋後,可以固定轉換成5—10條匯編語言,但是一條C++語言,就沒有這麼幸運了,可能會是3條匯編語言,也可能是300條。C++影響性能的原因很多,其中一個就是臨時對象的創建和銷毀。這裡我簡述一種減少創建臨時對象的方法--返回值優化問題

很多時候,函數需要按值返回,這其中就會不可避免地涉及到臨時對象的創建和銷毀。假設定義如下的Complex類:

class Complex
{
friend Complex operator +(const Complex&,const Complex&);
public:
Complex(double r=0, double i=0):real(r),imag(i)
{
cout<<"I'm in constructor"<<endl;
}
Complex(const Complex& c):real(c.real),imag(c.imag)
{
cout<<"I'm in copy constructor"<<endl;
}
Complex& operator =(const Complex& c)
{
real=c.real;
imag=c.imag;
cout<<"I'm in assignment"<<endl;
return *this;
}
void print()
{
cout<<real<<"+"<<imag<<"i"<<endl;
}
~Complex()
{
cout<<"I'm in destructor"<<endl;
}
private:
double real;
double imag;
};
Complex operator +(const Complex& a,const Complex& b)
{
/*Complex retVal;
retVal.real=a.real+b.real;
retVal.imag=a.imag+b.imag;
return retVal;*/
cout<<"calling plus"<<endl;
// return Complex(a.real+b.real,a.imag+b.imag);
Complex retVal(a.real+b.real,a.imag+b.imag);
return retVal;
}

其中的友元函數operator + 是一個按值返回的函數。編譯器會將這個函數解釋成如下:

void Complex_Add(const Complex& __result,
const Complex& c1,
const Complex& c2)
{
......
}

定義一下語句:Complex a(1,1),b(2,2),c;

c=a+b;

a和b相加的結果賦值給對象c的過程,可以被轉化為:

Complex __tempResult;
Complex_Add(__tempResult, a,b);
c=__tempResult;

可以看出,在上述的一個簡單的操作中,編譯器會隱蔽地產生一個臨時對象__tempResult。這是產生的第一個臨時對象,先記下,但是這可能並不是唯一的一個。比如當operator +如下實現時

Complex operator +(const Complex& a,const Complex& b)
{
Complex retVal;
retVal.real=a.real+b.real;
retVal.imag=a.imag+b.imag;
return retVal;
}

在operator +函數內部又會產生一個臨時對象retVal,綜合一下,編譯器在遇到如上的函數定義及調用時會產生如下解釋:

void Complex_Add(const Complex& __tempResult,
const Complex& c1,[Page]
const Complex& c2)
{
Complex retVal;
retVal.Complex::Complex();
retVal.real=a.real+b.real;
retVal.imag=a.imag+b.imag;
__tempResult.Complex::Complex(retVal);
retVal.Complex::~Complex();
return;
}

所以

Complex a(1,1),b(2,2),c;
c=a+b;的運行結果是:
I'm in constructor
I'm in constructor
I'm in constructor
I'm in constructor
I'm in copy constructor
I'm in destructor
I'm in assignment
I'm in destructor
I'm in destructor
I'm in destructor
I'm in destructor

下面對程序進行優化,可以通過消除上述過程中產生的兩個臨時對象來減少對象的創建和析構

首先可以對operator +函數內部的retVal臨時對象進行優化,使得直接用__tempResult取代retVal

void Complex_Add(const Complex& __tempResult,
const Complex& c1,
const Complex& c2)
{
__tempResult.Complex::Complex();
__tempResult.real=a.real+b.real;
__tempResult.imag=a.imag+b.imag;
return;
}

以上是Efficient C++中的解釋,但是我認為__tempResult.Complex::Complex()不應該在函數內部調用,不過並不影響調用次數。

以上便是RVO優化,很多編譯器都是支持的,為了防止某些編譯器不支持,可以顯式地將operator +函數如下實現:

Complex operator +(const Complex& a,const Complex& b)
{
return Complex(a.real+b.real,a.imag+b.imag);
}

以上這種形式稱為未命名變量,有些編譯器拒絕對已命名變量的RVO優化(比如前面的retVal),這樣我們就消除了局部的retVal臨時變量。

下面再談談__tempResult臨時變量的產生,以及消除的方法:

Complex a(1,1),b(2,2),c;

c=a+b;

定義c的時候會調用默認的構造函數進行初始化,因此第一條語句執行完之後,c已經是一個存在的對象,所以第二條語句並沒有權利去直接修改c的內容,必須要通過調用賦值操作符(=),因此必須要產生一個臨時對象,具體解釋語句前面已經介紹過,即__tempResult的產生原因。

但是如果在執行第二條語句的時候c沒有舊的內容,即c不是一個已經存在的對象,那麼就可以直接調用構造函數,而不需要=操作符,這樣也就可以避免了__tempResult 的產生。

所以如下的操作

Complex a(1,1),b(2,2);

Complex c=a+b;

其運行結果為

I'm in constructor
I'm in constructor
I'm in constructor
I'm in destructor
I'm in destructor
I'm in destructor

只調用了三次構造函數和析構函數,和沒有優化前相比,性能得到了很大的提升。

來源:http://blog.csdn.net/SeeSeaBee/archive/2007/06/27/1668825.aspx

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved