Effective C++建構函式解構函式Assignment運算子

來源:互聯網
上載者:User

在看《Effective C++》這本書的過程中,我無數次的發出感歎,這他媽寫得太好了,句句一針見血,直接說到點上。所以決定把這本書的內容加上自己的理解寫成5篇部落格,我覺得不管你是否理解這些條款,都值得你先記下來。下面的索引對應的是書中的章節。

 

11:如果class內動態配置有記憶體,請為此class聲明一個copy constructor和一個assignment運算子

12:在constructor中盡量initialization動作取代assignment工作

13:initialization list中的members初始化次序應該和其在class內的聲明次序相同

14:總上base class擁有virtual destructor

15:令operator =傳回*this的reference

16:在operator=中為所有data member賦值

17:在operator =中檢查是否自己賦值給自己

 

11:如果class內動態配置有記憶體,請為此class聲明一個copy constructor和一個assignment運算子。

預設的copy constructor和operator=不會對類的每個data member一一賦值,而是一個簡單引用,讓左邊的對象指向右邊對象所指的對象。再也沒有起動作,如果這個類動態分配了記憶體,比如左邊對象本來指向一塊記憶體A,現在左邊的對象指向右邊對象所指的記憶體B了,而再也沒有其他對象指向記憶體A,由於它是動態分配,不會自己回收,所以就出現記憶體泄露,還有就是兩個對象指向同一塊記憶體,如果其中一個對象出了其範圍,那麼其解構函式將自動調用,其動態分配的記憶體將被回收,現在另一個對象卻指向一塊已經被回收的記憶體,只要一調用這個對象的資料,就會出現不可知的異常,還有一點值得一提,就算一個對象沒有指向任何地址或是它所指的地址已經被回收了,在調用它的方法的時候,只要它的方法沒有使用它的data member(肯定不存在),就不會出現任何問題,因為方法的記憶體是和物件類型一起分配,執行個體化一個對象的時候不會為方法分配記憶體,只會為data member及其他一些指標分配記憶體,如指向父類的指標,指向虛擬表的指標等。

如11所述,如果不聲明那兩個方法,在方法調用的時候也會出現問題,當這個對象是以傳值的方式被調用時,會產生一個臨時變數,這個臨時變數會引用這個對象,當方法執行完成,這個臨時變數超出它的範圍,解構函式被調用,這個對象就這樣被銷毀了。所以你必須遵守這一條規則。

 

12:在constructor中盡量initialization動作取代assignment工作

對象的構造分兩個階段:

1:data member被初始化

2:被調用的建構函式執行起來

如果在建構函式中對data member一一賦值,那麼先要調用data member的建構函式,如果你沒有為data member賦初值,那麼調用的是預設的建構函式,如果你賦了初值調用的是copy constructor,但是我的編譯器不允許data member在定義的時候賦初值,那麼就是調用預設的建構函式,當你在建構函式內為data member賦值的時候調用operator =,相當於你調用了一次constructor和一次operator =,而initialization 只調用一次copy constructor,因為在data member初始化的時候已經為data member賦值了,在建構函式裡面就不用為data member賦值了,經常會遇到這樣的面試題:一個data member在定義的時候給他一個初始值,又在建構函式內賦另一個值,請問這個data member現在的值是多少?還有一些就是base class中的一個data member,多處賦值,然後問題最後它的值是多少?只要記住父類的建構函式在子類的建構函式之前執行,初始化參數在建構函式之前執行。

總之一句話initialization效率比在建構函式中賦值的效率高,如果data member很多且需要初始化成同一個值,而且效率不是那麼重要的話,可以在建構函式中用連等式賦值,這樣會清晰明了一點。效率不是永遠都放在第一位的,代碼的可讀性也很重要。82法則還記得嗎,我曾經因為多寫了一個if,在程式碼檢閱中被批評,理由就是100萬訪問的時候會影響效率,在兩家公司遇到過這種情況,什麼都是這個理由,百萬級訪問時會影響效率。

 

13:initialization list中的members初始化次序應該和其在class內的聲明次序相同

有時候data member的初始化是依賴別的data member的,那麼data member的初始化順序就必須弄清楚。data member的初始化順序與其在在initialization中出現的順序無關,只與它們定義的順序有關,先定義的data member會在initialization中先初始化,在destructor中後析構,先初始化的data member後析構,所以base data member後析構,跟棧中的變數一樣先定義的變數後析構一樣。如果類繼承多個類,那麼base data member的初始化順序由繼承的先後順序決定,先繼承的先初始化。

 

