《Effective C++》重點摘要(二)

《Effective C++》第二章:構造/析構/賦值運算

  1. C++默認編寫的函數。C++編譯器如果沒有發現以下函數,就會爲類生成一份默認版本的:
    1) default構造函數
    2) default析構函數
    3) copy構造函數
    4) copy assignment操作符(=運算符)
    前兩個函數並不總是產生,它只在編譯器需要的時候才產生出來。後兩個函數只保證以bitwise語義拷貝non-static成員,所以如果有指針、引用等non-static成員的類要麼拒絕編譯器產生的版本,要麼自行小心實現這兩個函數。
  2. 若不想使用編譯器自動生成的函數,就明確拒絕之。關鍵在於需要明確正在編寫的類是否允許這些默認函數的存在,如果確定不允許,那麼,在C++98中可以將其聲明爲private訪問權限的,而C++11中還可以將其聲明爲deleted。最好的做法是做一個通用的拒絕了編譯器生成默認函數的基類,讓不需要默認函數的類繼承自它。
  3. 爲多態基類聲明virtual析構函數。如果基類的析構函數不是virtual的,那麼基類的virtual table中不會有實際需要調用的析構函數,那麼刪除一個指向derived類指針的基類指針時,結果自然是不能確定的(通常是析構不完全)。
  4. 別讓異常逃離析構函數(如歸還資源)。如果析構函數中拋出了異常而沒有被處理,那麼析構函數該做的事情就很可能沒做完,即便這個被它傳播出來的異常被上層函數捕獲並處理也無法爲爲拋出異常的對象做完它該做的事情。所以如果虛構函數中出現異常,要麼abort程序,要麼自行處理異常(析構函數吞下異常)。abort掉程序往往讓人“心中一跳“,所以儘管“吞下異常”並不完美,也該如此。具體做法是爲可能拋出異常的操作編寫一個函數,並把這個函數暴露給客戶,給客戶一個自己處理異常的機會。如果客戶沒有自己處理,那麼就在析構函數裏替用戶做這件事情。
  5. 絕不要在構造和析構過程中調用virtual函數。構造函數總是先調用基類的構造函數,所以在構造函數裏調用虛函數在語義上總是不合理的,因爲此時的虛函數一點都不“虛”,調用的是父類版本,析構函數情況也是一樣,儘管總是後調用基類的析構函數,不,應該說,正是因爲這樣的調用順序,使得虛函數“不虛”。
  6. 令operator=返回一個reference to *this。這樣做,可以讓對象能夠做到連續賦值,讓你設計的類更像一個內置type。這樣返回一個引用是可行,因爲*this不是一個局部變量,當離開operator=函數作用域時,*this不會被析構。
  7. 在operator=中處理“自我賦值”。編寫operator=有四個基本要求:返回reference to *this,以const reference作爲參數傳遞方式,確保深淺拷貝語義合適、處理自我賦值,即處理客戶讓一個對象=本身的操作,在編譯器眼裏這是合法的表達式,但是運行時有可能出錯,解決之道是在copy之間先判斷出入參數(是個引用)所指對象的地址和this是否相等。還有一個更高的要求是保證“異常安全”,這通常可以通過copy-and-swap實現。
  8. 複製對象時,勿忘其每一部分。這個有什麼好說的,如果沒有做到,就不是複製!!注意拷貝成員變量時需要的拷貝語義(深、淺拷貝),也不要忘了所有基類裏的成員。也不要讓拷貝構造函數和copy assignment函數相互調用,寫一個共用的copy函數,讓拷貝構造函數和copy assignment函數調用它。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章