欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
C++ 箴言:必須返回對象時(shí)別返回引用

C++箴言:必須返回對象時(shí)別返回引用

2005-08-01 09:44 作者: fatalerror99 出處: csdn blog 責任編輯:方舟
  一旦程序員抓住對象傳值的效率隱憂(yōu),很多人就會(huì )成為狂熱的圣戰分子,誓要根除傳值的罪惡,無(wú)論它隱藏多深。他們不屈不撓地追求傳引用的純度,但他們全都犯了一個(gè)致命的錯誤:他們開(kāi)始傳遞并不存在的對象的引用。這可不是什么好事。
  考慮一個(gè)代表有理數的類(lèi),包含一個(gè)將兩個(gè)有理數相乘的函數:

class Rational {
 public:
  Rational(int numerator = 0, // see Item 24 for why this
  int denominator = 1); // ctor isn’t declared explicit

  ...

 private:
  int n, d; // numerator and denominator

 friend:
  const Rational // see Item 3 for why the
  operator*(const Rational& lhs, // return type is const
  const Rational& rhs);
};

  operator* 的這個(gè)版本以傳值方式返回它的結果,而且如果你沒(méi)有擔心那個(gè)對象的構造和析構的代價(jià),你就是在推卸你的專(zhuān)業(yè)職責。如果你不是迫不得已,你不應該為這樣的一個(gè)對象付出成本。所以問(wèn)題就在這里:你是迫不得已嗎?

  哦,如果你能用返回一個(gè)引用來(lái)作為代替,你就不是迫不得已。但是,請記住一個(gè)引用僅僅是一個(gè)名字,一個(gè)實(shí)際存在的對象的名字。無(wú)論何時(shí)只要你看到一個(gè)引用的聲明,你應該立刻問(wèn)自己它是什么東西的另一個(gè)名字,因為它必定是某物的另一個(gè)名字。在這個(gè) operator* 的情況下,如果函數返回一個(gè)引用,它必須返回某個(gè)已存在的而且其中包含兩個(gè)對象相乘的產(chǎn)物的 Rational 對象的引用。

  當然沒(méi)有什么理由期望這樣一個(gè)對象在調用 operator* 之前就存在。也就是說(shuō),如果你有

Rational a(1, 2); // a = 1/2
Rational b(3, 5); // b = 3/5

Rational c = a * b; // c should be 3/10

  似乎沒(méi)有理由期望那里碰巧已經(jīng)存在一個(gè)值為十分之三的有理數。不是這樣的,如果 operator* 返回這樣一個(gè)數的引用,它必須自己創(chuàng )建那個(gè)數字對象。

  一個(gè)函數創(chuàng )建一個(gè)新對象僅有兩種方法:在棧上或者在堆上。棧上的生成物通過(guò)定義一個(gè)局部變量而生成。使用這個(gè)策略,你可以用這種方法試寫(xiě) operator*:

const Rational& operator*(const Rational& lhs, // warning! bad code!
const Rational& rhs)
{
 Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
 return result;
}

  你可以立即否決這種方法,因為你的目標是避免調用構造函數,而 result 正像任何其它對象一樣必須被構造。一個(gè)更嚴重的問(wèn)題是這個(gè)函數返回一個(gè)引向 result 的引用,但是 result 是一個(gè)局部對象,而局部對象在函數退出時(shí)被銷(xiāo)毀。那么,這個(gè) operator* 的版本不會(huì )返回引向一個(gè) Rational 的引用——它返回引向一個(gè)前 Rational;一個(gè)曾經(jīng)的 Rational;一個(gè)空洞的、惡臭的、腐敗的,從前是一個(gè) Rational 但永不再是的尸體的引用,因為它已經(jīng)被銷(xiāo)毀了。任何調用者甚至于沒(méi)有來(lái)得及匆匆看一眼這個(gè)函數的返回值就立刻進(jìn)入了未定義行為的領(lǐng)地。這是事實(shí),任何返回一個(gè)引向局部變量的引用的函數都是錯誤的。(對于任何返回一個(gè)指向局部變量的指針的函數同樣成立。)

  那么,讓我們考慮一下在堆上構造一個(gè)對象并返回引向它的引用的可能性?;诙训膶ο笸ㄟ^(guò)使用 new 而開(kāi)始存在,所以你可以像這樣寫(xiě)一個(gè)基于堆的 operator*:

const Rational& operator*(const Rational& lhs, // warning! more bad
const Rational& rhs) // code!
{
 Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
 return *result;
}

  哦,你還是必須要付出一個(gè)構造函數調用的成本,因為通過(guò) new 分配的內存要通過(guò)調用一個(gè)適當的構造函數進(jìn)行初始化,但是現在你有另一個(gè)問(wèn)題:誰(shuí)是刪除你用 new 做出來(lái)的對象的合適人選?

  即使調用者盡職盡責且一心向善,它們也不太可能是用這樣的方案來(lái)合理地預防泄漏:

Rational w, x, y, z;

w = x * y * z; // same as operator*(operator*(x, y), z)

  這里,在同一個(gè)語(yǔ)句中有兩個(gè) operator* 的調用,因此 new 被使用了兩次,這兩次都需要使用 delete 來(lái)銷(xiāo)毀。但是 operator* 的客戶(hù)沒(méi)有合理的辦法進(jìn)行那些調用,因為他們沒(méi)有合理的辦法取得隱藏在通過(guò)調用 operator* 返回的引用后面的指針。這是一個(gè)早已注定的資源泄漏。

  但是也許你注意到無(wú)論是在棧上的還是在堆上的方法,為了從 operator* 返回的每一個(gè) result,我們都不得不容忍一次構造函數的調用。也許你想起我們最初的目標是避免這樣的構造函數調用。也許你認為你知道一種方法能避免除一次以外幾乎全部的構造函數調用。也許下面這個(gè)實(shí)現是你做過(guò)的,一個(gè)基于 operator* 返回一個(gè)引向 static Rational 對象的引用的實(shí)現,而這個(gè) static Rational 對象定義在函數內部:

