消除臨時對象

消除臨時對象

在咱們的代碼中,有些臨時對象正在使用而咱們並未察覺; 性能優化時,消除臨時對象,特別是大的臨時對象,對提高性能效果明顯; 這裏列出常見的臨時對象產生的地方: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

相關文章
相關標籤/搜索