青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Beginning to 編程

VC++ 方面編程文章

 

EffectiveC++2ed 關于函數返回對象,引用還是指針

我看到EffectiveC++2ed中函數返回對象中的說明感覺以后再也不想讓返回任何東西啦。比較怕。

但是有的時候不返回任何東西是不行的阿。

返回引用,返回指針,返回對象到底怎么寫?!

——————————————————————————————
下面是EC中的內容

條款23: 必須返回一個對象時不要試圖返回一個引用

據說愛因斯坦曾提出過這樣的建議:盡可能地讓事情簡單,但不要過于簡單。在c++語言中相似的說法應該是:盡可能地使程序高效,但不要過于高效。

一旦程序員抓住了“傳值”在效率上的把柄(參見條款22),他們會變得十分極端,恨不得挖出每一個隱藏在程序中的傳值操作。豈不知,在他們不懈地追求純粹的“傳引用”的過程中,他們會不可避免地犯另一個嚴重的錯誤:傳遞一個并不存在的對象的引用。這就不是好事了。

看一個表示有理數的類,其中包含一個友元函數,用于兩個有理數相乘:

class rational {
public:
? rational(int numerator = 0, int denominator = 1);

? ...

private:
? int n, d;????????????? // 分子和分母

friend
? const rational????????????????????? // 參見條款21:為什么
??? operator*(const rational& lhs,??? // 返回值是const
????????????? const rational& rhs)????
};

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

很明顯,這個版本的operator*是通過傳值返回對象結果,如果不去考慮對象構造和析構時的開銷,你就是在逃避作為一個程序員的責任。另外一件很明顯的事實是,除非確實有必要,否則誰都不愿意承擔這樣一個臨時對象的開銷。那么,問題就歸結于:確實有必要嗎?

答案是,如果能返回一個引用,當然就沒有必要。但請記住,引用只是一個名字,一個其它某個已經存在的對象的名字。無論何時看到一個引用的聲明,就要立即問自己:它的另一個名字是什么呢?因為它必然還有另外一個什么名字(見條款m1)。拿operator*來說,如果函數要返回一個引用,那它返回的必須是其它某個已經存在的rational對象的引用,這個對象包含了兩個對象相乘的結果。

但,期望在調用operator*之前有這樣一個對象存在是沒道理的。也就是說,如果有下面的代碼:

rational a(1, 2);??????????????? // a = 1/2
rational b(3, 5);??????????????? // b = 3/5
rational c = a * b;????????????? // c 為 3/10

期望已經存在一個值為3/10的有理數是不現實的。如果operator* 一定要返回這樣一個數的引用,就必須自己創建這個數的對象。

一個函數只能有兩種方法創建一個新對象:在堆棧里或在堆上。在堆棧里創建對象時伴隨著一個局部變量的定義,采用這種方法,就要這樣寫operator*:

// 寫此函數的第一個錯誤方法
inline const rational& operator*(const rational& lhs,
???????????????????????????????? const rational& rhs)
{
? rational result(lhs.n * rhs.n, lhs.d * rhs.d);
? return result;
}

這個方法應該被否決,因為我們的目標是避免構造函數被調用,但result必須要象其它對象一樣被構造。另外,這個函數還有另外一個更嚴重的問題,它返回的是一個局部對象的引用,關于這個錯誤,條款31進行了深入的討論。

那么,在堆上創建一個對象然后返回它的引用呢?基于堆的對象是通過使用new產生的,所以應該這樣寫operator*:

// 寫此函數的第二個錯誤方法
inline const rational& operator*(const rational& lhs,
???????????????????????????????? const rational& rhs)
{
? rational *result =
??? new rational(lhs.n * rhs.n, lhs.d * rhs.d);
? return *result;
}

首先,你還是得負擔構造函數調用的開銷,因為new分配的內存是通過調用一個適當的構造函數來初始化的(見條款5和m8)。另外,還有一個問題:誰將負責用delete來刪除掉new生成的對象呢?

