• <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>

            Shuffy

            不斷的學(xué)習(xí),不斷的思考,才能不斷的進步.Let's do better together!
            posts - 102, comments - 43, trackbacks - 0, articles - 19

            [轉(zhuǎn)]http://m.shnenglu.com/tiandejian/archive/2007/04/30/ecpp_11.html
            第11條:
            operator= 中要考慮到 自賦值問題

            當(dāng)對象對其自身賦值時,就發(fā)生了一次“自賦值”:

            class Widget { ... };

             

            Widget w;

            ...

            w = w;                                   // 自賦值

            這樣做看上去沒什么意義,但這是合法的,因此以后我們假設(shè)客戶端程序員可能會這樣做。而且,賦值工作本身并不總是那么容易辨認(rèn)的。比如:

            a[i] = a[j];                           // 可能發(fā)生自賦值

            如果 i j 的值相同,那么這就是一次自賦值。另外

            *px = *py;                             // 可能發(fā)生自賦值

            px py 指向同一處時,上面一行也是一次自賦值。這些自賦值并不是那么一目了然,它們是由別名造成的:可以通過多種方式引用同一個對象。大體上講,用來操作指向同一類型多個對象的引用或指針的代碼都應(yīng)考慮對象重復(fù)的問題。實際上,假如兩個對象來自同一層次,即使它們并未聲明為同一類型,也要考慮重復(fù)問題,這是因為一個基類的引用或指針可以引用或指向其派生類的類型的對象。

            class Base { ... };

             

            class Derived: public Base { ... };

             

            void doSomething(const Base& rb,       // rb *pd 可能實際上

                                  Derived* pd);        // 是同一個對象

            假設(shè)你遵循第 13 條和第 14 條中的建議,你將會一直使用對象來管理資源,而且在復(fù)制時你將會確保資源管理對象能正確工作。如果上邊的假設(shè)成立,你的賦值運算符很可能在處理自賦值時將是安全的,你不需要額外關(guān)注它。然而如果你試圖自己來管理資源(顯然你在編寫資源管理類時必須要這樣做),此時你很有可能陷入這個陷阱中:一個對象尚未用完,但是你卻不小心將其釋放了。比如說,你創(chuàng)建了一個類其中放置了一個無類型指針,你用這個指針來動態(tài)分配位圖:

            class Bitmap { ... };

             

            class Widget {

             ...

             

            private:

             Bitmap *pb;                          // 指向一個分配在堆上的對象

            };

            下邊給出 operator= 的一個實現(xiàn),它在表面看上去很合理,但是如果存在自賦值,它便是不安全的。(它在出現(xiàn)異常時也不安全,稍后我們討論這個問題)

            Widget&

            Widget::operator=(const Widget& rhs)   // operator= 不安全的實現(xiàn)

            {

             delete pb;                           // 停止使用當(dāng)前的位圖

             pb = new Bitmap(*rhs.pb);            // 開始使用 rhs 位圖的一份拷貝

             return *this;                        // 參見第 10

            }

            此處的自賦值問題出現(xiàn)在 operator= 的內(nèi)部, *this (賦值操作的目標(biāo))和 rhs 有可能是同一對象。如果它們是, delete 便不僅僅銷毀了當(dāng)前對象的位圖,同時它也銷毀了 rhs 的位圖。 Widget 的值本不應(yīng)該在自賦值操作中改變,然而在函數(shù)的末尾,它會發(fā)現(xiàn):它們包含的指針指向了一個已經(jīng)被刪除的對象!

            防止這類錯誤發(fā)生的傳統(tǒng)方法是:在 operator= 的最頂端通過一致性檢測來監(jiān)視自賦值:

            Widget& Widget::operator=(const Widget& rhs)

            {

             if (this == &rhs) return *this;       // 一致性檢測: 如果出現(xiàn)自賦值

                                                   // 則什么也不做

             delete pb;

             pb = new Bitmap(*rhs.pb);

             return *this;

            }

            這樣可以正常工作,但是我曾經(jīng)說過 operator= 的早期版本不僅僅在賦值時不安全,在發(fā)生異常時它也會出現(xiàn)問題。特別地,如果“ new Bitmap ”語句引發(fā)了一個異常(有可能是可分配內(nèi)存耗盡,或者是 Bitmap 的拷貝構(gòu)造函數(shù)拋出了一個異常),最后 Widget 所包含的指針仍將指向一個已被刪除的 Bitmap 。這類指針是有毒的。你無法安全的刪除它們。你甚至沒辦法安全的讀取它們。此時你所做的唯一一件安全的事情也許就是耗費大量的精力去排查 bug

            還好,在讓 operator= 在遇到異常時能安全執(zhí)行的同時,它也不會在自賦值時出現(xiàn)問題了。因此,你可以把目光集中在異常的安全問題上,而忽略自賦值的問題。第 29 條中深入討論異常中的安全問題,但是本條中已經(jīng)可以很清晰地看出:在許多情況下,認(rèn)真安排一下語句可以使你的代碼在出現(xiàn)異常時是安全的(同時在自賦值時也是安全的)。比方說,現(xiàn)在我們只需要認(rèn)真考慮:在我們沒有把 pb 對象復(fù)制出來以前,千萬不要刪除它:

            Widget& Widget::operator=(const Widget& rhs)

            {

             Bitmap *pOrig = pb;               // 復(fù)制原始的 pb

             pb = new Bitmap(*rhs.pb);         // pb 指向 *pb 的這一副本

             delete pOrig;                     // 刪除原始的 pb

             

             return *this;

            }

            現(xiàn)在,如果“ new Bitmap ”拋出一個異常, pb (及其所在的 Widget )沒有被改動。即使沒有進行一致性檢測,這段代碼也可以解決自賦值問題,這是因為我們復(fù)制出了原始位圖的一個副本,并且刪除了原始副本,然后指向我們復(fù)制出的那個副本。這也許不是解決自賦值問題的最高效的途徑,但是這樣做確實有效。

            如果你考慮到效率問題,你可以重新在程序最開端添加一致性檢測。然而在做這件事之前,問一下自己,你期望自賦值出現(xiàn)的有多頻繁,因為一致性檢測也有系統(tǒng)開銷。首先這使得代碼(源代碼和對象)變得稍長一些,同時它也會為控制流引入一個分支,這兩點都會降低運行的速度。比如說,指令預(yù)讀、捕獲、管線分配等操作的執(zhí)行效率將會受到影響。

            為了使 operator= 的實現(xiàn)對異常和自賦值都保證安全,必須為其手動安排語句,這里還有另一個途徑:使用一個稱為“復(fù)制并交換”的技術(shù)。這一技術(shù)更加貼近異常安全問題,所以我們在第 29 條中討論它。但是它在編寫 operator= 時得到了十分普遍的應(yīng)用,看一下它實現(xiàn)的方法是十分值得的:

            class Widget {

             ...

             void swap(Widget& rhs);              // 交換 *this rhs 中的數(shù)據(jù) ;

             ...                                  // 更多細節(jié)請參見第 29

            };

             

            Widget& Widget::operator=(const Widget& rhs)

            {

             Widget temp(rhs);                     // rhs 的數(shù)據(jù)保存副本

             swap(temp);                           // 使用上邊的副本與 *this 交換

             return *this;

            }

            上述的主題可以進行一下演變,可 以利用以下一些事實: (1) 一個類的拷貝賦值運算符的參數(shù)可以通過傳值方式實現(xiàn); (2) 通過傳值可以傳遞這一參數(shù)的一個副本(參見第 20 條):

            Widget& Widget::operator=(Widget rhs)   // rhs is a copy of the object

            {                                       // passed in — note pass by val

             swap(rhs);                            // swap *this's data with

                                                    // the copy's

             return *this;

            }

            從我個人角度來講,我很擔(dān)心這一點,這個手段會將清晰度作為“祭祀品”擺放在靈巧性的“祭壇”上,但是把復(fù)制操作從函數(shù)體中移出來,放在參數(shù)結(jié)構(gòu)中,在一些場合確實能夠編寫出更加高效的代碼。

            需要記住的

            在一個對象為自己賦值時,要確保 operator= 可以正常地運行。可以使用的技術(shù)有:比較源對象和目標(biāo)對象的地址、謹(jǐn)慎安排語句、以及“復(fù)制并交換”。

            在兩個或兩個以上的對象完全一樣時,要確保對于這些重復(fù)對象的操作可以正常運行。

            久久久国产亚洲精品| 最新久久免费视频| 久久午夜伦鲁片免费无码| 日韩亚洲国产综合久久久| 超级97碰碰碰碰久久久久最新| 亚洲精品成人久久久| 亚洲精品国产第一综合99久久| 亚洲一级Av无码毛片久久精品| 亚洲精品乱码久久久久久不卡| 亚洲精品乱码久久久久66| 国产一级做a爰片久久毛片| 要久久爱在线免费观看| 久久精品国产91久久综合麻豆自制 | 久久久久亚洲精品无码蜜桃| 国产麻豆精品久久一二三| 韩国三级中文字幕hd久久精品| 99精品国产99久久久久久97| 亚洲av成人无码久久精品| 久久精品国产亚洲AV无码偷窥| 熟妇人妻久久中文字幕| 久久婷婷五月综合国产尤物app| 女人高潮久久久叫人喷水| 91精品国产色综合久久| 久久免费看黄a级毛片| 亚洲国产日韩欧美综合久久| 2021久久精品免费观看| 久久一区二区三区免费| 国产精品va久久久久久久| 亚洲国产精品高清久久久| 久久国产精品-国产精品| 久久99精品九九九久久婷婷| 日韩欧美亚洲综合久久影院Ds | 精品综合久久久久久97超人 | 久久精品aⅴ无码中文字字幕不卡 久久精品aⅴ无码中文字字幕重口 | 97久久超碰国产精品2021| 国产精品久久久久AV福利动漫| 亚洲精品国产自在久久| 久久久久久亚洲精品无码| 精品久久久久久久中文字幕| 国产日韩欧美久久| 久久久久综合中文字幕|