天天看點

5、繼承與派生2-通路控制

但是通過基類的對象,就隻能通路該基類的公有成員。

由于繼承所導緻的原來具有不同通路屬性的基類成員在派生類中的通路屬性也有所不同,這裡說的通路來自兩個方面:一是派生類中的新增成員通路從基類繼承的成員;二是在派生類外部(非類族内的成員),通過派生類的對象通路從基類繼承的成員。

1、公有繼承

當類的繼承方式為公有繼承時,基類的公有和保護成員的通路屬性在派生類中不變,而基類的私有成員不可直接通路。---也就是說基類的公有成員和保護成員被繼承到派生類中通路屬性不變,仍作為派生類的公有成員和保護成員,派生類的其他成員可以直接通路他們。在類族之外隻能通過派生類的對象通路從基類繼承的公有成員,而無論是派生類的成員還是派生類的對象都無法直接通路基類的私有成員。

eg:

point類公有繼承。

在這個例子中從point類派生出新的Rectangle(矩形)類。矩形是由一個點加上長、寬構成,矩形的點具備了point類的全部特征,同時矩形自身也有一些特點,這就需要在繼承point類的同時添加新的成員。

程式的頭檔案部分

//Rectangle.h
class Point  //基類Point類的定義
{
  public:
    void InitP(float xx=0,float yy=0){X=xx;Y=yy;}
    void Move(float xOff,float yOff){X+=xOff;Y+=yOff;}
    float GetX(){return X;}
    float GetY(){return Y;}
  private:
    float X,Y;
};
class Rectangle:public Point             //派生類定義部分
{
  public:        //新增公有函數成員
    void InitR(float x,float y,float w,float h)
    {InitP(x,y);W=w;H=h;} //調用基類公有成員函數
    float GetH(){return H;}
    float GetW(){return W;}
  private:    //新增私有資料成員
    float W,H;
};
//End of Rectangle.h      

首先定義了基類point,派生類Rectangle繼承了point類的全部成員

因為繼承方式為公有形式,故基類中的公有成員在派生類中通路屬性保持原樣,派生類的成員函數及對象可以通路到基類的公有成員。基類原有的外部接口變成了派生類的外部接口的一部分。當然派生類自己新增的成員之間都是可以互相通路的。

下面是程式的主函數部分

#include<iostream>
#include<cmath>
#include"rectangle.h"
using namespace std;
int main()
{
  Rectangle rect;//聲明Rectangle類的對象
  rect.InitR(2,3,20,10);//設定矩形的資料
  rect.Move(3,2);//移動矩形位置
  cout<<"The data of rect(X,Y,W,H):"<<endl;
  cout<<rect.GetX()<<","
    <<rect.GetY()<<","
    <<rect.GetW()<<","
    <<rect.GetH()<<endl;
}      

2、私有繼承

基類中的公有成員和保護成員都以私有成員身份出現在派生類中,而基類的私有成員在派生類中不可直接通路。也就是說基類的公有和保護成員被繼承後作為派生類的私有成員,派生類的其他成員可以直接通路他們,但是在類族外部通過派生類的對象無法直接通路他們。無論是派生類的成員還是通過派生類的對象,都無法直接通路從基類繼承的私有成員。

經過私有繼承之後,所有基類的成員都成為了派生類的私有成員或不可直接通路的成員,如果進一步派生的話,基類的全部成員就無法在新的派生類中被直接通路。是以,私有繼承之後,基類的成員再也無法在以後的派生類中直接發揮作用,實際是相當于中止了基類功能的繼續派生。

eg:point類私有繼承

class Point //基類Point類的定義
{
public:
  void InitP(float xx=0,float yy=0){X=xx;Y=yy;}
  void Move(float xOff,float yOff){X+=xOff;Y+=yOff;}
  float GetX(){return X;}
  float GetY(){return Y;}
private:
  float X,Y;
};class Rectangle:private Point             //派生類定義部分
{
  public:     //新增公有函數成員
    void InitR(float x,float y,float w,float h)
      {InitP(x,y);W=w;H=h;} //調用基類公有成員函數
    float GetX(){return Point::GetX();}
    float GetY(){return Point::GetY();}
    float GetH(){return H;}
    float GetW(){return W;}
  private:    //新增私有資料成員
    float W,H;};      

在私有繼承情況下,為了保證基類的一部分外部接口特征能夠在派生類中也存在,就必須在派生類中重新聲明同名的成員。比如這裡在派生類中重新聲明了Move,GetX,GetY等函數,利用派生類對基類成員的通路能力,把基類的原有成員函數的功能照搬過來。

程式主函數部分

#include<iostream>
#include<cmath>
#include"rectangle.h"
using namespace std;
int main()
{
  Rectangle rect;//聲明Rectangle類的對象
  rect.InitR(2,3,20,10);//設定矩形的資料
  rect.Move(3,2);//移動矩形位置
  cout<<"The data of rect(X,Y,W,H):"<<endl;
  cout<<rect.GetX()<<","
    <<rect.GetY()<<","
    <<rect.GetW()<<","
    <<rect.GetH()<<endl;
  getchar();
}      

注意:

1、::是C++裡的“作用域運算符”。作用域符号::的前面一般是類名稱,後面一般是該類的成員名稱。
2、其作用主要有三點:
(1)辨別作用域的級别;
:: 用在全局函數或變量前,表示是全局函數或變量
(2)辨別成員屬于哪個類
A,B表示兩個類,在A,B中都有成員member。那麼A::member就表示類A中的成員member
B::member就表示類B中的成員member
(3) 限定成員的作用範圍 
應用于命名空間namespace定義的實體後面。      

3、保護繼承

保護繼承中,基類的公有和保護成員都以保護成員的身份出現在派生類中,而基類的私有成員不可直接通路。這樣,派生類的其他成員就可以直接通路從基類繼承來的公有和保護成員,但在類外部通過派生類的對象無法直接通路他們。

如果保護繼承的派生類作為新的基類,繼續派生時,派生出來的類有可能可以通路間接從基類中繼承來的成員。

如果合理地利用保護成員,就可以在類的複雜層次關系中在共享與成員隐藏之間找到一個平衡點,即能實作成員隐藏,又能友善繼承,實作代碼的高效重用和擴充。

eg:假設類A有保護資料成員x,我們來讨論成員x的通路特征。

基類A的定義為:
class A
{
  protected:
    int x;//保護資料成員
};
主函數:
int main()
{
  A a;
  a.x=5;//錯誤
}      

在編譯時會錯誤,因為在建立A類對象的子產品中試圖通路A類的保護成員,這是不允許的。

如果A類以公有方式派生了B類,則在B類中,A類保護成員和該類的公有成員一樣可以通路的,eg

class A
{
  protected:
    int x;
};
class B:public A  //公有派生
{
  public :
    void function();
};
void B::function()      

繼續閱讀