實際上,這絕對是一個內存泄漏。即使可以說服operator*的調用者去取函數返回值地址,然后用delete去刪除它(絕對不可能——條款31展示了這樣的代碼會是什么樣的),但一些復雜的表達式會產生沒有名字的臨時值,程序員是不可能得到的。例如:

rational w, x, y, z;

w = x * y * z;

兩個對operator*的調用都產生了沒有名字的臨時值,程序員無法看到,因而無法刪除。(再次參見條款31)

也許,你會想你比一般的熊——或一般的程序員——要聰明;也許,你注意到在堆棧和堆上創建對象的方法避免不了對構造函數的調用;也許,你想起了我們最初的目標是為了避免這種對構造函數的調用;也許,你有個辦法可以只用一個構造函數來搞掂一切;也許,你的眼前出現了這樣一段代碼:operator*返回一個“在函數內部定義的靜態rational對象”的引用:

// 寫此函數的第三個錯誤方法
inline const rational& operator*(const rational& lhs,
???????????????????????????????? const rational& rhs)
{
? static rational result;????? // 將要作為引用返回的
?????????????????????????????? // 靜態對象

? lhs和rhs 相乘,結果放進result;

? return result;
}

這個方法看起來好象有戲,雖然在實際實現上面的偽代碼時你會發現,不調用一個rational構造函數是不可能給出result的正確值的,而避免這樣的調用正是我們要談論的主題。就算你實現了上面的偽代碼,但,你再聰明也不能最終挽救這個不幸的設計。

想知道為什么,看看下面這段寫得很合理的用戶代碼:

bool operator==(const rational& lhs,????? // rationals的operator==
??????????????? const rational& rhs);???? //

rational a, b, c, d;

...

if ((a * b) == (c * d)) {

? 處理相等的情況;

} else {

? 處理不相等的情況;

}

看出來了嗎?((a*b) == (c*d)) 會永遠為true,不管a,b,c和d是什么值!

用等價的函數形式重寫上面的相等判斷語句就很容易明白發生這一可惡行為的原因了:

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

注意當operator==被調用時,總有兩個operator*剛被調用,每個調用返回operator*內部的靜態rational對象的引用。于是,上面的語句實際上是請求operator==對“operator*內部的靜態rational對象的值”和“operator*內部的靜態rational對象的值”進行比較,這樣的比較不相等才怪呢!

幸運的話,我以上的說明應該足以說服你:想“在象operator*這樣的函數里返回一個引用”實際上是在浪費時間。但我沒幼稚到會相信幸運總會光臨自己。一些人——你們知道這些人是指誰——此刻會在想,“唔,上面那個方法,如果一個靜態變量不夠用,也許可以用一個靜態數組……”

請就此打住!我們難道還沒受夠嗎?

我不能讓自己寫一段示例代碼來太高這個設計,因為即使只抱有上面這種想法都足以令人感到羞愧。首先,你必須選擇一個n,指定數組的大小。如果n太小,就會沒地方儲存函數返回值,這和我們前面否定的那個“采用單個靜態變量的設計”相比沒有什么改進。如果n太大,就會降低程序的性能,因為函數第一次被調用時數組中每個對象都要被創建。這會帶來n個構造函數和n個析構函數的開銷,即使這個函數只被調用一次。如果說"optimization"(最優化)是指提高軟件的性能的過程, 那么現在這種做法簡直可以稱為"pessimization"(最差化)。最后,想想怎么把需要的值放到數組的對象中以及需要多大的開銷?在對象間傳值的最直接的方法是通過賦值,但賦值的開銷又有多大呢?一般來說,它相當于調用一個析構函數(摧毀舊值)再加上調用一個構造函數(拷貝新值)。但我們現在的目標正是為了避免構造和析構的開銷啊!面對現實吧:這個方法也絕對不能選用。

