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

隨筆-19  評論-2  文章-0  trackbacks-0

========================
Effective C++   繼承與面向對象設計
書作者:Scott Meyers
原筆記作者:Justin
========================
 
Item 32 : public 繼承意味著 is-a 關系
--------------------------------------------------
 tag: public inheritance 公有繼承  is-a
 
 每一個類型為 Derived 的對象同時也是一個類型為 Base 的對象,反之不成立。
 實際情況中很多“是一個”的體現并不那么純粹:大師說“鳥”都會飛,但是實際上是有不會飛的“鳥”的。

 在公有繼承中,有兩種辦法來解決這種“不純粹”:
  - 多重繼承。對于“鳥”的例子,設計一個“鳥”類,然后從中派生出一個“不會飛的鳥”類和一個“會飛的鳥”類,然后再在它們之中分別派生其他具體的“鳥”們。
  - 允許運行時出錯。還是“鳥”的例子,對于每一個“鳥”類的派生類,不管它是不是能飛,都會有個“飛”的函數。不同的是,能飛的“鳥”就直接飛了,不能飛的“鳥”則會在“飛”函數里說:”對不起,我不能飛,找別人去吧……”(所謂的運行時錯誤,runtime error)

 
Item 33 : 避免遮掩繼承而來的名稱
--------------------------------------------------
 tag: scopes
 ·derived classes 內的名稱會遮掩 base classes 內的名稱。
 ·可以使用 Using 聲明式或轉角函數 (forwarding functions)。
 
 先在本地域中查找(local scope,比如說函數內部)是否有該名字的定義,如果沒有找到
      往外一層名字域(比如說函數所在的類)中查找,如果沒有找到      
        再往外一層名字域(比如說函數所在類的父類)中查找,如果沒有找到
          繼續忘外一層名字域中查找(比如說函數所在類的父類的父類,等等),一直找到全局名字域(global scope)還是沒找到的話,就報告錯誤。

 在“洋蔥”的內部某層定義了和外部某層一樣名字的函數:使得位于內部的函數“屏蔽”了外部的同名函數(哪怕兩個函數擁有不同的參數表)。
  第一,在公有繼承中,上述的情況是不允許存在的,因為從定義上來說,公有繼承中的子類應該具備父類所有的特征和功能,應該“是一個”父類。
  第二,如果在上述情況中需要調用/訪問被“屏蔽”的函數/對象,有兩個方法可以采用:
   using。用using“聲明”過完整的名字后,就可以“看見”并使用這個函數/對象了。
   踢皮球函數(forwarding functions)。編寫一個函數,把真正的活踢給別人……
 兩種方法示例見下,Derived_0是有“屏蔽”問題的類,Derived_1和Derived_2分別是采用了第一種和第二種方法的類。

  class  Base  {
  public :
     virtual   void  func_1();
     virtual   void  func_1( int  param);
  }
 
  class  Derived_0:  public  Base  {
  public :
     virtual   void  func_1();
  }
 
  class  Derived_1:  public  Base  {
  public :
     using  Base::func_1;
     virtual   void  func_1();
  }
 
  class  Derived_2:  private  Base  {
  public :
     virtual   void  func_1();
     virtual   void  func_1( int  param)
     { Base::func_1(param);}
   }


Item 34 : 區分接口繼承和實現繼承
--------------------------------------------------
 tag: function interfaces,   function implementations.
 
 ·聲明一個 pure virtual 函數以讓 derived classes 只繼承函數接口
 ·聲明 普通virtual 函數以讓 derived classes 繼承該函數的接口和缺省實現
 ·聲明 non-virtual 函數以令 derived classes 繼承函數的接口及一份強制性實現。
 
  class  AClass  {
  public :
     virtual   void  interface_1()  =   0 ;
     virtual   void  interface_2()
      { /* the default implementation..*/ }
      void  interface_3()
      { /* the compulsory implementation..*/ }
  // ..
  } ;

  class  AClassDerived  {
  public :
     virtual   void  interface_1()
      { /* OK you have to implement this..*/ }
      virtual   void  interface_2()
      { /* you can, but don't have to implement this..*/ }
     //  void interface_3()
     //  {you can't implement your own..}
  } ;

  class  AClass  {
  public :
     virtual   void  interface_1. 5 ()  =   0 ;
  protected :
     void  default_interface_1. 5 ()
      { /* ..*/ }
  } ;

  class  AClassDerived  {
  public :
     virtual   void  interface_1. 5 ()
      {
        // you can either do this
        default_interface_1. 5 ();
        // or implement in your own way..
     }
  } ;
 
 
