天天看點

[C++]類的繼承與派生

[C++]類的繼承與派生

繼承性是面向對象程式設計的第二大特性,它允許在既有類的基礎上建立新類,新類可以繼承既有類的資料成員和成員函數,可以添加自己特有的資料成員和成員函數,還可以對既有類中的成員函數重新定義。利用類的繼承和派生實作了更高層次的代碼可重用性,符合現代軟體開發的思想。

  C++語言同時支援單一繼承和多重繼承。單一繼承是指派生類隻從一個基類繼承而來;相應的,多重繼承指派生類同時從兩個或更多的基類繼承而來。java隻支援單一繼承。      

一. 派生類

派生類的定義格式如下:

class <派生類名>:[繼承方式]<基類名1>

                             [,[繼承方式]<基類名2>,...,[繼承方式]<基類名n>]

  {

          <派生類新增的資料成員和成員函數定義>

  };      

說明:

(1)定義派生類關鍵字可以是class或者是struct,兩者差別是:用class定義派生類,預設的繼承方式是private,用struct定義派生類,預設的繼承方式為public。新增加的成員預設屬性也是class對應private屬性,struct對應public屬性。

  (2)基類不能被派生類繼承的兩類函數是構造函數和析構函數。      

二. 3種繼承方式下基類成員在派生類中的通路屬性

[C++]類的繼承與派生

用下面的代碼簡單了解一下:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
private:
    int priData;
protected:
    int proData;
public:
    int pubData;
};

class D1:private Base//私有繼承
{
    void f1()
    {
        //priData=1;//基類private成員在派生類中不可直接通路
        proData=2;//基類的protected成員在派生類中為private通路屬性
        pubData=3;//基類的public成員在派生類中為private通路屬性
    }
};

class D2:protected Base//保護繼承
{
    void f2()
    {
        //priData=1;//基類private成員在派生類中不可直接通路
        proData=2;//基類的protected成員在派生類中為protected通路屬性
        pubData=3;//基類的public成員在派生類中為protected通路屬性
    }
};

class D3:public Base//公有繼承
{
    void f3()
    {
        //priData=1;//基類private成員在派生類中不可直接通路
        proData=2;//基類的protected成員在派生類中為protected通路屬性
        pubData=3;//基類的public成員在派生類中為public通路屬性
    }
};

int main()
{
    Base obj;
    //obj.priData=1;//對象不可通路Base類中private成員
    //obj.proData=2;//對象不可通路Base類中protected成員
    obj.pubData=3;
    D1 objD1;
    //objD1.pubData=3;//private屬性,不可通路
    D2 objD2;
    //objD2.pubData=3;//protected屬性,不可通路
    D3 objD3;
    objD3.pubData=3;//public屬性,可以通路
    return 0;
}      

基類的private成員函數雖然在派生類的成員函數中不可直接通路,但派生類的成員函數可以通過調用基類被繼承的函數來間接通路這些成員。如果基類的函數被繼承後在派生類中仍為public成員,則可以通過派生類對象直接調用。

先來看一下類成員的通路屬性及作用:

[C++]類的繼承與派生

好了,繼續通過代碼來了解:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
private:
    int priData;
protected:
    int proData;
public:
    int pubData;
//在類的定義中不能對資料成員進行初始化
    void SetData()//為基類中的資料成員指派
    {
        priData=100;
        proData=200;
        pubData=300;
    }
    void Print()
    {
        cout<<"priData="<<priData<<endl;
        cout<<"proData="<<proData<<endl;
        cout<<"pubData="<<pubData<<endl;
    }
};

class Derived:public Base
{
public:
    void ChangeData()
    {
        SetData();
        proData=12;//在派生類的成員函數類可以通路基類的非私有成員                   
    }
};

int main()
{
    Base b;
    b.SetData();
    b.Print();

    Derived d1;
    d1.ChangeData();
    d1.pubData=13;
    d1.Print();
    
    return 0;
}      
程式運作結果如下:      
[C++]類的繼承與派生

三. 派生類的構造函數和析構函數