所以,寫一個必須返回一個新對象的函數的正確方法就是讓這個函數返回一個新對象。對于rational的operator*來說,這意味著要不就是下面的代碼(就是最初看到的那段代碼),要不就是本質上和它等價的代碼:

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

的確,這會導致“operator*的返回值構造和析構時帶來的開銷”,但歸根結底它只是用小的代價換來正確的程序運行行為而已。況且,你所擔心的開銷還有可能永遠不會出現:和所有程序設計語言一樣,c++允許編譯器的設計者采用一些優化措施來提高所生成的代碼的性能,所以,在有些場合,operator*的返回值會被安全地除去(見條款m20)。當編譯器采用了這種優化時(當前大部分編譯器這么做),程序和以前一樣繼續工作,只不過是運行速度比你預計的要快而已。

以上討論可以歸結為:當需要在返回引用和返回對象間做決定時,你的職責是選擇可以完成正確功能的那個。至于怎么讓這個選擇所產生的代價盡可能的小,那是編譯器的生產商去想的事。

posted on 2006-03-24 15:51 Beginning to 編程 閱讀(2951) 評論(0)  編輯 收藏 引用 所屬分類: 心得體會

導航

統計

常用鏈接

留言簿(4)

隨筆分類

隨筆檔案

文章檔案

相冊