Item 35 : 考慮 virtual 函數意外的其他選擇
--------------------------------------------------
 tag: tr1::function  tr1::bind (祥書上實例),
   Template Method,  Strategy ,
 ·使用 non-virtual interface(NVI)手法,即 Template Method 設計模式的一種特殊形式。以public non-virtual成員函數包裹較低訪問性的virtual函數
 ·將virtual函數替換為“函數指針成員變量”,即Strategy設計模式的一種分解表現形式
 ·以tr1::function成員變量替換virtual函數,因而允許使用任何可調用物(callable entiry)搭配一個兼容于需求的簽名式。也是Strategy設計模式的某種形式。
 ·將繼承體系內的 virtual函數替換為另一個繼承體系內的 virtual函數。即Strategy設計模式的傳統實現手法。
 ----------------------------------
 一: Non-Virtual Interface 手法實現 Template Method 模式
 
 :讓客戶通過 public non-virtual 成員函數間接調用 private virtual 函數,此即NVI手法,Template Method 設計模式的一種表現形式。
   這個 public non-virtual 函數成為 virtual 函數的外覆器(wrapper)。
   優點在于可在 wrapper 中做一些事前工作和事后工作。
   NVI 手法中virtual function 不一定得是 private.
 
 ----------------------------------
 二: Function Pointers 實現 Strategy 模式
 
 :這種方法的實質,就是把接口函數的實現拿到了類之外。類之中只聲明接口的形式,只定義一個函數指針。真正干活的函數(實現)都不是類的成員。
 這樣做帶來了一定的靈活性,具體采用哪種實現與類的繼承關系是獨立無關聯的;同時,非類成員函數也有局限性:無法訪問類的非公有成員。如果把函數定義為友元或利用公有函數輸出私有成員,又會破壞原設計的 封裝。如下代碼所示:

  class  AClass
  {
     public :
        typedef  void *(Interface)( /* param.. */ );
        explicit  AClass( Interface pint  =  defaultInterface) : pInterface(pint)
        {}
     private :
        Interface pInterface;
  } ;
 在構造AClass對象的時候即可指定Interface的真身,雖然,它無法直接訪問AClass的非公有成員。
 指針在C++里簡單一些,更推崇用對象(如智能指針tr1)來管理接口函數。(是不是想到item13?:))

 原理和函數指針是一樣的,只不過因為用了對象來管理資源,使得應用更加靈活。當然,要付出更多一點的代碼體積和運行時間代價。

  class  AClass
  {
   //  all are the same with the funtion pointer version
   //  except for:
     typedef std::tr1::function < void ( /* param.. */ ) >  Interface;
  } ;
 
  ---------------------------------
  三: 古典策略模式實現,也是我覺得比較漂亮且容易理解的實現方式。

 用兩個類搞定:
  class AInterface
  {
     public:
        virtual void DoInterface(/* param.. */);
  };

  AInterface defaultInterface;

  class AClass
  {
     public:
        explicit AClass(AInterface * pinter = &defaultInterface) : pInter(pinter)
        {}
        void TryInterface()
        {
           pInter->DoInterface();
        }
     private:
        pInterface * pInter;
  };


 
Item 36 :絕不重新定義繼承而來的non-virtual函數
--------------------------------------------------
 tag:
 
 任何情況下都不該重新定義一個繼承而來的non-virtual函數,

 
