面向對象——繼承與派生

繼承與派生

繼承與派生的概念

區分繼承和組合

​ 從模塊累積的角度來說,大模塊是由無數個小模塊構成的。構建一個模塊的方式有兩種,一是小模塊架構組合成爲大模塊,二是小模塊繼承生成大模塊。

​ 模塊可以通過調用模塊成員使用模塊成員的性質,這就是組合。

​ 組合的代碼理解如下:

#include<bits/stdc++.h>
using namespace std;
class point{
    private:
        int x,y;
    public:
        point(int xx,int yy):x(xx),y(yy){}
        void showpoint(){
            cout<<"("<<x<<","<<y<<")"<<endl;
        }
    ~point(){}
};
///小模塊組合爲大模塊
class circle{
    public:
        point p;
        double r;
    public:
        circle(int xx,int yy,double rr);
        double getarea(){return 3.14*r*r;}
        void shouarea(){
            cout<<"S="<<getarea()<<endl;
        }
};
///構造函數
circle::circle(int xx,int yy,double rr):p(xx,yy),r(rr){}
int main(){
    circle c(3,4,5.1);
    c.shouarea();
    (c.p).showpoint();
    return 0;
}

​ 模塊除了組合關係,還有繼承關係,簡要地說就是把父模塊的功能性質直接作爲自己的功能性質。

繼承的概念

​ 簡單的理解就是在原有類的功能性質上添加新的功能從而產生一個新的類,這就是繼承。

​ 一個新類繼承了原有類,原有類稱爲基類或父類,新類稱爲派生類或子類。子類獲得父類的特性是繼承,父類產生子類是派生。

​ 如果一個類只有一個基類,稱爲單繼承,如果有多個基類,稱爲多繼承。

(如果一個派生模塊只繼承了一個基類模塊,稱爲單繼承;如果一個派生模塊繼承了多個基類模塊作爲自己的子成員,這種繼承關係成爲多繼承。)

​ 派生類模塊的構造是在已有模塊的基礎上發展起來的,即派生類模塊繼承了基類模塊的接口功能。

​ 派生模塊繼承了基類模塊,也就是說基類模塊是派生模塊的一部分,在構造派生模塊時當然要構造一個基類模塊作爲他的子成員

派生類的聲明

語法

(以公有繼承關係爲例)

(具體的繼承方式在下一段介紹)

class 派生類名 : public 基類名{
    成員說明列表
}

說明:

1.派生類名指的是新構造的模塊,基類名是基礎模塊,派生類是以基類爲基礎發展形成的;

2. public表示的是基類的接口被繼承到派生模塊後將被提升爲派生類的對外接口
3. 成員說明列表指的是派生模塊裏除了繼承模塊外新加的成員。
4. 基類聲明的公有成員和保護成員在派生類中**保持原有訪問屬性**;基類聲明的私有成員在派生類中不可訪問,仍然爲基類私有。

代碼:

單繼承!

#include<bits/stdc++.h>

using namespace std;

class base{
    private:
        int b_num;
    public:
        ///無參構造函數
        base(){}
        ///有參構造函數
        base(int i):b_num(i){}
        int getbnum(){return b_num;}
        int setnum(int x){b_num=x;}
        void showbnum(){cout<<b_num<<endl;}
        ~base(){}
};
///定義的繼承派生關係,沒有添加新成員
///公有繼承,基類的對外接口繼承下來成爲派生類的對外接口
class derived:public base{
};

int main(){
    cout<<"測試基類性質"<<endl;
    base b;
    cout<<b.getbnum()<<endl;
    b.setnum(500);
    cout<<b.getbnum()<<endl;
    cout<<"基類base的對象大小爲"<<sizeof(b)<<endl;

    cout<<"測試派生類性質"<<endl;
    derived d;
    cout<<d.getbnum()<<endl;
    d.setnum(555);
    cout<<d.getbnum()<<endl;
    cout<<"派生類derived的對象大小爲"<<sizeof(d)<<endl;
    ///cout<<d.b_num; 沒有訪問權限
    return 0;
}

多繼承!

#include<bits/stdc++.h>

using namespace std;

class A{
    private: int a;
    public: int puba;
    public:
        void showa(){cout<<a<<endl;}
        int geta(){return a;}
};

class B{
    private: int b;
    public: int pubb;
    public:
        void showb(){cout<<b<<endl;}
        int getb(){return b;}
};

///公有繼承下類C有6個接口成員
class C: public A, public B{

};

int main(){
    C e;
    cout<<e.geta()<<endl;
    cout<<e.puba<<endl;
    cout<<e.pubb<<endl;
    cout<<e.getb()<<endl;
    ///e.A::getb();
    e.showa();
    e.showb();
    return 0;
}

說明:

​ 1.公有繼承下類C有6個接口成員:類A的puba showa() geta() ,類B的pubb showb() getb()

​ 2.可以理解爲類C中含有類A的一個無名對象和類B的一個無名對象,所以

e.getb();
e.B::getb();

其實是等價的,但是爲了方便一般寫第一種。

(如果有二義性的話就要格外討論了,後續會介紹)

派生模塊的構造和析構

派生模塊的成員構成和構造規則

以下面的代碼爲例

class C:public A,public B{
    private:
    	int c;
    	Clock time;
    public:
    	int get_c(){return c;}
}

藉助之前生成無名對象的思想,我們可以分析得到派生類C中含有四個數據成員,分別的類A的無名對象,類B的無名對象,c和Clock類的對象time

派生模塊的構造

