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

            思勤無邪

            上學時,因我年齡最小,個頭也最小,上課時,就像大猩猩堆里的猴一般。如今,這猴偶爾也把最近的一些情況寫在這里。

               :: 首頁 :: 聯系 :: 聚合  :: 管理
              132 Posts :: 1 Stories :: 178 Comments :: 0 Trackbacks

            公告

                 吾日常三省吾身,曰思、曰勤、曰無邪。

            積分與排名

            • 積分 - 183657
            • 排名 - 141

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

             

             

                  C++中封裝的概念是把一個對象的外觀接口同實際工作方式(實現)分離開來,但是C++的封裝是不完全的,編譯器必須知道一個對象的所有部分的聲明,以便創建和管理它。我們可以想象一種只需聲明一個對象的公共接口部分的編程語言,而將私有的實現部分隱藏起來。C + +在編譯期間要盡可能多地做靜態類型檢查。這意味著盡早捕獲錯誤,也意味著程序具有更高的效率。然而這對私有的實現部分來說帶來兩個影響:一是即使程序員不能輕易地訪問實現部分,但他可以看到它;二是造成一些不必要的重復編譯

                        然而C++并沒有將這個原則應用到二進制層次上,這是因為C++的類既是描述了一個接口同時也描述了實現的過程,示例如下:

             

            class CMyString
            {
            private:
             
            const int m_cch;
              
            char *m_psz;
            public:
              CMyString(
            const char *psz);
              
            ~CMyString();
              
            int Length() const;
              
            int Index(const char *psz) const;
            }

             

             

            CMyStirng對外過多的暴露了內存布局實現的細節,這些信息過度的依賴于這些成員變量的大小和順序,從而導致了客戶過度依賴于可執行代碼之間的二進制耦合關系,這樣的接口不利于跨語言跨平臺的軟件開發和移植。

             

            1.1.1      Handle-Body模式

             

                   解決這個問題的技術有一種叫句柄類( handle classes。有關實現的任何東西都消失了,只剩一個單一的指針“m_pThis”。該指針指向一個結構,該結構的定義與其所有的成員函數的定義都出現在實現文件中。這樣,只要接口部分不改變,頭文件就不需變動。而實現部分可以按需要任意更動,完成后只要對實現文件進行重新編譯,然后再連接到項目中。

                   下面是這項技術的簡單例子。頭文件中只包含公共的接口和一個簡單的沒有完全指定的類指針。

             

            class CMyStringHandle
            {
            private:
              
            class CMyString;
              CMyString 
            *m_pThis;
            public:
              CMyStringHandle (
            const char *psz);
              
            ~ CMyStringHandle ();
              
            int Length() const;
              
            int Index(const char *psz) const;
            }
            ;

            CMyStringHandle:: CMyStringHandle(
            const char *psz)
            :m_pThis(
            new CMyString(psz));
            {
            }


            CMyStringHandle::
            ~ CMyStringHandle()
            {
               delete m_pThis;
            }


            int CMyStringHandle::Length()
            {
              
            return m_pThis->Length();
            }


            int CMyStringHandle::Index(const char *psz)
            {
             
            return m_pThis->Index(psz);
            }

             

                這是所有客戶程序員都能看到的。
                        class CMyString;

            是一個沒有完全指定的類型說明或類聲明(一個類的定義包含類的主體)。它告訴編譯器,CMyString是一個結構的名字,但沒有提供有關該結構的任何東西。這對產生一個指向結構的指針來說已經足夠了。但我們在提供一個結構的主體部分之前不能創建一個對象。在這種技術里,包含具體實現的結構主體被隱藏在實現文件中。

             

                        在設計模式中,這就叫做Handle-Body 模式,Handle-Body只含有一個實體指針,服務的數據成員永遠被封閉在服務系統中。

            Handle-Body的布局結構永遠不會隨著實現類數據成員的加入或者刪除或者修改而導致Handle-Body的修改,即Handle-Body協議不依賴于C++實現類的任何細節。這就有效的對用戶的編譯器隱藏了這些細節,用戶在使用對這項技術時候,Handle-Body 接口成了它唯一的入口。

             

            然而Handle-Body模式也有自己的弱點:

            1、接口類必須把每一個方法調用顯示的傳遞給實現類,這在一個只有一個構造和一個析構的類來說顯然不構成負擔,但是如果一個龐大的類庫,它有上百上千個方法時候,光是編寫這些方法傳遞就有可能非常冗長,這也增加了出錯的可能性。

            2、對于關注于性能的應用每一個方法都得有兩層的函數調用,嵌套的開銷也不理想

            3、由于句柄的存在,依然存在編譯連接器兼容性問題。

             

            1.1.2        抽象接口

                        使用了“接口與實現的分離”技術的 Handle-Body 解決了編譯器/鏈接器的大部分問題,而C++面向對象編程中的抽象接口同樣是運用了“接口與實現分離”的思想,而采用抽象接口對于解決這類問題是一個極其完美的解決方案。

            1、抽象接口的語言描述:

                        class IMyString

                        {

                          virtual int Length() const = 0; //這表示是一個純虛函數,具有純虛函數的接口

                          virtual int Index(const char *psz) const = 0;

                        };

             

            2、抽象接口的內存結構:

                    抽象接口采用虛函數表來調用成員方法。 

            3、   抽象接口的實現代碼:

                          接口:

                        class IMyString

                        {

                          virtual int Length() const = 0; //這表示是一個純虛函數,具有純虛函數的接口

                          virtual int Index(const char *psz) const = 0;

                        }

                         實現:

                        class CMyStringpublic IMyString

                        {

                        private:

                         const int m_cch;

                          char *m_psz;

                        public:

                          CMyString(const char *psz);

                          virtual ~CMyString();

                          int Length() const;

                          int Index(const char *psz) const;

                        }

                        從上面采用抽象接口的實例來看,抽象接口解決了Handle-Body所遺留下來的全部缺陷。

            抽象接口的一個典型應用:

                        抽象工廠(AbstractFactroy

             

            1.2       多繼承與菱形缺陷、this跳轉等

            多重繼承是C++語言獨有的繼承方式,其它幾乎所有語言都秉承了單一繼承的思想。這是因為多重繼承致命的缺陷導致的:

            1.2.1        菱形缺陷

            當繼承基類時,在派生類中就獲得了基類所有的數據成員副本。假如類B A1A2兩個類多重繼承而來,這樣B類就包含A1、A2類的數據成員副本。

            考慮如果A1A2都從某基類派生,該基類稱為Base,現在繼承關系將出現菱形繼承關系。


            我們C++語言來描述這種繼承關系:

             

            class Base{ };

            class A1 :public Base { };

            class A2 :public Base { };

            class B :public A1,public A2 { }

             

            那么A1、A2都具有Base的副本。這樣B就包含了Base的兩個副本,副本發生了重疊,不但增加了存儲空間,同時也引入了二義性。這就是菱形缺陷,菱形缺陷的兩個缺陷:

            1、子對象重疊

            2、向上映射的二義性。

            菱形缺陷的其中一種解決辦法是使用虛擬繼承。

             

            C++世界里最廣泛的使用虛擬繼承解決菱形缺陷的應用便是標準C++的輸入/輸出iostream

             

             

             

            1.2.2        多重接口與方法名沖突問題(Siamese twins

                   對繼承而來的虛函數改寫很容易,但是如果是在改寫一個“在兩個基類都有相同原型”的虛函數情況就不那么容易了。

                   提出問題:

                   假設汽車最大速度的接口為ICar,潛艇最大速度的接口為 IBoat,有一個兩棲類的交通工具它可以奔跑在馬路上,也可以航行在大海中,那么它就同時擁有ICarIBoat兩種交通工具的最大速度特性,我們定義它的接口為ICarBoat;

                   class ICar

                   {

                         virtual int GetMaxSpeed()= 0;

                   };

                   class IBoat

                   {

                  virtual int GetMaxSpeed()= 0

                   }

               我們先對ICarBoat的接口做一個嘗試:

                   class CCarBoat

                   {

                          virtual int GetMaxSpeed();//既完成ICarGetMaxSpeed()接口方法又                                     //完成IBoat的接口方法?顯然不能夠

                   };

            解決問題:

                   顯然上面這個嘗試根本就無法成功,只用一個實現方法,怎么能夠求出這個ICarBoat交通工具奔跑在馬路上的最高時速,同時也能夠求出航行在大海上的最大航行速度呢。

                   上面這一問題矛盾就在一一個方法,卻需要兩個答案??磥?/span>ICarBoat要返回兩個答案就必須有兩個方法了,我們假設一個方法是求在陸地上奔跑的速度,名稱為GetCarMaxSpeed();另一個方法是求在大海上航行的最大速度,名稱為GetBoatMaxSpeed();那這兩個方法又怎么和GetMaxSpeed()接口方法聯系起來呢;

             

                   幸運的是,我們找到了解決辦法,而且解決辦法有很多種,下面介紹一下繼承法。

                   class IXCar :public ICar

                   {

                         virtual int GetMaxSpeed()

                          {

                                 GetCarMaxSpeed();

                          }

                          virtual int GetCarMaxSpeed() = 0;

                   }

                   class IXBoat:public IBoat

                   {

                          virtual int GetMaxSpeed()

                          {

                                 GetBoatMaxSpeed();

                          }

                          virtual int GetBoatMaxSpeed() = 0;

                   };

                   classCCarBoat: public IXCar , public IXBoat

                   {

                          virtual int GetCarMaxSpeed()

                          {

                                 … …

                          }

                          virtual int GetBoatMaxSpeed()

                          {

                                 … …

                          }

                   };

                  

             

                       

            1.2.3        this跳轉

            this跳轉是指的“對象同一性”問題。

                  在單一繼承的世界內,無論繼承關系怎么復雜,針對于同一對象,無論它的子類或者父類的this指針永遠相等。即如果 BA繼承,那么 對于一個已經實例化B類的對象 bObject,永遠有(B*&bObject ==(A*)&bObject 成立。

                  但是在多繼承的世界內,上面的等式就不能恒成立,對象的同一性受到了挑戰。

                  特別的是,在多繼承世界內如果菱形關系存在情況下,如果對于已經實例化B類的對象bObject; Base*)(A1*&bObject != Base*)(A2*&bObject 成立,當這種事情發生的時候我們就只能特殊處理了。這種情況在COM應用中處處都會發生。

             

            1.3       C++多態的兩種多態形式和區別

                 C++有兩種多態多態形式:

            1、編譯時刻多態,編譯時刻多態依靠函數重載或者模板實現

            2、運行時刻多態。運行時刻多態依靠需函數虛接口實現

            posted on 2007-07-21 16:18 思勤無邪 閱讀(2211) 評論(2)  編輯 收藏 引用 所屬分類: C++

            Feedback

            # re: 聊聊封裝、繼承 2007-08-09 20:55
            這好像是華為的培訓資料呀  回復  更多評論
              

            # re: 聊聊封裝、繼承 2007-12-12 09:09 虎子
            不錯,不錯  回復  更多評論
              

            久久无码人妻一区二区三区午夜| 久久国产欧美日韩精品| 国产成人综合久久久久久| 国产精品伊人久久伊人电影| 欧美麻豆久久久久久中文| 一级A毛片免费观看久久精品| 亚洲国产另类久久久精品小说| 精品一区二区久久久久久久网站| 免费一级欧美大片久久网| 久久精品无码午夜福利理论片| 91精品国产色综久久 | 亚洲午夜久久久久久噜噜噜| 久久99精品久久久久久久不卡 | 国产精品无码久久久久| 久久精品一本到99热免费| 99精品伊人久久久大香线蕉| 欧洲人妻丰满av无码久久不卡| 国内精品久久久久久久影视麻豆 | 久久无码国产专区精品| 丰满少妇人妻久久久久久4| 色欲综合久久躁天天躁蜜桃| 青草久久久国产线免观| 亚洲天堂久久精品| 久久99精品久久久久久久不卡| 无码任你躁久久久久久老妇| 久久国产精品免费一区| 丰满少妇高潮惨叫久久久| 久久人人妻人人爽人人爽| 久久久久久国产a免费观看黄色大片| 国产一级做a爰片久久毛片| 亚洲精品无码久久久久| 99蜜桃臀久久久欧美精品网站| 亚洲国产一成久久精品国产成人综合| 国产午夜福利精品久久| 2021久久精品国产99国产精品| 久久久久久午夜成人影院| 亚洲伊人久久大香线蕉综合图片| 精品国产青草久久久久福利| 精品国产乱码久久久久久人妻| 婷婷久久久亚洲欧洲日产国码AV | 国产91色综合久久免费|