Item 37 :絕不重新定義繼承而來的 缺省參數值
--------------------------------------------------
 tag: 靜態類型(static type)  動態綁定  靜態綁定  前期綁定  后期綁定  bound bind
 
 virtual 函數為動態綁定(dynamically bound),而缺省參數值卻是靜態綁定(statically bound)。
 靜態類型 ( static type ):
  在程序中被聲明時所采用的類型。
  Shape* ps;      //靜態類型為 Shape*, ps沒有動態類型,因為為指向任何對象。
  Shape* pc = new Circle;   //靜態類型為 Shape*, pc的動態類型為 Circle*
  Shape* pr = new Rectangle;  //靜態類型為 Shape*, pr的動態類型為 Rectangle*
 動態類型 ( dynamic type ) :
  目前所指對象的類型,
  virtual 函數系動態綁定而來,調用一個virtual函數時,調用哪一份實現代碼,取決于發出調用的那個對象的動態類型。
  
  class  AClass
  {
     public :
        virtual   void  func(  int  param  =   123  )
        {
           // ..
        }
  } ;

  class  AClassDerived :  public  AClass
   {
     public :
        //  problematic overwriting the default parameter..
         virtual   void  func(  int  param  =   456  )
         {
           // ..
        }
  } ;
  int  main()
   {
    AClass  *  pA  =   new  AClassDerived;
    pA -> func();
  }
 由于函數默認參數的靜態綁定特性,pA->func()執行時param事實上被賦予了123,而非子類中期望的456,雖然接下來執行的是子類的函數實現……
 C++考慮到執行效率和復雜性方面的代價,規定了只能是靜態綁定的。
 
 解決方式:
  可以用非虛函數接口(NVI)來解決這個問題,看代碼
 class AClass
 {
    public:
       void func(int param = 123)
       {
          funcImpl(param);
       }
    private:
       virtual void funcImpl( int real_param ) = 0;
    //..
 };

 class AClassDerived : public AClass
 {
    private:
       virtual void funcImpl( int real_param )
       {
          //do whatever you feel like to do here..
       }
    //..
 };


 
Item 38 :通過 composition 塑模出 has-a 或 is-implemented-in-terms-of
--------------------------------------------------
 tag: has-a  composition  is-implemented-in-terms-of 、根據list實現set(祥書上例)
 
 ·復合(composition)的意義同public繼承完全不同。
 ·在應用域(application domain),   復合意味著 has-a,
   在實現域(implementation domain),復合意味著 is-implemented-in-terms-of
 
 composition是類型之間的一種關系,即某種類型的對象內含它種類型的對象。
 composition、 layering分層、  containment內含、aggregation聚合、 embedding內嵌
 composition意味著has-a 或 is-implemented-in-terms-of。

 
