effective C++ 讀書筆記(下)

條款42:瞭解typename的雙重意義

      C++裏在模板定義中“typename”和“class”可以互換。但是有時候,必須使用typename:

 

      上面的代碼是一個簡單的函數模板,初看起來似乎沒有什麼問題,但是C::const_iterator卻不一定是一種類型(默認情況下,它在迭代器裏確實是一種類型,但具體到C,即使C是一種容器,C裏面的局部變量定義還是可能覆蓋這個類型定義),因此,此時必須在C::const_iterator前加上typename。

      還有一個地方要注意的是代碼裏的兩個局部變量iter和value。iter的類型是由模板參數決定的,這稱爲從屬名稱,對應的value稱爲非從屬名稱。如果從屬名稱在類裏面呈嵌套狀,則稱爲嵌套從屬名稱(即在類裏面定義的類)。

      typename不可以出現在派生類聲明裏的基類列表裏的嵌套從屬類型名稱之前,也不可以在成員初始化列表裏出現作爲基類修飾符。這句話聽着可能有點不好理解,看看下面的例子就瞭然了:

 

 

條款44:將與參數無關的代碼抽離templates

      templates生成多個classes和多個函數,所以任何template代碼都不該與某個造成膨脹的template參數產生相依關係。因非類型模板參數而造成的代碼膨脹,往往可以消除,做法是以函數參數或class成員變量替換template參數。因類型參數而造成的代碼膨脹,往往可降低,做法是讓帶有完全相同二進制表述的具現類型共享實現碼。

 

條款45:運用成員函數模板接受所有兼容類型

      同一個template的不同具現體之間並不存在什麼與生俱來的固有關係(意指如果以帶有base-derived關係的B,D兩類型分別具現化某個template,產生出來的兩個具現體並不帶有base-derived關係)。所以如果B爲D的基類,而template<B>和template<D>分別是兩個模板的具現體,那麼template<B>和template<D>並沒有繼承關係。

      看看下面的代碼:

 

      上面代碼的意思是,對任何類型T和任何類型U,這裏可以根據SmartPtr<U>生成一個SmartPtr<T>,因爲SmartPtr<T>有個構造函數接受一個SmartPtr<U>參數。這一類構造函數根據對象u創建對象t,而u和t的類型是同一個template的不同具現體,有時我們稱之爲泛化 copy構造函數。

      完成聲明之後,這個爲SmartPtr而寫的“泛化copy構造函數”提供的東西比我們需要的更多。是的,我們希望根據一個SmartPtr<Bottom>創建一個SmartPtr<Top>,卻不希望反過來。(書中Top是Bottom的基類。)假設SmartPtr遵循auto_ptr和tr1::shared_ptr所提供的榜樣,也提供一個get成員函數,返回智能指針對象所持有的那個原始指針的副本,那麼我們可以在“構造模板”實現代碼中約束轉換行爲,使它符合我們的期望:

 

      此處使用成員初值列來初始化SmartPtr<T>之內類型爲T*的成員變量,並以類型爲U*的指針作爲初值。這個行爲只有當“存在某個隱式轉換可將一個U*指針轉爲一個T*指針”時才能通過編譯,而那正是我們想要的。最終效益是SmartPtr<T>現在有了一個泛化copy構造函數,這個構造函數只在其所獲得的實參隸屬適當(兼容)類型時才它通過編譯。

 

條款46:需要類型轉換時請爲模板定義非成員函數

      在template實參推導過程中從不將隱式類型轉換函數考慮在內。

 

條款47:請使用traits classes表現類型信息

      這裏提到了traits,關於它的功能和使用,可參看我轉載的一篇博客。http://blog.csdn.net/nkorange/archive/2011/03/21/6265159.aspx

 

條款49:瞭解new-handle的行爲

      當operator new拋出異常以反映一個未獲滿足的內存需求之前,它會先調用一個客戶指定的錯誤處理函數,一個所謂的new-handle。爲了指定這個“用以處理內存不足”的函數,客戶必須調用set_new_handle,那是聲明於<new>的一個標準程序庫函數:

 

      new_handler是個typedef,定義出一個指針指向函數,該函數沒有參數也不返回任何東西。set_new_handler則是“獲得一個new_handler並返回一個new_handler”的函數。set_new_handler聲明式尾端的“throw()”是一份異常明細,表示該函數不拋出任何異常。

      set_new_handler的參數是個指針,指向operator new無法分配足夠內存時該被調用的函數。其返回值也是個指針,指向set_new_handler被調用前正在執行的那個new_handler函數。可以如下使用:

 

      如果operator new無法爲pBigDataArray分配足夠空間,outOfMem會被調用。

      設計一個良好的new-handler函數必須做以下事情:

  • 讓更多內存可被使用。 這便造成operator new內的下一次內存分配動作可能成功。實現此策略的一個做法是,程序一開始就分配一大塊內存,而後當new-handler第一次被調用,將它們釋還給程序使用。
  • 安裝另一個new-handler。 如果目前這個new-handler無法取得更多可用內存,或許它知道另外哪個new-handler有此能力。果真如此,目前這個new-handler就可以安裝另外那個new-handler以替換自己(只要調用set_new_handler)。下次當operator new調用new-handler,調用的將是新安裝的那個。(在new-handler裏改變自己)。
  • 卸載new-handler, 也就是將null指針傳給set_new_handler。一旦沒有安裝任何new-handler,operator new會在內存分配不成功時拋出異常。
  • 拋出bad_alloc。這樣的異常不會被operator new捕捉,因此會被傳播到內存索求處。
  • 不返回, 通常調用abort或exit。

      如果要爲類定製new-handler,只需令每一個class提供自己的set_new_handler和operator new即可。

 

      Widget的operator new主要做的事情是:調用set_new_handler,告知Widget的錯誤處理函數。這會將Widget的new-handler安裝爲global new-handler。然後調用global new-handler執行實際內存分配。在此之後(無論內存分配成功與否),舊的new-handler必須被恢復(如何做:存儲起原來的new-handler,在適當時機恢復,如析構或new-handler。)。下面是書上的代碼:

 

條款50:瞭解new和delete的合理替換時機

此處討論了齊位問題,不要讓指針隨便加上偏移量(比如double型指針被轉換爲char型,然後偏移若干字節)。

 

條款51:編寫new和delete時需固守常規

operator new的返回值十分單純。如果它有能力供應客戶申請的內存,就返回一個指針指向那塊內存。如果沒有那個能力,則拋出一個異常。operator new嘗試不止一次分配內存。

 

條款52:寫了placement new也要寫placement delete

placement new,placement delete。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章