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

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 編程 閱讀(2952) 評論(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>
            亚洲人成毛片在线播放| 亚洲无人区一区| 国产精品久久久久久久久| 免费在线日韩av| 欧美成人精品福利| 欧美精品v国产精品v日韩精品| 美女亚洲精品| 欧美国产日韩一区二区| 欧美韩国日本一区| 欧美午夜精品久久久久久超碰| 欧美视频网址| 国产日韩欧美夫妻视频在线观看| 国产亚洲欧美在线| 亚洲国产裸拍裸体视频在线观看乱了 | 美国十次成人| 欧美国产日本高清在线| 国产精品多人| 伊人成人开心激情综合网| 亚洲精品一区二区三区樱花| 一区二区三区欧美视频| 亚洲欧洲av一区二区| 久久精品视频在线| 亚洲二区在线视频| 一区二区三区av| 久久成人亚洲| 欧美人妖在线观看| 国产午夜精品一区理论片飘花| 亚洲国产日韩欧美在线动漫| 亚洲综合视频一区| 亚洲电影下载| 午夜精品久久久久久久男人的天堂 | 国语对白精品一区二区| 亚洲激情小视频| 亚洲永久网站| 欧美大片免费观看| 亚洲欧美日韩专区| 欧美国产高清| 在线成人黄色| 久久精品国亚洲| 在线视频你懂得一区二区三区| 亚洲欧美精品在线观看| 亚洲高清在线| 久久久久久久国产| 国产日韩亚洲欧美综合| 亚洲小视频在线观看| 免费影视亚洲| 欧美一级午夜免费电影| 欧美手机在线| 日韩亚洲欧美在线观看| 免费人成网站在线观看欧美高清| 亚洲一区二区三区国产| 欧美日韩日本网| 亚洲美女黄网| 亚洲国产精品成人| 美女视频一区免费观看| 在线成人激情黄色| 欧美大片在线看免费观看| 欧美在线精品一区| 好吊日精品视频| 开元免费观看欧美电视剧网站| 欧美在线观看视频| 国产在线播放一区二区三区| 久久精品日产第一区二区三区| 亚洲一区日本| 国产日韩av高清| 久久久综合激的五月天| 欧美自拍偷拍午夜视频| 狠狠色狠狠色综合日日五| 久久久久久穴| 久久综合九色综合欧美狠狠| 亚洲激情第一区| 亚洲国内精品在线| 欧美日韩伦理在线| 亚洲永久精品大片| 先锋影音网一区二区| 国产亚洲一本大道中文在线| 国产精品婷婷| 欧美制服丝袜| 久久久噜噜噜久久中文字免| 亚洲高清不卡一区| 亚洲激情成人网| 国产精品a级| 久久免费视频在线观看| 久久综合精品一区| 亚洲视频在线一区观看| 亚洲欧美在线免费| 亚洲电影免费观看高清完整版在线观看 | 亚洲国产高清一区| 亚洲免费黄色| 日韩视频在线免费| 国产一区二区三区四区hd| 欧美在线视频一区二区三区| 久久精品理论片| 国产精品jvid在线观看蜜臀| 亚洲视频在线观看视频| 国产精品天美传媒入口| 午夜精品一区二区三区在线视| 99这里只有精品| 国产日韩av高清| 亚洲第一久久影院| 国产精品v欧美精品v日韩| 欧美在线亚洲| 欧美大片免费观看| 欧美影院成人| 欧美大片在线观看一区| 亚洲欧美区自拍先锋| 久久久视频精品| 中文在线资源观看视频网站免费不卡| 亚洲欧美国产另类| 99国产成+人+综合+亚洲欧美| 午夜精品视频网站| 日韩视频在线观看国产| 久久精品欧美日韩| 亚洲亚洲精品三区日韩精品在线视频| 久久国产免费| 午夜精品久久久久久久久| 久久久久久久网站| 欧美伊久线香蕉线新在线| 欧美另类极品videosbest最新版本| 欧美在线视频观看| 欧美日韩在线精品| 亚洲高清久久网| 伊人蜜桃色噜噜激情综合| 亚洲一区二区在| 亚洲视频专区在线| 欧美连裤袜在线视频| 欧美粗暴jizz性欧美20| 国内精品伊人久久久久av一坑| 一区二区av| 一区二区三区黄色| 欧美激情aaaa| 欧美aaa级| 极品裸体白嫩激情啪啪国产精品| 亚洲天堂av图片| 亚洲天堂网站在线观看视频| 欧美国产精品中文字幕| 亚洲大片在线| 亚洲久久视频| 欧美另类videos死尸| 日韩视频在线观看| 男人的天堂成人在线| 欧美成人亚洲成人日韩成人| 一区二区在线观看av| 久久精品亚洲| 欧美国产国产综合| 亚洲欧洲精品一区| 国产精品一区免费视频| 亚洲免费久久| 在线午夜精品| 国产精品精品视频| 亚洲欧美日韩精品久久| 久久av在线| 好看的日韩视频| 麻豆久久精品| 亚洲精品久久久蜜桃| 一区二区免费在线观看| 国产精品高潮粉嫩av| 亚洲综合国产精品| 久久亚洲精品伦理| 亚洲精选大片| 国产精品一区亚洲| 久久精品国产在热久久| 欧美激情1区2区3区| 亚洲视频在线二区| 国产一区二区av| 卡一卡二国产精品| 99香蕉国产精品偷在线观看| 性欧美video另类hd性玩具| 国产自产精品| 欧美精品一区视频| 午夜日韩av| 亚洲大胆av| 羞羞答答国产精品www一本 | 欧美日韩伦理在线免费| 亚洲视频网在线直播| 久久久久国产精品一区三寸| 亚洲国产精品成人| 国产精品成人观看视频国产奇米| 欧美中文字幕在线播放| 亚洲三级色网| 久久五月天婷婷| 亚洲小说欧美另类社区| 在线观看一区| 国产精品一区二区久久国产| 美玉足脚交一区二区三区图片| 一本一本久久a久久精品综合麻豆 一本一本久久a久久精品牛牛影视 | 亚洲电影免费在线| 欧美一区高清| 亚洲久久视频| 狠狠综合久久av一区二区小说| 欧美日韩国产91| 久久欧美中文字幕| 亚洲一区二区不卡免费| 亚洲第一区在线观看| 欧美在线一区二区三区| 毛片一区二区| 欧美一区二区三区视频在线观看 | 午夜精品久久久久久久蜜桃app| 亚洲福利专区| 欧美顶级艳妇交换群宴|