Item 39 :明智而審慎地使用 private 繼承
--------------------------------------------------
 tag: private inheritance
 
 Private inheritance意味著 implemented-in-terms-of.只有實現部分被繼承,接口部分被略去。
 若 D 以 private 形式繼承 B, 意思是 D 對象根據 B 對象實現而得。
 
 公有繼承中的子類對象是可以被轉換為它的父類對象的(“是一個”的關系),而私有繼承中這種轉換是不成立的。
 另外一點,私有繼承中父類的所有公有和保護成員(public和protected)到了子類中,都變成了私有成員。

 因為上面的特性,私有繼承并不滿足“是一個”模型的需要。更可憐的是,私有繼承并不能代表一種設計思路(公有繼承代表了“是一個”的模型設計),而僅僅是“有一個”模型的一種實現手段(私有繼承過來的所有成員都是私有的,從這個角度來說它就只是“實現”)。
 另一種手段大師在Item38中有提過,就是用類成員的方式來構造,名曰composition。
 既然兩者都能實現“有一個”模型,那么如何選擇呢?能用composition就用composition,必需私有繼承的時候方才私有繼承。

 比如我們有個AClass:class AClass{
 public:
    virtual void Interface_1(/*..*/);
 };
 以下為私有繼承:class BClass : private AClass{
 private:
    virtual void Interface_1(/*..*/);
 //..
 };

 而下面的composition可以達到一樣甚至更好的效果:class AnotherAClass: public AClass{
 public:
    virtual void Interface_1(/*..*/);
 //..
 };

 class DClass{
 private:
    AnotherAClass* a;
 //..
 };

 BClass和DClass都實現了“有一個”,但相比之下還是能分辨出長短:

 DClass中的AnotherAClass是私有成員,除了它自己沒有人能夠訪問修改;而私有繼承的BClass不能保證其“擁有”的AClass實現部分不會被第三者修改,即使是私有繼承來的。(為什么這么說?看下去……)
 BClass私有繼承了AClass,相當于它“有了一個”AClass可以用,可以玩。AClass中的公有/保護成員都變成了BClass的人,但是在享受使用這些成員的同時,BClass還要承擔提供這些成員給別人服務的義務。
 ITEM35中曾經提到:虛擬函數機制和公有/私有/保護體制是沒有任何關系的。因此在例子中的Interface_1有可能在以下的情況中被替代然后“調用”:
 一個CClass公有繼承了BClass
 CClass定義了自己的Interface_1版本
 有一個BClass的指針,指向一個CClass的對象,某個操作中調用了Interface_1(CClass的實現版本)

 這時候BClass可能要有意見了:它的作者并沒有打算讓它的繼承者修改BClass版本的Interface_1,但事實是CClass違背了它的意志!
 很曲折哈?希望我下次讀的時候還能看懂@#¥%
 DClass由于只是定義了一個指向AnotherAClass的指針,那么在定義DClass的文件中就不需要include AClass或AnotherAClass的頭文件。于是就避免了編譯依賴(compilation dependecies)
 而BClass因為是繼承了AClass,在BClass的文件中就需要加上AClass的頭文件,也就不可避免的產生了編譯時的依賴。
 由此看來,絕大部分情況下,組合方式(composition)是要優于私有繼承的。之所以說“絕大部分”,是因為大師說了:
 對于EBO(Empty Base Optimization)的情況,私有繼承就顯現出了它的優勢。
 所謂EBO就是這樣的一種情況,有一種特殊的類,它沒有非靜態數據成員(non-static data member),也沒有虛函數(于是不會需要空間存儲虛表)。
 所以這樣的一種類其實不占用任何空間,不過因為C++不允許0字節的對象存在,而且很多編譯器都會添加額外的空間來實現字節對齊,于是這種特殊的類的實際大小應該是1個char對象的大小。
 在這種類中,往往會有很多typedef,enum,靜態數據成員或者是非虛函數。所以他們還是有價值的。
 需要在“有一個”關系中利用這種類的時候,如果采用composition,那么根據上面的結論,就需要付出額外的空間來“存放”這個本來不占空間的類。
 然而如果是私有繼承呢,就可以避免這種情況。

 