14:總上base class擁有virtual constructor

在繼承關係中,是調用父類的方法還是調用子類的方法,這個動態實現是由虛擬函數來決定的,含有虛擬函數的類都有一個虛擬函數表,如果父類中的方法是virtual的,如果沒有子類沒有覆寫這個方法那麼就是直接繼承過來,不管子類還是父類調用這個方法產生的結果是一樣的,如果子類覆寫了這個方法,那麼子類的虛擬表中存的就是子類方法的地址,如果一個父類的指標指向子類,如果方法被子類覆寫了,那麼調用的方法就是子類的方法,如果沒有覆寫那麼就是調用父類的方法。如果父類的方法不是virtual的,而且子類有一個一樣的方法,那麼父類的方法不會被子類覆寫,也就是說:父類指向子類的指標調用的將不會是子類的方法,而是父類的方法。同樣的父類的解構函式不是virtual的,那麼在delete 這個指標的時候就會出現不可知的情況,反正之類的建構函式是不會調用的,所以當你決定讓一個類成為父類,那麼就讓他的destructor為virtual。

但是也不需要讓每一個類的destructor成為virtual的,因為含有virtual方法的類都有一個指向virtual table的指標,會讓對象變大,如果對象本來就不太的話可能會出現成本翻倍的情況。只有當class中含有至少一個虛擬方法時才讓他的解構函式成為虛擬。

 

15:令operator =傳回*this的reference

先看一個等式:(A=B)=C,為了實現這種連等式,operator=肯定是不能返回void的,你可以為*void賦值,但是你不能為void賦值,operator=的返回方式不能是by value,如果是這樣的話,(A=B)返回的是A或是B的副本(正確的方式應該是A的reference),讓後將C的值賦給這個副本,而A、B的值卻沒有發生任何變化,這當然不是我們想要的,為了不讓這個副本的產生,傳回值必須是引用的方式,你可以用指標或是reference的方式返回,指標必須加個*麻煩,所以就是以reference的方式返回,那麼是返回A的引用還是B的引用,毫無疑問是A的,如果是B的,那麼執行的順序是這樣的,先將B賦給A,然後將C的值賦給B,賦值其實就是將右邊的值賦給左邊的傳回值嗎!這樣然不是我們想要的,我想要的其實是將B賦給A,然後將C在賦給A,當然寫這樣等式的絕對不是一個合格的程式員。所以operator=必須返回左邊對象的reference。我一直在想this為什麼是一個指標類型而不是一個reference呢?因為我們必須在operator=方法的最後一句加上return *this;而不是 return this;

 

16:在operator=中為所有data member賦值

這是毫無疑問的,如果有部分data member沒有被賦值,那麼被賦值的對象不就是殘廢了的嗎!有時候我們可能會在為類加data member的時候忘了再operator=中為它賦值了,或是在子類的operator=中忘了為父類的data member賦值。當然這些都不是重點,不記得不是問題,出錯了自然就知道了,為父類的data member賦值只需要在子類的operator=中加上Base::operator=(this);如果編譯器不支援調用base的operator=的話,可以做類型轉換啊,static_case<&Base>(*this)=Derived;我發現在類型轉換中,轉換成的類型一般都是指標或是引用,特別是轉換類型在左邊的時候就一定不能是by value的方式,會產生臨時變數,然後給臨時變數賦值,當然不是你想要的。在為指標類型的data member 賦值時要記住一點,是為指標所指的對象賦值,而不是為指標賦值,如果你讓data member一會兒指向這一會指向那的,可能會出現某些地址不可到達而出現記憶體泄露。

 

17:在operator =中檢查是否自己賦值給自己

在為一個對象賦值之前,先要確認這個對象是否動態配置記憶體,如果動態配置記憶體,先要回收掉這塊記憶體,不然當這個對象被賦值,指向別的地址後就會出現記憶體泄露,當然也不能一發現它動態配置記憶體了就把它先回收掉,因為可能出現把自己賦給自己的情況,你總不能因為把自己賦給自己之後,自己就莫名其妙的被回收了吧,所以在operator=中藥檢查是否自己賦值給自己。

 

Effective C++系列:

      Effective C++建構函式解構函式Assignment運算子

  Effective C++ 類與函數的設計和申明

   Effective C++物件導向與繼承

作者:陳太漢

部落格:http://www.cnblogs.com/hlxs/

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.