雞啄米在編程入門系列上一節中講了公有繼承方式的訪問屬性,這一節接着講剩下的兩種繼承方式:保護繼承和私有繼承。

       一.保護繼承

       在保護繼承方式中,基類的公有成員和保護成員被派生類繼承後變成派生類的保護成員,而基類的私有成員在派生類中不能訪問。因為基類的公有成員和保護成員在派生類中都成了保護成員,所以派生類的新增成員可以直接訪問基類的公有成員和保護成員,而派生類的對象不能訪問它們,上一講雞啄米說過,類的對象也是處于類外的,不能訪問類的保護成員。對基類的私有成員,派生類的新增成員函數和派生類對象都不能訪問。

       通過上述保護繼承的講解,相信大家對類的保護成員的訪問屬性有更加深刻的理解了。假設A類是基類,B類是從A類繼承的派生類,A類中有保護成員,則對派生類B來說,A類中的保護成員和公有成員的訪問權限是一樣的。而對A類的對象的使用者來說,A類中的保護成員和私有成員都一樣不能訪問。可見類中的保護成員可以被派生類訪問,但是不能被類的外部對象(包括該類的對象、一般函數、其他類等)訪問。我們可以利用保護成員的這個特性,在軟件開發中充分考慮數據隐藏和共享的結合,很好的實現代碼的複用性和擴展性。

       雞啄米舉個簡單的例子讨論下保護成員的訪問屬性。

       class Base
       {
       protected:
                  int x;           // 基類的保護成員
       };
       int main()
       {
                 Base base;
                 base.x = 0;      // 編譯報錯
                 return 0;
       }

       這段代碼在編譯的時候會報錯,錯誤就出在通過對象base訪問保護成員x時,就像上面講的,對Base類的對象base的使用者來說,Base類中的保護成員x和私有成員的訪問特性是一樣的,所以對象base不能訪問x,這樣跟使用私有成員一樣通過保護成員實現了數據的隐藏。

        class Base
        {
        protected:
                     int x;           // 基類的保護成員
        };
       class Child : public Base
        {
       public:
                     void InitX();
        };
        void Child::InitX()
        {
                     x = 0;
        }

       對上面的派生類Child來說,基類Base中的保護成員x和公有成員的訪問權限一樣,所以Child類的成員函數InitX可以訪問Base類的保護成員x。

雞啄米:C++編程入門系列之三十八(繼承與派生:派生類對基類成員的訪問控制之保護繼承與私有繼承)

       二.私有繼承

       在私有繼承方式中,基類的公有成員和保護成員被派生類繼承後變成派生類的私有成員,而基類的私有成員在派生類中不能訪問。派生類的新增成員可以直接訪問基類的公有成員和保護成員,但是在類的外部通過派生類的對象不能訪問它們。而派生類的成員和派生類的對象都不能訪問基類的私有成員。

       講到這裡,我們看到不管是保護繼承還是私有繼承,在派生類中成員的訪問特性都是一樣的,都是基類的公有和保護成員可以訪問,私有成員不能訪問。但是派生類作為基類繼續派生新類時,兩種繼承方式就有差别了。例如,A類派生出B類,B類又派生出C類,如果B類是以保護繼承方式從A類繼承的,則A類的公有成員和保護成員都成為B類的保護成員,再由B類派生出C類時,原來A類的公有成員和保護成員間接繼承到C類中,成為C類的保護成員或者私有成員(C類從B類公有繼承或保護繼承時為前者,私有繼承時為後者),所以C類的成員可以間接訪問A類的公有成員和保護成員。但是如果B類是以私有繼承方式從A類繼承的,則A類的公有成員和保護成員都成為B類的私有成員,A類的私有成員不能在B類中訪問,B類再派生出C類時,原來A類的所有成員都不能在C類中訪問。

       由以上分析得出,私有繼承使得基類的成員在其派生類後續的派生中不能再被訪問,中止了基類成員繼續向下派生,這對代碼的複用性沒有好處,所以一般很少使用私有繼承方式。

       雞啄米将上一講中的例子由公有繼承改為私有繼承,繼而更形象的說明私有繼承的特性。

        #include <iostream>
        using namespace std;
        class Base            // 基類Base的聲明
         {
         public:               // 公有成員函數
                 void SetTwo(int a, int b)  { x=a; y=b; }
                 int GetX()   { return x; }
                 int GetY()   { return y; }
        private:              // 私有數據成員
                 int x;
                 int y;
        };
        class Child : private Base    // 派生類的聲明,繼承方式為私有繼承
        {
        public:                      // 新增公有成員函數
                void SetThree(int a, int b, int c)  { SetTwo(a, b); z=c; }
                int GetX()   { return Base::GetX(); }
                int GetY()   { return Base::GetY(); }
                int GetZ()   { return z; }
        private:                     // 新增私有數據成員
                int z;
        };
        int main()
        {
               Child child;           // 聲明Child類的對象
               child.SetThree(1, 2, 3); // 設置派生類的數據
               cout << "The data of child:"<<endl;
               cout << child.GetX() << "," << child.GetY() << "," << child.GetZ() << endl;
               return 0;                                                        
        }

       程序運行結果:

       The data of child:
       1,2,3

       Child類從Base類中私有繼承,Base類中的公有成員SetTwo()、GetX()和GetY()成為Child類的私有成員,在Child類中可以直接訪問它們,例如Child類的成員函數SetThree()中直接調用了Base類的公有成員函數SetTwo()。Base類的私有成員x和y在Child類中不能訪問。在外部通過Child類的對象不能訪問Base類的任何成員,因為Base類的公有成員成為Child類的私有成員,Base類的私有成員在Child類中不能訪問。那麼Base類的作為外部接口的公有成員SetTwo()、GetX()和GetY()都被派生類Child隐藏起來,外部不能通過Child類的對象直接調用。

       如果我們希望派生類也提供跟基類中一樣的外部接口怎麼辦呢?我們可以在派生類中重新定義重名的成員。上面的Child類就重新定義了公有成員函數GetX()和GetY(),函數體則隻有一個調用基類函數的語句,照搬了基類函數的功能。因為派生類中重新定義的成員函數的作用域位于基類中同名函數的作用域範圍的内部,根據前面可見性中講的同名覆蓋原則,調用時會調用派生類的函數。通過這種方式可以對繼承的函數進行修改和擴展,在軟件開發中經常會用到這種方法。

       大家可能發現,main函數的函數體跟上一講例子中的完全相同,但實際上在程序執行的時候是不同的,這裡調用的函數GetX()和GetY()都是派生類Child的函數,由于是私有繼承,基類Base中的同名函數都不能通過Child類的對象訪問。

       這個例子跟上一講的例子相比,Base類和主函數main的函數體都沒有修改,雞啄米隻修改了派生類Child的聲明,但Child類的外部接口沒有改變。到此大家有沒有認識到面向對象設計封裝性的優越性?我們可以根據需要調整類的内部數據結構,但隻要保持外部接口不變,那我們做的類的内部調整對外部就是透明的,不會影響到程序的其他部分。這充分體現了面向對象設計的可維護性和可擴展性。

       關于派生類以各種繼承方式派生後對基類成員的訪問控制就講到這裡了,幾種繼承方式中最常用的就是公有繼承了,但保護繼承和私有繼承大家也有必要學習一下,對于提升内功很有幫助。有問題到雞啄米博客留言交流吧。

 

除非特别注明,雞啄米文章均為原創
轉載請标明本文地址:http://dnsf85p.top/software/98.html
2011年12月9日
作者:雞啄米 分類:軟件開發 浏覽: 評論:13