Item 40 :明智而審慎地使用多重繼承
--------------------------------------------------
 tag: Multiple Inheritance, MI
 
 MI 的優與劣。

 MI 的第一個問題就是名字沖突, 最經典的例子就是鉆石問題 (diamond problem)。
 設想 A 中有一個函數叫做 GetName(), B 和 C 中都將有這一函數成員,這個時候 D::GetName() 的真正實現是來自 B 的還是 C 的呢?二義性出現了 (ambiguity) 。

 不過如果真的發生了這種情況,要解決的方法也不是沒有,可以這樣做:

 D d;
 d.B::GetName(); //Calling B's implementation

 另外一個高階一點的方法叫做虛繼承 (virtual inheritance) 。對于在虛擬繼承中的父類,其中的成員都保證不會在后面的子類中出現二義現象 (ambiguity) 。

 class A
 {
    public:
       void GetName();
 };
 class B : virtual public A { };
 class C : virtual public A { };
 class D : public B, public C { }
 D d;
 d.GetName(); //there is no ambiguity here.

 但是虛繼承不是沒有代價的,大師說這種技術會使得最終代碼變得更大,訪問虛擬繼承中的父類成員也會變得更慢一些。

 這個也不難理解。和空間換時間一樣,和不給牛吃草牛就不干活一樣。 ( 另外的一個代價我還沒能完全理解透徹:書上說因為虛繼承中基類的初始化是由繼承關系中最底層的子類負責的,因此對于這些最底下的 “ 嫡孫 ” 類來說,就不是那么方便了 )

 于是大師建議只有在必要的時候才使用虛繼承,而在虛繼承中的基類里也不要放置數據成員,這樣就不用擔心初始化的問題了。

 不過存在就是合理,還是有需要用到 MI 的時候。一個在書中提到的使用 MI 的情形是:當需要從一個類 AClass 中繼承接口,又需要從另外一個類 BClass 中繼承實現細節時,就可以考慮在公有繼承 AClass 的同時又私有繼承 BClass 。道理大致就是這樣,就不編造程序畫蛇添足了。

 總結一下: MI 比 SI(Single Inheritance) 要復雜容易出錯 ( 比如說鉆石問題 ) ,即使可以用虛繼承來解決鉆石問題,但是其帶來的代碼體積增大,訪問效率下降以及初始化問題還是不能忽視的。最后話說回來,需要用到 MI 的時候,小心點用便是