在定義一個派生類的對象時,在派生類中新增加的資料成員當然用派生類的構造函數初始化,但是對于從基類繼承來的資料成員的初始化工作就必須由基類的構造函數完成,這就需要在派生類的構造函數中完成對基類構造函數的調用。同樣,派生類的析構函數值能完成派生類中新增加資料成員的掃尾、清理工作,而從基類繼承來的資料成員的掃尾工作也應有基類的析構函數完成。由于析構函數不能帶參數,是以派生類的析構函數預設直接調用了基類的析構函數。      

派生類構造函數定義格式如下:

<派生類名>(<總形式參數表>):<基類名1>(<參數表1>),

<基類名2>(<參數表2>),[...,<基類名n>(<參數表n>),其他初始化項>]

   {

           [<派生類自身資料成員的初始化>]

    }

  說明:(1)總形式表給出派生類構造函數中所有的形式參數,作為調用基類帶參構造函數的實際參數以及初始化本類資料成員的參數;

  (2)一般情況下,基類名後面的參數表中的實際參數來自前面派生類構造函數形式參數總表,當然也可能是與前面形式參數無關的常量;

  (3)在多層次繼承中,每一個派生類隻需要負責向直接基類的構造函數提供參數;如果一個基類有
  多個派生類,則每個派生類都要負責向該積累的構造函數提供參數。      

1.單一繼承

#include"stdafx.h"
#include<iostream>
using namespace std;

class Other
{
public:
    Other()
    {
        cout<<"constructing Other class"<<endl;
    }
    ~Other()
    {
        cout<<"destructing Other class"<<endl;
    }
};

class Base
{
public:
    Base()
    {
        cout<<"constructing Base class"<<endl;
    }
    ~Base()
    {
        cout<<"destructing Base class"<<endl;
    }
};

class Derive:public Base
{
private:
    Other ot;
public:
    Derive()
    {
        cout<<"constructing Derive class"<<endl;
    }
    ~Derive()
    {
        cout<<"destructing Derive class"<<endl;
    }
};

int main()
{
    Derive d;

    return 0;
}      

程式運作結果如下:

[C++]類的繼承與派生
可以看到定義派生類對象時,構造函數的調用順序:

  a.先調用基類的構造函數

  b.然後調用派生類對象成員所屬類的構造函數(如果有對象成員)

  c.最後調用派生類的構造函數

  析構函數的調用順序正好與構造函數調用順序相反。      

2.多重繼承

#include"stdafx.h"
#include<iostream>
using namespace std;

class Grand
{
    int g;
public:
    Grand(int n):g(n)
    {
        cout<<"Constructor of class Grand g="<<g<<endl;
    }
    ~Grand()
    {
        cout<<"Destructor of class Grand"<<endl;
    }
};

class Father:public Grand
{
    int f;
public:
    Father(int n1,int n2):Grand(n2),f(n1)
    {
        cout<<"Constructor of class Father f="<<f<<endl;
    }
    ~Father()
    {
        cout<<"Destructor of class Father"<<endl;
    }
};

class Mother
{
    int m;
public:
    Mother(int n):m(n)
    {
        cout<<"Constructor of class Mother m="<<m<<endl;
    }
    ~Mother()
    {
        cout<<"Destructor of class Mother"<<endl;
    }
};

class Son:public Father,public Mother
{
    int s;
public:
    Son(int n1,int n2,int n3,int n4):Mother(n2),Father(n3,n4),s(n1)
    {
        cout<<"Constructor of class Son s="<<s<<endl;
    }
    ~Son()
    {
        cout<<"Destructor of class Son"<<endl;
    }
};

int main()
{
    Son s(1,2,3,4);
    return 0;
}      
可以看到,與單一繼承不同的是:在多重繼承中,派生類有多個平行的基類,這些處于同一層次的基類構造函數的調用順序,取決于聲明派生類時所指定的各個基類的順序,而與派生類構造函數的成員初始化清單中調用基類構造函數的順序無關。      

繼續閱讀