在咱們的代碼中,有些臨時對象正在使用而咱們並未察覺; 性能優化時,消除臨時對象,特別是大的臨時對象,對提高性能效果明顯; 這裏列出常見的臨時對象產生的地方:ios
按值返回函數結果,結果就是一個臨時對象git
string add(string s1,string s2) { string s3; s3 = s1+s2; return s3; }
解決方案: 在大多數場景下,這個臨時對象能夠經過按引用返回來消除;github
void add(string s1,string s2,string& retvalue ) { retvalue = s1+s2; }
幸運的是,編譯器一般會對按值返回作優化,將其改寫爲按引用返回; 但編譯器作的也是很是保守的工做,僅對匿名返回臨時對象作這種按引用傳遞; 以上函數就不會作,而如下函數,編譯器會自動優化爲按引用傳遞:性能優化
string add(string s1,string s2) { return s1+s2; }
注:返回值優化(RVO: return value Optimition):由編譯器來完成將值返回轉換爲引用返回;函數
按值傳遞參數,會有臨時對象的分配性能
string add(string s1,string s2) { string s3; s3 = s1+s2; return s3; }
解決方案: 改成按引用傳遞(若是不但願函數內部修改,加上const修飾符)優化
string add(const string& s1, const string& s2) { string s3; s3 = s1+s2; return s3; }
賦值操做兩邊不是同一類型時,若是右邊能夠做爲做爲的構造函數的參數作隱式轉換,那麼就會有臨時對象的產生; 好比:this
class Counter { public: Counter(int i):m_nCount(i){ } void setx(){ m_x = 8; } private: int m_nCount; }; Counter c(5); c = 6;
首先會產生一個臨時對象Counter :Counter (6); 而後賦值給s1: s1 = Counter (6);spa
解決方案: 儘可能使用相同類型,不用編譯器來自動作隱式轉換: 好比: 初始化:code
Counter s1(5);
賦值:
Counter s1; s1.setx(6); //若是重載了operator=函數以後,就能夠直接使用s1 = 6
注:最開始這裏使用string做爲示例,但string重載了operator=(char*),所以如下調用不會產生臨時對象,感謝@飛龍 指正;
string s1; s1 = "A";
例:
string s3; s3 = s1+ s2;
s1+s2的中間結果須要存到一個臨時對象中,而後再賦值給s3;
解決方案: 採用+=操做符,一個個的加上須要的對象:
s3 = s1; s3+= s2;
固然,第一種寫法更爲優雅,第二種則性能高效; 當此處不是優化關鍵 路徑上的時候,咱們仍是採用第一種寫法就好;
class MyClass { MyClass(){ m_a = "A";} private: string m_a; }
成員對象放在構造函數中初始化,那麼必然產生一箇中間的臨時對象;
解決方案: 採用成員初始化列表:
class MyClass { MyClass():m_a("A"){} private: string m_a; }
若是你以爲本文對你有所幫助,請點擊如下【推薦】按鈕, 讓更多人閱讀;
最直觀的方法是編譯生產彙編文件,查看彙編代碼來查看;但這對彙編語言能力有要求;另外,咱們能夠經過直接打印出內存地址來查看(若是是在堆中從新申請的臨時對象,內存地址和以前的通常都不同,而若是是在棧中申請的臨時對象,可能重用而保持一致,這時能夠在類中加入參考變量,經過打印出變量的值來檢查是否生成了新的對象。)
示例:
#include <iostream> using namespace std; class Counter { public: Counter(int i):m_nCount(i){ } void setx(){ m_x = 8; } void print(){ printf("m_nCount:%d,m_x:%d,addr:%x \n",m_nCount,m_x,this); } private: int m_nCount; int m_x; }; int main() { Counter c(5); c.setx(); c.print(); c = 6; c.print(); string s1("AAAAAA"); printf("%s,addr:%x \n",s1.data(),s1.data()); s1.assign("bbbbbb"); // 使用賦值,不會新申請空間 printf("%s,addr:%x \n",s1.data(),s1.data()); s1 = "cccccc";// 使用操做符重載函數operator=,不會新申請空間 printf("%s,addr:%x \n",s1.data(),s1.data()); s1 = string("dddddd");//用臨時對象直接替換,新空間更改了 printf("%s,addr:%x \n",s1.data(),s1.data()); return 0; }
運行結果:
m_nCount:5,m_x:8,addr:22ff28 m_nCount:6,m_x:-1971344528,addr:22ff28 AAAAAA,addr:8117e4 bbbbbb,addr:8117e4 cccccc,addr:8117e4 dddddd,addr:811804
Posted by: 大CC | 06AUG,2015 博客:blog.me115.com [訂閱] Github:大CC