posted on 2010-03-15 22:54 Euan 閱讀(431) 評論(0)  編輯 收藏 引用 所屬分類: C/C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久精品国产亚洲一区二区| 欧美一区二区三区在线看| 久久婷婷国产综合尤物精品| 亚洲一区二区不卡免费| 国产精品久久久久久久9999| 欧美亚洲三区| 欧美一区二区三区视频在线观看 | 欧美暴力喷水在线| 亚洲黄色在线看| 99精品视频免费| 国产精品一区二区三区四区 | 麻豆精品在线观看| 欧美成人蜜桃| 亚洲欧美日韩一区| 久久久久国色av免费看影院| 99re热这里只有精品视频| 一区二区三区视频在线 | 国产精品爽爽爽| 免费成人性网站| 欧美日韩一区在线观看视频| 欧美专区在线观看| 欧美刺激午夜性久久久久久久| 亚洲午夜精品久久久久久app| 欧美一区二区三区四区夜夜大片| 亚洲国产美女精品久久久久∴| 一区二区三区蜜桃网| 狠狠入ady亚洲精品经典电影| 亚洲欧洲日韩在线| 国产欧美一区二区精品婷婷| 亚洲高清精品中出| 国产一区二区三区免费观看| 日韩视频中文字幕| 亚洲国产日韩欧美一区二区三区| 亚洲宅男天堂在线观看无病毒| 亚洲国产99| 欧美一区永久视频免费观看| 一本高清dvd不卡在线观看| 久久精品视频免费| 91久久久国产精品| 久久精品99国产精品酒店日本| 夜夜爽99久久国产综合精品女不卡| 欧美一区二区三区啪啪| 中日韩男男gay无套| 美日韩在线观看| 久久激情网站| 国产精品视频yy9099| 亚洲日本成人在线观看| 国产一区二区三区日韩| 一区二区三区四区国产| 9i看片成人免费高清| 欧美成人免费小视频| 久久国内精品视频| 国产欧美日本一区视频| 亚洲视频播放| 亚洲一区激情| 欧美三级日本三级少妇99| 亚洲国产成人精品女人久久久 | 亚洲国产精品久久久久秋霞影院| 韩国在线视频一区| 久久www成人_看片免费不卡 | 亚洲国产高清在线| 久久女同互慰一区二区三区| 狂野欧美激情性xxxx| 国内视频精品| 久久久久.com| 美腿丝袜亚洲色图| 黄色日韩精品| 久久躁日日躁aaaaxxxx| 欧美国产一区二区| 亚洲精品美女久久7777777| 欧美电影在线观看完整版| 亚洲国产精品久久久久| 亚洲理伦在线| 国产精品豆花视频| 欧美在线免费观看亚洲| 久热精品视频| 亚洲精品久久在线| 欧美区在线播放| 制服丝袜亚洲播放| 欧美一区在线直播| 狠狠狠色丁香婷婷综合激情| 久久久久久亚洲精品杨幂换脸| 久久婷婷色综合| 亚洲狠狠丁香婷婷综合久久久| 欧美大学生性色视频| 一区二区三区 在线观看视频 | 在线成人av| 欧美日韩亚洲国产一区| 亚洲性感美女99在线| 久久精品人人做人人爽| 亚洲国产精品久久久久秋霞不卡| 欧美日本中文字幕| 午夜精品久久久久久久蜜桃app| 欧美成人免费小视频| 亚洲少妇一区| 亚洲成人在线免费| 欧美视频日韩| 久久综合色天天久久综合图片| 亚洲精品资源| 久久久综合香蕉尹人综合网| 永久91嫩草亚洲精品人人| 亚洲国产精彩中文乱码av在线播放| 中文有码久久| 蜜桃久久精品乱码一区二区| 夜夜狂射影院欧美极品| 国内外成人免费激情在线视频网站 | 亚洲一区综合| 亚洲高清视频中文字幕| 欧美在线精品免播放器视频| 亚洲伦理在线观看| 红桃av永久久久| 国产精品日韩一区| 欧美日韩精品一区视频| 久久综合九色九九| 亚洲综合999| 9色精品在线| 亚洲激情网站| 欧美v亚洲v综合ⅴ国产v| 欧美一区二区黄| 亚洲一区在线免费| 99在线|亚洲一区二区| 亚洲国产精品精华液网站| 国内成人自拍视频| 国产欧美一区二区三区沐欲| 欧美三区在线观看| 欧美喷水视频| 欧美成人69av| 美国成人直播| 久久久久久久久岛国免费| 午夜视频精品| 亚洲欧美日韩精品久久| 亚洲免费福利视频| 日韩亚洲精品电影| 亚洲另类视频| 亚洲精品一区二区三区在线观看 | 欧美一区二区三区免费视频| 亚洲色在线视频| 亚洲一区免费看| 亚洲精品一级| 亚洲少妇自拍| 亚洲欧美激情视频| 亚洲一区精品视频| 新67194成人永久网站| 亚洲综合国产| 午夜在线一区二区| 久久成人资源| 巨胸喷奶水www久久久免费动漫| 久久久久在线| 欧美69视频| 欧美黄污视频| 亚洲精品欧美一区二区三区| 日韩视频免费观看| 亚洲小少妇裸体bbw| 欧美在线|欧美| 你懂的亚洲视频| 欧美日韩一区二区免费视频| 国产精品美女久久久浪潮软件| 国产精品国产三级国产aⅴ无密码| 国产精品日韩一区二区三区| 狠狠久久综合婷婷不卡| 亚洲人成毛片在线播放女女| 一区二区三区精品国产| 欧美一区二区三区精品电影| 久久男人资源视频| 亚洲欧洲综合另类| 亚洲一二三四久久| 久久国产精品久久久| 欧美高清在线视频观看不卡| 欧美日韩综合一区| 国产在线精品一区二区中文 | 欲色影视综合吧| 一区二区三区四区五区视频| 欧美一区二区三区免费视| 午夜精品在线观看| 亚洲精品久久久久久下一站| 一区二区三区欧美| 欧美中文字幕在线播放| 欧美裸体一区二区三区| 国产欧美视频一区二区三区| 91久久精品国产91久久| 香蕉成人伊视频在线观看| 欧美激情乱人伦| 羞羞漫画18久久大片| 欧美精品一线| 在线精品国精品国产尤物884a| 在线亚洲观看| 模特精品裸拍一区| 亚洲男人av电影| 欧美国产日产韩国视频| 国产一区二区在线观看免费播放 | 亚洲色图综合久久| 欧美国产极速在线| 欧美一区影院| 国产精品视频一| 在线亚洲激情| 亚洲国产精品毛片| 久久久久久有精品国产| 国产精品一卡二| 亚洲天堂网在线观看| 亚洲欧洲视频|