const Rational& operator*(const Rational& lhs, // warning! yet more
const Rational& rhs) // bad code!
{
 static Rational result; // static object to which a
 // reference will be returned

 result = ... ; // multiply lhs by rhs and put the
 // product inside result
 return result;
}

  就像所有使用了 static 對象的設計一樣,這個(gè)也會(huì )立即引起我們的線(xiàn)程安全(thread-safety)的混亂,但那是它的比較明顯的缺點(diǎn)。為了看到它的更深層的缺陷,考慮這個(gè)完全合理的客戶(hù)代碼:

bool operator==(const Rational& lhs, // an operator==
const Rational& rhs); // for Rationals

Rational a, b, c, d;

...
if ((a * b) == (c * d)) {
 do whatever’s appropriate when the products are equal;
} else {
 do whatever’s appropriate when they’re not;
}

  猜猜會(huì )怎么樣?不管 a,b,c,d 的值是什么,表達式 ((a*b) == (c*d)) 總是等于 true!

  如果代碼重寫(xiě)為功能完全等價(jià)的另一種形式,這一啟示就很容易被理解了:

if (operator==(operator*(a, b), operator*(c, d)))

  注意,當 operator== 被調用時(shí),將同時(shí)存在兩個(gè)起作用的對 operator* 的調用,每一個(gè)都將返回引向 operator* 內部的 static Rational 對象的引用。因此,operator== 將被要求比較 operator* 內部的 static Rational 對象的值和 operator* 內部的 static Rational 對象的值。如果它們不是永遠相等,那才真的會(huì )令人大驚失色了。

  這些應該足夠讓你信服試圖從類(lèi)似 operator* 這樣的函數中返回一個(gè)引用純粹是浪費時(shí)間,但是你們中的某些人可能會(huì )這樣想“好吧,就算一個(gè) static 不夠用,也許一個(gè) static 的數組是一個(gè)竅門(mén)……”

  我無(wú)法拿出示例代碼來(lái)肯定這個(gè)設計,但我可以概要說(shuō)明為什么這個(gè)想法應該讓你羞愧得無(wú)地自容。首先,你必須選擇一個(gè) n 作為數組的大小。如果 n 太小,你可能會(huì )用完存儲函數返回值的空間,與剛剛名譽(yù)掃地的 single-static 設計相比,在任何一個(gè)方面你都不會(huì )得到更多的東西。但是如果 n 太大,就會(huì )降低你的程序的性能,因為在函數第一次被調用的時(shí)候數組中的每一個(gè)對象都會(huì )被構造。即使這個(gè)我們正在討論的函數僅被調用了一次,也將讓你付出 n 個(gè)構造函數和 n 個(gè)析構函數的成本。如果“優(yōu)化”是提高軟件效率的過(guò)程,對于這種東西也只能是“悲觀(guān)主義”的。最后,考慮你怎樣將你所需要的值放入數組的對象中,以及你做這些需要付出什么。在兩個(gè)對象間移動(dòng)值的最直接方法就是通過(guò)賦值,但是一次賦值將要付出什么?對于很多類(lèi)型,這就大約相當于調用一次析構函數(銷(xiāo)毀原來(lái)的值)加上調用一次構造函數(把新值拷貝過(guò)去)。但是你的目標是避免付出構造和析構成本!面對的結果就是:這個(gè)方法絕對不會(huì )成功。(不,用一個(gè) vector 代替數組也不會(huì )讓事情有多少改進(jìn)。)

  寫(xiě)一個(gè)必須返回一個(gè)新對象的函數的正確方法就是讓那個(gè)函數返回一個(gè)新對象。對于 Rational 的 operator*,這就意味著(zhù)下面這些代碼或在本質(zhì)上與其相當的某些東西:

inline const Rational operator*(const Rational& lhs, const Rational& rhs)
{
 return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}

  當然,你可能付出了構造和析構 operator* 的返回值的成本,但是從長(cháng)遠看,這只是為正確行為付出的很小的代價(jià)。除此之外,這種令你感到恐怖的賬單也許永遠都不會(huì )到達。就像所有的程序設計語(yǔ)言,C++ 允許編譯器的實(shí)現者在不改變生成代碼的可觀(guān)察行為的條件下使用優(yōu)化來(lái)提升它的性能,在某些條件下會(huì )產(chǎn)生如下結果:operator* 的返回值的構造和析構能被安全地消除。如果編譯器利用了這一點(diǎn)(編譯器經(jīng)常這樣做),你的程序還是在它假定的方法上繼續運行,只是比你期待的要快。 全部的焦點(diǎn)在這里:如果需要在返回一個(gè)引用和返回一個(gè)對象之間做出決定,你的工作就是讓那個(gè)選擇能提供正確的行為。讓你的編譯器廠(chǎng)商去絞盡腦汁使那個(gè)選擇盡可能地廉價(jià)。


  Things to Remember

  ·絕不要返回一個(gè)局部棧對象的指針或引用,絕不要返回一個(gè)被分配的堆對象的引用,如果存在需要一個(gè)以上這樣的對象的可能性時(shí),絕不要返回一個(gè)局部 static 對象的指針或引用。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
More Effective C++ (M16 -to- M24) 效率
C++編程實(shí)用技巧 #31:千萬(wàn)不要返回局部對象的引用
const使用詳解
關(guān)于接口的設計與聲明--對封裝性的理解
Effective C++讀書(shū)筆記
C++ vector 類(lèi)學(xué)習筆記 - C/C++ - 在路上
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久