廣州做企業(yè)網(wǎng)站域名注冊(cè)哪個(gè)平臺(tái)比較好
?? 一個(gè)返回對(duì)象的函數(shù)很難有較高的效率,因?yàn)閭髦捣祷貢?huì)導(dǎo)致調(diào)用對(duì)象內(nèi)的構(gòu)造和析構(gòu)函數(shù)(參見(jiàn)條款M19),這種調(diào)用是不能避免的。問(wèn)題很簡(jiǎn)單:一個(gè)函數(shù)要么為了保證正確的行為而返回對(duì)象要么就不這么做。如果它返回了對(duì)象,就沒(méi)有辦法擺脫被返回的對(duì)象。就說(shuō)到這。
考慮rational(有理數(shù))類的成員函數(shù)operator*:
class Rational {
public:
? Rational(int numerator = 0, int denominator = 1);
? ...
? int numerator() const;
? int denominator() const;
};
// 有關(guān)為什么返回值是const的解釋,參見(jiàn)條款M6,
const Rational operator*(const Rational& lhs,
???????????????????????? const Rational& rhs);
?? 甚至不用看operator*的代碼,我們就知道它肯定要返回一個(gè)對(duì)象,因?yàn)樗祷氐氖莾蓚€(gè)任意數(shù)字的計(jì)算結(jié)果。這些結(jié)果是任意的數(shù)字。operator*如何能避免建立新對(duì)象來(lái)容納它們的計(jì)算結(jié)果呢?這是不可能的,所以它必須得建立新對(duì)象并返回它。不過(guò)C++程序員仍然花費(fèi)大量的精力尋找傳說(shuō)中的方法,能夠去除傳值返回的對(duì)象。
?? 有時(shí)人們會(huì)返回指針,從而導(dǎo)致這種滑稽的句法:
// 一種不合理的避免返回對(duì)象的方法
const Rational * operator*(const Rational& lhs,
?????????????????????????? const Rational& rhs);
Rational a = 10;
Rational b(1, 2);
Rational c = *(a * b);???????????? //你覺(jué)得這樣很“正常”么?
?? 它也引發(fā)出一個(gè)問(wèn)題。調(diào)用者應(yīng)該刪除函數(shù)返回對(duì)象的指針么?答案通常是肯定的,并且通常會(huì)導(dǎo)致資源泄漏。
?? 其它一些開(kāi)發(fā)人員會(huì)返回引用。這種方法能產(chǎn)生可接受的句法,
//一種危險(xiǎn)的(和不正確的)方法,用來(lái)避免返回對(duì)象
const Rational& operator*(const Rational& lhs,
????????????????????????? const Rational& rhs);
Rational a = 10;
Rational b(1, 2);
Rational c = a * b;????????????????????????? // 看上去很合理
?? 但是函數(shù)不能被正確地實(shí)現(xiàn)。一種嘗試的方法是這樣的:
// 另一種危險(xiǎn)的方法 (和不正確的)方法,用來(lái)
// 避免返回對(duì)象
const Rational& operator*(const Rational& lhs,
????????????????????????? const Rational& rhs)
{
? Rational result(lhs.numerator() * rhs.numerator(),
????????????????? lhs.denominator() * rhs.denominator());
? return result;
}
?? 這個(gè)函數(shù)返回的引用,其指向的對(duì)象已經(jīng)不存在了。它返回的是一個(gè)指向局部對(duì)象result的引用,當(dāng)operator* 退出時(shí)result被自動(dòng)釋放。返回指向已被釋放的對(duì)象的引用,這樣的引用絕對(duì)不能使用。
?? 相信我:一些函數(shù)(operator*也在其中)必須要返回對(duì)象。這就是它們的運(yùn)行方法。不要與其對(duì)抗,你不會(huì)贏的。
?? 你消除傳值返回的對(duì)象的努力不會(huì)獲得勝利。這是一場(chǎng)錯(cuò)誤的戰(zhàn)爭(zhēng)。從效率的觀點(diǎn)來(lái)看,你不應(yīng)該關(guān)心函數(shù)返回的對(duì)象,你僅僅應(yīng)該關(guān)心對(duì)象的開(kāi)銷。你所應(yīng)該關(guān)心的是把你的努力引導(dǎo)到尋找減少返回對(duì)象的開(kāi)銷上來(lái),而不是去消除對(duì)象本身(我們現(xiàn)在認(rèn)識(shí)到這種尋求是無(wú)用的)。如果沒(méi)有與這些對(duì)象相關(guān)的開(kāi)銷,誰(shuí)還會(huì)關(guān)心有多少對(duì)象被建立呢?
?? 以某種方法返回對(duì)象,能讓編譯器消除臨時(shí)對(duì)象的開(kāi)銷,這樣編寫(xiě)函數(shù)通常是很普遍的。這種技巧是返回constructor argument而不是直接返回對(duì)象,你可以這樣做:
// 一種高效和正確的方法,用來(lái)實(shí)現(xiàn)
// 返回對(duì)象的函數(shù)
const Rational operator*(const Rational& lhs,
???????????????????????? const Rational& rhs)
{
? return Rational(lhs.numerator() * rhs.numerator(),
????????????????? lhs.denominator() * rhs.denominator());
}
?? 仔細(xì)觀察被返回的表達(dá)式。它看上去好象正在調(diào)用Rational的構(gòu)造函數(shù),實(shí)際上確是這樣。你通過(guò)這個(gè)表達(dá)式建立一個(gè)臨時(shí)的Rational對(duì)象,
Rational(lhs.numerator() * rhs.numerator(),
???????? lhs.denominator() * rhs.denominator());
?? 并且這是一個(gè)臨時(shí)對(duì)象,函數(shù)把它拷貝給函數(shù)的返回值。
?? 返回constructor argument而不出現(xiàn)局部對(duì)象,這種方法還會(huì)給你帶來(lái)很多開(kāi)銷,因?yàn)槟闳耘f必須為在函數(shù)內(nèi)臨時(shí)對(duì)象的構(gòu)造和釋放而付出代價(jià),你仍舊必須為函數(shù)返回對(duì)象的構(gòu)造和釋放而付出代價(jià)。但是你已經(jīng)獲得了好處。C++規(guī)則允許編譯器優(yōu)化不出現(xiàn)的臨時(shí)對(duì)象(temporary objects out of existence)。因此如果你在如下的環(huán)境里調(diào)用operator*:
Rational a = 10;
Rational b(1, 2);
Rational c = a * b;????????????????????????? // 在這里調(diào)用operator*
?? 編譯器就會(huì)被允許消除在operator*內(nèi)的臨時(shí)變量和operator*返回的臨時(shí)變量。它們能在為目標(biāo)c分配的內(nèi)存里構(gòu)造return表達(dá)式定義的對(duì)象。如果你的編譯器這樣去做,調(diào)用operator*的臨時(shí)對(duì)象的開(kāi)銷就是零:沒(méi)有建立臨時(shí)對(duì)象。你的代價(jià)就是調(diào)用一個(gè)構(gòu)造函數(shù)――建立c時(shí)調(diào)用的構(gòu)造函數(shù)。而且你不能比這做得更好了,因?yàn)閏是命名對(duì)象,命名對(duì)象不能被消除。不過(guò)你還可以通過(guò)把函數(shù)聲明為inline來(lái)消除operator*的調(diào)用開(kāi)銷:
// 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());
}
?? “好,不錯(cuò)”,你嘀咕地說(shuō),“優(yōu)化,誰(shuí)關(guān)心編譯器能做什么?我想知道它們確實(shí)做了什么,Does any of this nonsense work with real compilers?” It does。這種特殊的優(yōu)化――通過(guò)使用函數(shù)的return 位置(或者在函數(shù)被調(diào)用位置用一個(gè)對(duì)象來(lái)替代)來(lái)消除局部臨時(shí)對(duì)象――是眾所周知的和被普遍實(shí)現(xiàn)的。它甚至還有一個(gè)名字:返回值優(yōu)化(return value optimization)(WQ加注:在《深度探索C++物件模型》中有更多更詳細(xì)的講述,它叫之為named return value optimization。