More Effective C++ 條款20 (轉)
條款20:協助完成返回值:namespace prefix = o ns = "urn:schemas--com::office" />
一個返回的很難有較高的,因為傳值返回會導致物件內的構造和解構函式(參見條款19),這種呼叫是不能避免的。問題很簡單:一個函式要麼為了保證正確的行為而返回物件要麼就不這麼做。如果它返回了物件,就沒有辦法擺脫被返回的物件。就說到這。
考慮rational(有理數)類的成員函式operator*:
class Rational {
public:
Rational(int numerator = 0, int denominator = 1);
...
int numerator() const;
int denominator() const;
};
// 有關為什麼返回值是const的解釋,參見條款6,
const Rational operator*(const Rational& lhs,
const Rational& rhs);
甚至不用看operator*的程式碼,我們就知道它肯定要返回一個物件,因為它返回的是兩個任意數字的計算結果。這些結果是任意的數字。operator*如何能避免建立新物件來容納它們的計算結果呢?這是不可能的,所以它必須得建立新物件並返回它。不過C++員仍然花費大量的精力尋找傳說中的方法,能夠去除傳值返回的物件(參見Effective C++ 條款23和條款31)。
有時人們會返回指標,從而導致這種滑稽的句法:
// 一種不合理的避免返回物件的方法
const Rational * operator*(const Rational& lhs,
const Rational& rhs);
Rational a = 10;
Rational b(1, 2);
Rational c = *(a * b); //你覺得這樣很“正常”麼?
它也引發出一個問題。呼叫者應該刪除函式返回物件的指標麼?答案通常是肯定的,並且通常會導致資源洩漏。
其它一些開發人員會返回引用。這種方法能產生可接受的句法,
//一種危險的(和不正確的)方法,用來避免返回物件
const Rational& operator*(const Rational& lhs,
const Rational& rhs);
Rational a = 10;
Rational b(1, 2);
Rational c = a * b; // 看上去很合理
但是函式不能被正確地實現。一種嘗試的方法是這樣的:
// 另一種危險的方法 (和不正確的)方法,用來
// 避免返回物件
const Rational& operator*(const Rational& lhs,
const Rational& rhs)
{
Rational result(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
return result;
}
這個函式返回的引用,其指向的物件已經存在了。它返回的是一個指向區域性物件result的引用,當operator* 退出時result被自動釋放。返回指向已被釋放的物件的引用,這樣的引用絕對不能使用。
相信我:一些函式(operator*也在其中)必須要返回物件。這就是它們的執行方法。不要與其對抗,你不會贏的。
你消除傳值返回的物件的努力不會獲得勝利。這是一場錯誤的戰爭。從效率的觀點來看,你不應該關心函式返回的物件,你僅僅應該關心物件的開銷。你所應該關心的是把你的努力引導到尋找減少返回物件的開銷上來,而不是去消除物件本身(我們現在認識到這種尋求是無用的)。如果沒有與這些物件相關的開銷,誰還會關心有多少物件被建立呢?
以某種方法返回物件,能讓消除臨時物件的開銷,這樣編寫函式通常是很普遍的。這種技巧是返回constructor argument而不是直接返回物件,你可以這樣做:
// 一種高效和正確的方法,用來實現
// 返回物件的函式
const Rational operator*(const Rational& lhs,
const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
仔細觀察被返回的。它看上去好象正在呼叫Rational的建構函式,實際上確是這樣。你透過這個表示式建立一個臨時的Rational物件,
Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
並且這是一個臨時物件,函式把它複製給函式的返回值。
返回constructor argument而不出現區域性物件,這種方法還會給你帶來很多開銷,因為你仍舊必須為在函式內臨時物件的構造和釋放而付出代價,你仍舊必須為函式返回物件的構造和釋放而付出代價。但是你已經獲得了好處。C++規則允許編譯器最佳化不出現的臨時物件(temporary s out of existence)。因此如果你在如下的環境裡呼叫operator*:
Rational a = 10;
Rational b(1, 2);
Rational c = a * b; // 在這裡呼叫operator*
編譯器就會被允許消除在operator*內的臨時變數和operator*返回的臨時變數。它們能在為目標c分配的裡構造return表示式定義的物件。如果你的編譯器這樣去做,呼叫operator*的臨時物件的開銷就是零:沒有建立臨時物件。你的代價就是呼叫一個建構函式――建立c時呼叫的建構函式。而且你不能比這做得更好了,因為c是命名物件,命名物件不能被消除(參見條款22)。不過你還可以透過把函式宣告為inline來消除operator*的呼叫開銷(不過首先參見Effective C++ 條款33):
// the most efficient way to write a function returning
// an object
inline const Rational operator*(const Rational& lhs,
const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
“好,不錯”,你嘀咕地說,“最佳化,誰關心編譯器能做什麼?我想知道它們確實做了什麼,Does any of this nonsense work with real compilers?” It does。這種特殊的最佳化――透過使用函式的return location(或者用一個在函式呼叫位置的物件來替代),來消除區域性臨時物件――是眾所周知的和被普遍實現的。它甚至還有一個名字:返回值最佳化(return value optimization)。實際上這種最佳化有自己的名字本身就可以解釋為什麼它被廣泛地使用。尋找C++編譯器的程式設計師會問銷售商編譯器是否有返回值最佳化功能。如果一個銷售商說有而另一個問“那是什麼東西?”,第一個銷售商就會有明顯的競爭優勢。啊,資本主義,有時你實在應該去愛它。(謹代表作者觀點,譯者堅決擁護四項基本原則 譯者注 :-) )
附錄:
文中最後一段黑體部分如何翻譯,我有些拿不準,請高手告知,為了容易理解,我在此附上此文最後一段的英文原文:
"Yeah, yeah," you mutter, "optimization, schmoptimization. Who cares what compilers can do? I want to know what they do do. Does any of this nonsense work with real compilers?" It does. This particular optimization — eliminating a local temporary by using a function's return location (and possibly replacing that with an object at the function's call site) — is both well-known and commonly implemented. It even has a name: the return value optimization. In fact, the existence of a name for this optimization may explain why it's so wly available. Programmers looking for a C++ compiler can ask vendors whether the return value optimization is implemented. If one vendor says yes and another says "The what?," the first vendor has a notable competitive advantage. Ah, ctalism. Sometimes you just gotta love it.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990738/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Effective C++ 條款08_不止於此C++
- Effective Modern C++ 系列之 條款2: autoC++
- Effective c++條款11:在operator=中處理“自我賦值”C++賦值
- [讀書筆記][effective C++]條款30-inline的原理筆記C++inline
- 文獻學習——Making Deduction More Effective in SAT Solvers
- 學懂現代C++——《Effective Modern C++》之轉向現代C++C++
- effective C++ : CHAPTER 8C++APT
- 【Effective Modern C++】索引C++索引
- Effective C++筆記C++筆記
- effective C++筆記1C++筆記
- 《Effective C++》讀書筆記C++筆記
- Effective C++ 筆記(3)資源管理C++筆記
- Effective C++ 4.設計與宣告C++
- 股市鬼才操盤20年總結的20條投資心得(轉)
- 條款01: 視C++為一個語言聯邦C++
- 《Effective C++》閱讀總結(三):資源管理C++
- 《Effective C++》第三版-1. 讓自己習慣C++(Accustoming Yourself to C++)C++
- 學懂現代C++——《Effective Modern C++》之型別推導和autoC++型別
- 條款05: 瞭解c++默默編寫並呼叫哪些函式C++函式
- 《Effective C++》第三版-5. 實現(Implementations)C++
- 讀完Java名著《Effective Java》: 我整理了這50條技巧Java
- 《Effective C++》閱讀總結(四): 設計、宣告與實現C++
- 《Effective C++》第三版-3. 資源管理(Resource Management)C++
- c++切面條題目C++
- 《Effective C++》第三版-4. 設計與宣告(Design and Declarations)C++
- 《Effective Python 第二版》第二條 遵循PEP8風格指南Python
- 《Effective C++》閱讀總結(二):類的構造、析構和賦值C++賦值
- 《看雪服務條款》
- 20條IPTables防火牆規則用法!防火牆
- 【C++】C++之型別轉換C++型別
- C++中的條件變數C++變數
- MORE_DETAIL_TECHAI
- TypeScript 之 More on FunctionsTypeScriptFunction
- More web function requests go online concurrently, and web service deployment is faster and more economical!WebFunctionGoAST
- C++整理20_C++11新特性C++
- 學習進度條2024-03-20
- 20款前端特效及部分原始碼前端特效原始碼
- effective java 觀後感Java
- DNS: More than just namesDNS