BlogDev

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国内精品久久久久影院色| 国产精品三级久久久久久电影| 老司机免费视频久久| 久久精品日产第一区二区| 小黄鸭精品密入口导航| 亚洲免费视频网站| 久久精品国产亚洲aⅴ| 久久精品国产v日韩v亚洲 | 亚洲精品一区在线观看| 99精品热6080yy久久| 一区二区激情| 午夜精品一区二区三区在线| 久久久久综合网| 欧美激情bt| 一区二区三区成人| 久久www成人_看片免费不卡| 免费中文字幕日韩欧美| 欧美日韩国产在线一区| 国产视频欧美视频| 日韩亚洲欧美一区| 久久精品视频在线观看| 亚洲国产三级网| 最新成人在线| 欧美在线视频在线播放完整版免费观看| 校园激情久久| 欧美日韩日本国产亚洲在线| 狠狠色综合一区二区| 中文一区二区| 欧美777四色影视在线| 一区二区国产日产| 久久久久国产精品麻豆ai换脸| 亚洲精品欧洲精品| 国产精品日韩一区| 亚洲日韩欧美一区二区在线| 性8sex亚洲区入口| 亚洲人成网站777色婷婷| 性色av一区二区三区| 欧美区亚洲区| 亚洲国产美女久久久久| 久久精品亚洲国产奇米99| 99在线精品视频| 欧美劲爆第一页| 亚洲国产一区在线| 免费观看久久久4p| 久久高清福利视频| 国产精品一区=区| 亚洲自拍偷拍麻豆| 夜夜嗨av一区二区三区四区 | 久久久久久**毛片大全| 一本综合精品| 欧美日韩精品免费在线观看视频| 精久久久久久| 久久在线观看视频| 久久国产精品久久久| 国产欧美一区二区三区久久人妖| 亚洲神马久久| 99re8这里有精品热视频免费 | 亚洲在线观看免费| 亚洲日本中文字幕区| 美日韩在线观看| 亚洲国产日韩欧美综合久久| 欧美va天堂在线| 久久夜色精品国产欧美乱极品| 国产在线观看精品一区二区三区 | 久久亚洲美女| 久久精品夜夜夜夜久久| 国产一区二区三区av电影| 久久人人爽人人| 欧美专区亚洲专区| 国产精品亚洲综合天堂夜夜| 久久aⅴ国产欧美74aaa| 亚洲欧洲一区二区天堂久久| 欧美国产激情二区三区| 夜夜狂射影院欧美极品| 亚洲精品在线免费| 欧美午夜国产| 久久精品二区| 免费在线亚洲| 午夜天堂精品久久久久| 午夜一区在线| 亚洲电影激情视频网站| 欧美国产欧美亚州国产日韩mv天天看完整 | 欧美日本三级| 免费观看不卡av| 久久视频在线免费观看| 久久九九99视频| 亚洲黄色av| 亚洲国产日韩综合一区| 欧美视频一区二区三区在线观看 | 亚洲网站在线| 欧美一区二区三区喷汁尤物| 亚洲第一在线视频| 日韩视频一区二区在线观看 | 久久精品国产综合精品| 亚洲精选成人| 性色av一区二区三区| 亚洲电影免费在线| 亚洲一区二区三区四区在线观看 | 一本大道久久a久久精二百| 亚洲专区免费| 亚洲精品日韩激情在线电影| 亚洲一区二区三区四区中文| 亚洲国产高清高潮精品美女| 亚洲美女中文字幕| 亚洲第一网站| 亚洲一区二区伦理| 亚洲人成网站精品片在线观看| 一区二区三区色| 亚洲激情成人网| 销魂美女一区二区三区视频在线| 日韩亚洲欧美综合| 久久国产日韩欧美| 午夜在线精品偷拍| 欧美日韩国产色视频| 欧美不卡视频一区| 国产一区在线播放| 亚洲深夜福利在线| 夜久久久久久| 欧美国产精品久久| 欧美成年视频| 一区二区三区在线免费观看| 亚洲欧美日韩综合aⅴ视频| 中文一区二区在线观看| 欧美+日本+国产+在线a∨观看| 久久久久久久综合色一本| 国产精品热久久久久夜色精品三区 | 91久久久精品| 亚洲国产精品美女| 久久久久免费视频| 欧美一区二区三区四区在线观看| 欧美日韩午夜在线| 亚洲欧洲日本专区| 亚洲一级二级| 久久大综合网| 久久天天躁狠狠躁夜夜av| 国产乱肥老妇国产一区二| 亚洲网站在线看| 亚洲一区二区三区在线看| 欧美日韩免费观看一区=区三区| 亚洲激情小视频| 99国产精品久久| 欧美日韩第一区日日骚| 99精品国产在热久久| 一区二区三区高清在线观看| 欧美日韩国产综合一区二区| 99国产麻豆精品| 亚洲一区高清| 国产精品男gay被猛男狂揉视频| 日韩一本二本av| 欧美一区二区三区视频免费| 好看的日韩视频| 欧美v国产在线一区二区三区| 亚洲国产精品嫩草影院| 亚洲视频综合| 国产欧美在线观看一区| 久久精品网址| 亚洲日本欧美日韩高观看| 亚洲欧美日韩精品久久久久| 国产一区二区精品久久91| 久久成人国产精品| 亚洲国产成人午夜在线一区| 亚洲一区中文| 激情欧美国产欧美| 欧美久久久久免费| 亚洲影视综合| 噜噜噜噜噜久久久久久91| 亚洲日韩第九十九页| 国产精品免费观看视频| 久热精品在线| 亚洲无线一线二线三线区别av| 久久综合给合| 亚洲小说欧美另类社区| 精品1区2区3区4区| 欧美天堂亚洲电影院在线播放| 午夜精品视频在线观看| 欧美国产一区视频在线观看| 亚洲一区二区三区高清 | aa成人免费视频| 国产一区二区三区四区在线观看| 欧美成人首页| 亚洲欧美久久久久一区二区三区| 猛男gaygay欧美视频| 亚洲欧美日韩另类| 亚洲日本免费电影| 国产亚洲精品一区二555| 欧美日韩精品一区二区天天拍小说| 欧美在线视频观看免费网站| 日韩午夜在线视频| 欧美国产精品人人做人人爱| 亚洲中无吗在线| 亚洲理论在线观看| 精品51国产黑色丝袜高跟鞋| 国产精品亚洲产品| 国产精品国色综合久久| 欧美精品18| 欧美成人一二三| 久久综合五月| 久久蜜桃精品| 久久国产精品久久国产精品| 亚洲欧美制服另类日韩|