類的構造函數的作用是創建對象時初始化對象。若類的設計者沒有定義構造函數,創建
對象時,系統自動生成一個默認構造函數,但該默認構造函數不能將對象初始化。如果設計
者定義了構造函數,系統就不會自動生成默認構造函數,而是調用設計者定義的構造函數。
派生類繼承了基類所有的數據成員,自己又添加了新成員,所以派生類對象的構造函數由二
部分構成。一部分用來初始化繼承的基類成員,一部分用來初始化新添加的成員。初始化繼
承的基類成員是通過調用基類的構造函數實現的。如果用戶沒有定義派生類的構造函數,在
創建派生類對象時,系統自動生成一個默認構造函數,該構造函數調用基類的無參構造函數。

構造函數就是將其成員全部構造出來並且按照規則和結構進行組裝。

簡明扼要的語法定義就是:

/**
模塊名(參數列表):初始化列表中構造出模塊的要素{
	將構造出的模塊進行組裝
}
**/

代碼如下:

C(int aa,int bb,int cc,int hh,int mm,int ss):A(aa),B(bb),c(cc),time(hh,mm,ss){
    	cout<<"類C的構造函數"<<endl;
}
//A(aa),B(bb)可以藉助上述提到的無名對象進行理解

派生模塊的析構

析構函數是對象消亡時,爲進行一些必要的清理工作,系統自動執行的函數。用戶一般
不必定義類的析構函數,系統會自動生成一個析構函數。但在某些情況下,用戶需要定義析
構函數,完成一些特殊的清理工作。
派生類析構函數的定義與沒有繼承關係的普通類的析構函數的定義相同。在執行派生類
的析構函數時,系統自動調用基類的析構函數。派生類析構函數的調用順序與構造函數的調
用順序正好相反,即:先調用派生類的析構函數,再調用對象成員的析構函數,最後調用基
類的析構函數。

代碼如下:

~C(){
	cout<<"類C的析構函數"<<endl;
}

完整代碼將不再做贅述。

繼承方式和派生模塊成員的對外可見性

前言:

需要先明確一點:繼承和權限是兩回事,先繼承,再重新定義對外權限。

訪問權限都是定義的可見成員的訪問權限,比如基類中的public成員,在派生模塊中可以定義爲public,也可以定義爲private。但是基類中的private成員,是無法定義權限的,因爲其本身就不可見,對其進行權限的定義是不符合語法的。

派生模塊的新定義成員與同名覆蓋:

我們假設類C是由public A,public B派生而來。

如果此時類A和類B裏有同名函數,直接用類C的對象e調用該函數是不可行的,這就是二義性,值得注意的是,二義性與函數重載並不相同。

但是,如果我們在函數前加預作用符來說明這是屬於類C的哪一個基類,就是可行的。

e.show();///不合法
e.A::show();///合法

還要知道的一點是,如果我們設類A的show函數是public成員,而類B的show函數是private成員,理論上來說這是沒有歧義的,因爲類B的show成員根本就不可見。但是!編譯器並不會區分是public還是private成員,只要是函數名相同的就是二義性。

如果我們在類C裏定義一個新的同名show函數,這個新的函數就會覆蓋舊的,但是仍然可以通過預作用符來調用舊的同名函數。

比如這樣:

class C: public A, public B{
    public:///只要是同名 新的就覆蓋舊的
    	void show(){cout<<"CCCCCCC"<<endl;}
};

繼承方式和派生模塊成員的對外可見性

訪問屬性:

在這裏插入圖片描述

(這是偷的圖,侵刪致歉)

繼承方式

類成員有 public(公有)、protected(保護)和 private(私有)三種訪問屬性。在類的內
部對各種屬性的成員均可直接訪問。在類的外部通過類的對象,只能直接訪問類的公有成員,
類的保護成員與私有成員不可訪問。
在類的派生過程中,派生類對基類的繼承相應地也有 public(公有)、protected(保護)
和 private(私有)三種繼承方式。派生類接收基類成員成爲派生類成員時,可以改變基類成
員在派生類中的訪問屬性。派生類中基類成員的訪問屬性由基類成員聲明的屬性和派生方式
共同決定。具體訪問規則是:
(1)公有繼承方式
基類聲明的公有成員和保護成員在派生類中保持原有訪問屬性;基類聲明的私有成員在
派生類中不可訪問,仍然爲基類私有。
(2)保護繼承方式
基類聲明的公有成員和保護成員在派生類中都變成保護成員;基類聲明的私有成員在派
生類中不可訪問,仍然爲基類私有。
(3)私有繼承方式
基類聲明的公有成員和保護成員在派生類中變成私有成員;基類聲明的私有成員在派生
類中不可訪問,仍然爲基類私有。
類的保護成員和私有成員在不涉及繼承關係時可認爲是等價的,但有繼承關係存在時,
二者是有區別的。通過上面的訪問規則可以看出私有成員和保護成員的區別。無論哪種繼承
方式,基類的私有成員在派生類中仍爲基類私有,成爲派生類不可訪問的成員。基類的保護
成員可成爲派生類的保護成員或私有成員,在派生類的內部可以訪問。

保護繼承

希望某成員能夠被繼承到子類裏,並且還不作爲對外訪問的接口,這種訪問權限就是protected.

protected訪問權限的本意就是當前成員可以被繼承到派生類裏,但是不能作爲派生類的對外接口,此訪問權限介於public和private中間。

在這裏插入圖片描述
未完待續,實在是寫不下去了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章