理解臨時對象的來源

來源:互聯網
上載者:User

 當程式員之間進行交談時,他們經常把僅僅需要一小段時間的變數稱為臨時變數。例如在下面這段swap(交換)常式裡:

template<class T>

void swap(T& object1, T& object2)

{

  T temp = object1;

  object1 = object2;

  object2 = temp;

}

    通常把temp叫做臨時變數。不過就C++而言,temp根本不是臨時變數,它只是一個函數的局部對象。

    在C++中真正的臨時對象是看不見的,它們不出現在你的原始碼中。建立一個沒有命名的非堆(non-heap)對象會產生臨時對象。這種未命名的對象通常在兩種條件下產生:為了使函數成功調用而進行隱式類型轉換和函數返回對象時。理解如何和為什麼建立這些臨時對象是很重要的,因為構造和釋放它們的開銷對於程式的效能來說有著不可忽視的影響。

    首先考慮為使函數成功調用而建立臨時對象這種情況。當傳送給函數的物件類型與參數類型不符時會產生這種情況。例如一個函數,它用來計算一個字元在字串中出現的次數:

// 返回ch在str中出現的次數

size_t countChar(const string& str, char ch);

char buffer[MAX_STRING_LEN];

char c;

// 讀入到一個字元和字串中,用setw

// 避免緩衝溢出,當讀取一個字串時

cin >> c >> setw(MAX_STRING_LEN) >> buffer;

cout << "There are " << countChar(buffer, c)

     << " occurrences of the character " << c

     << " in " << buffer << endl;

    看一下countChar的調用。第一個被傳送的參數是字元數組,但是對應函數的正被綁定的參數的類型是const string&。僅當消除類型不符後,才能成功進行這個調用,你的編譯器很樂意替你消除它,方法是建立一個string類型的臨時對象。通過以buffer做為參數調用string的建構函式來初始化這個臨時對象。countChar的參數str被綁定在這個臨時的string對象上。當countChar返回時,臨時對象自動釋放。

    這樣的類型轉換很方便(儘管很危險-參見條款M5),但是從效率的觀點來看,臨時string對象的構造和釋放是不必要的開銷。通常有兩個方法可以消除它。一種是重新設計你的代碼,不讓發生這種類型轉換。這種方法在條款M5中被研究和分析。另一種方法是通過修改軟體而不再需要類型轉換,條款M21講述了如何去做。

    僅當通過傳值(by value)方式傳遞對象或傳遞常量引用(reference-to-const)參數時,才會發生這些類型轉換。當傳遞一個非常量引用(reference-to-non-const)參數對象,就不會發生。考慮一下這個函數:

void uppercasify(string& str);               // 把str中所有的字元

                                             // 改變成大寫

    在字元計數的例子裡,能夠成功傳遞char數組到countChar中,但是在這裡試圖用char數組調用upeercasify函數,則不會成功:

char subtleBookPlug[] = "Effective C++";

uppercasify(subtleBookPlug);                // 錯誤!

    沒有為使調用成功而建立臨時對象,為什麼呢?

    假設建立一個臨時對象,那麼臨時對象將被傳遞到upeercasify中,其會修改這個臨時對象,把它的字元改成大寫。但是對subtleBookPlug函數調用的真正參數沒有任何影響;僅僅改變了臨時從subtleBookPlug產生的string對象。無疑這不是程式員所希望的。程式員傳遞subtleBookPlug參數到uppercasify函數中,期望修改subtleBookPlug的值。當程式員期望修改非臨時對象時,對非常量引用(references-to-non-const)進行的隱式類型轉換卻修改臨時對象。這就是為什麼C++語言禁止為非常量引用(reference-to-non-const)產生臨時對象。這樣非常量引用(reference-to-non-const)參數就不會遇到這種問題。

    建立臨時對象的第二種環境是函數返回對象時。例如operator+必須返回一個對象,以表示它的兩個運算元的和(參見Effective C++ 條款23)。例如給定一個類型Number,這種類型的operator+被這樣聲明:

const Number operator+(const Number& lhs,

                       const Number& rhs);

    這個函數的傳回值是臨時的,因為它沒有被命名;它只是函數的傳回值。你必須為每次調用operator+構造和釋放這個對象而付出代價。(有關為什麼傳回值是const的詳細解釋,參見Effective C++條款21)

    通常你不想付出這樣的開銷。對於這種函數,你可以切換到operator=,而避免開銷。條款M22告訴我們進行這種轉換的方法。不過對於大多數返回對象的函數來說,無法切換到不同的函數,從而沒有辦法避免構造和釋放傳回值。至少在概念上沒有辦法避免它。然而概念和現實之間又一個黑暗地帶,叫做最佳化,有時你能以某種方法編寫返回對象的函數,以允許你的編譯器最佳化臨時對象。這些最佳化中,最常見和最有效是傳回值最佳化,這是條款M20的內容。

    綜上所述,臨時對象是有開銷的,所以你應該儘可能地去除它們,然而更重要的是訓練自己尋找可能建立臨時對象的地方。在任何時候只要見到常量引用(reference-to-const)參數,就存在建立臨時對象而綁定在參數上的可能性。在任何時候只要見到函數返回對象,就會有一個臨時對象被建立(以後被釋放)。學會尋找這些物件建構,你就能顯著地增強透過編譯器表面動作而看到其背後開銷的能力。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.