繼承與組合

來源:互聯網
上載者:User

標籤:

  • 繼承

一、基本概念

1 基類和衍生類別

1.1 只有兩層的繼承關係中,被繼承者稱為基類(父類);繼承者稱為衍生類別(子類)

1.2 多層繼承關係中,類A通過類B間接派生出類C,則類A和類B稱為類C的祖先類;類B和類C是類A的後代類;

一個類的祖先類包含了該類的基類和基類的祖先類;一個類的後代類包含了該類的衍生類別和衍生類別的後代類;

2 繼承形式

單重繼承、多重繼承、重複繼承;


3 繼承成員的存取控制

問:衍生類別B繼承基類A後,基類A中的資料成員和成員函數在類B中是否可以訪問?其他類能否訪問類B繼承的類A的成員?

答:取決於由類A中成員的存取控制方式和類B在繼承類A時指定的繼承控制方式共同決定。

基類中private成員以完全封閉的形式存在,只容許本類內部成員及友元對其訪問,因此不論繼承控制方式如何,在衍生類別中都無法訪問該成員。

基類中protected成員以半封閉的形式存在,容許衍生類別的成員對其訪問,因此不論繼承控制方式如何,在衍生類別中都可訪問該成員;但這些成員在衍生類別中對外表現形式有些變化,如繼承控制方式是private,基類中的protected成員在衍生類別中變成protected成員,在衍生類別中可以訪問這些成員,但衍生類別以外的其他不可訪問該成員;

基類中public成員以開放的形式存在,容許其他類的成員對其進行訪問,因此不論在繼承是繼承控制方式如何,在衍生類別中都可訪問這些成員,但這些成員在衍生類別中對外表現形式有些變化。

4 繼承文法

class <衍生類別名>: <基類名列表>{<資料成員和成員函數的聲明>};

基類名列表表示為:<繼承控制方式> <基類名1>, <繼承控制方式> <基類名2>, ......

繼承控制方式預設為private;

一般使用公有繼承,使基類中所有公有成員在衍生類別中也保持公有;

舉例:小汽車和跑車之間的關係用繼承描述。

其中,小汽車是基類,跑車是衍生類別,通過公有繼承方式從小汽車類中派生得到。

在跑車類中新增資料成員——顏色,同時增加對這個資料成員操作的成員函數。

#include <iostream>#include <string>using namespace std;class Car{public:Car(int theweight, int thespeed){weight = theweight;speed = thespeed;}void setWeight(int theweight){weight = theweight;}void setSpeed(int thespeed){speed = thespeed;}int getWeight(){return weight;}int getSpeed(){return speed;}private:int weight;int speed;};class sportCar :public Car{public:sportCar(int theweight, int thespeed, string thecolor) :Car(theweight,thespeed){color = thecolor;}void setColor(string thecolor){color = thecolor;}string getColor(){return color;}private:string color;};int main(){Car car(100, 100);sportCar sportcar(100, 200, "white");cout << car.getWeight() << "\t" << car.getSpeed() << endl;cout << sportcar.getWeight() << "\t" << sportcar.getSpeed() << "\t" << sportcar.getColor() << endl;return 0;}
二、衍生類別中繼承成員函數的重定義

繼承目的是在一般性的類的基礎上產生具有特殊性的類。

若在衍生類別中定義一個函數原型和繼承成員函數一樣的成員函數,則該成員函數實現對繼承成員函數的重定義。

當通過衍生類別對象調用成員函數,編譯器對函數調用的處理方法:

1 在衍生類別中尋找該函數,若在衍生類別中有該函數的定義,則調用衍生類別中定義的函數;否則進入2;

2 在基類中尋找該函數的定義,找到則調用;否則進入3;

3 繼續2,若尋找完所有的祖先類都未找到該函數的定義,則報未定義錯誤。

這體現衍生類別中定義的函數有優先權,即衍生類別中的重定義函數屏蔽基類中的相應函數。

三、繼承層次中的建構函式和解構函式

1 衍生類別對象的儲存空間

在類對象的儲存空間中,只存放從基類繼承的非待用資料成員和衍生類別中定義的非待用資料成員;

類的待用資料成員和成員函數由類的所有對象公用,只需儲存一份。

2 衍生類別並不繼承基類的建構函式和解構函式

衍生類別必須定義自己的建構函式和解構函式;

在產生衍生類別對象時,由衍生類別的建構函式調用其直接基類的建構函式,對從基類繼承的資料成員進行初始化;

若自己未定義建構函式和解構函式,系統會自動產生一個預設的建構函式,該建構函式的函數體為空白。

3 衍生類別的建構函式

通常,一個類中包含預設的建構函式和帶參數的建構函式;

3.1 衍生類別建構函式作用

a 通過初始化列表給基類建構函式傳遞參數,調用基類的帶參數建構函式初始化基類的資料成員;或調用基類的預設建構函式初始化基類的資料成員;

b 初始化衍生類別中定義的資料成員

3.2 帶參數列表的建構函式的文法:

<衍生類別名> (<形參列表>):<基類名>(<傳遞給基類的建構函式的實參列表>)

4 建構函式和解構函式的調用次序

產生衍生類別對象時,建構函式的調用次序是:首先調用直接基類的狗在函數,再調用衍生類別的建構函式;

解構函式的調用次序正好相反。

當建立一個後代類對象,需追溯到其最遠祖先,由最遠祖先開始逐級調用建構函式初始化該後代類繼承得到的資料,最後調用後代類自己的建構函式。

舉例:

#include <iostream>#include <string>using namespace std;class A{public:A(){cout << "Construct Base Object.\n";}~A(){cout << "Destruct Base Object.\n";}};class B :public A{public:B(){cout << "Construct Derived Level 1 Object.\n";}~B(){cout << "Destruct Derived Level 1 Object.\n";}};class C :public B{public:C(){cout << "Construct Derived Level 2 Object.\n";}~C(){cout << "Destruct Derived Level 2 Object.\n";}};int main(){C c;return 0;}
輸出:

Construct Base Object.
Construct Derived Level 1 Object.
Construct Derived Level 2 Object.
Destruct Derived Level 2 Object.
Destruct Derived Level 2 Object.
Destruct Base Object.


  • 組合

一、組合的文法表示和圖形表示

class Wheel{... ...         // 成員定義省略};class Car {public:... ...         private:int weight;int speed;Wheel wheel[4]; // 一個Car對象中包含4個Wheel對象};class SportCar:public Car{... ...};

其中,被包含對象稱為內嵌物件。

通常情況下,將內嵌物件作為私人成員;但若想保留內嵌物件的共有介面,也可將內嵌物件作為共有成員。

二、組合與建構函式和解構函式

1 建立一個包含內嵌物件的對象時,建構函式的調用次序是:

首先按類聲明中內嵌物件出現的次序,分別調用各內嵌物件的建構函式;

執行本類的建構函式;

2 當一個類既是衍生類別,又組合其他類,建立該類對象時建構函式的調用次序是:

調用基類的建構函式;

按類聲明中內嵌物件出現的次序,分別調用各內嵌物件的建構函式;

最後執行衍生類別的建構函式;

三、繼承與組合的比較

繼承表示一般性和特殊性的關係,使用繼承方法可建立已存在類的特殊版本;

組合表示組成關係,當一個對象是另一個對象的組成部分時,使用組合方法可用已存在的類組裝新的類;


  • 多重繼承與重複繼承

一、多重繼承

1 多重繼承中,衍生類別有多個基類,衍生類別與每個基類之間關係仍看作是單繼承關係;

class A{... ...};class B{... ...};class C:public A,public B{... ...};

通過多重繼承,衍生類別C具有兩個基類(類A和類B),在類C對象的儲存空間中除了存放C類中定義的非待用資料成員,還存放從類A和類B繼承下來的非待用資料成員;

多重繼承的應用背景是有時描述一個概念C,該概念具有雙重特性,即可以說是A,也可以說是B;

2 舉例

Device1定義一個裝置類,具有音量、開關等屬性作為被保護的資料成員,在資料上的操作包括:建構函式、顯示開關狀態、顯示音量;

Device2定義另一個裝置類,具有待機時間、通話時間、電池電量等屬性作為被保護的資料成員,在資料上的操作包括:建構函式、顯示裝置屬性、顯示電池電量;

有一個新裝置類DeviceNew,既是Device1定義的裝置,也是Device2定義的裝置;

通過共有多重繼承,定義DeviceNew類,在該類中定義屬性重量及顯示重量的函數。

#include <iostream>using namespace std;class Device1{public:Device1();Device1(int vol, bool onORoff);void showPower();void showVol();protected:int volume;bool powerOn;};class Device2{public:Device2();Device2(int newTalkTime, int newStandbyTime, float powerCent);void showProperty();void showPower();protected:int talkTime;int standbyTime;float power;};class DeviceNew:public Device1, public Device2{public:DeviceNew();DeviceNew(float newWeight, int vol, bool onORoff, int newTalkTime, int newStandbyTime, float powerCent):Device2(newTalkTime,newStandbyTime),Device1(vol, onORoff);float getWeight();private:float weight;};Device1::Device1(){cout<<"Initialize device 1 by default constructor in Device1."<<enel;volume=5;powerOn=false;}Device1::Device1(int vol, bool onORoff){cout<<"Initialize device 1 by constructor with parameters in Device1."<<endl;volume=vol;powerOn=onORoff;}void Device1::showPower(){cout<<"The status of power is: ";switch(powerOn){case true: cout<<"Power on."<<endl;break;case false: cout<<"Power off."<<endl;break;}}void Device1::showVol(){cout<<"Volume is "volume<<endl;}Device2::Device2(){cout<<"Initialize device 2 by default constructor in Device2"<<endl;talkTime=10;standbyTime=300;power=100;}Device2::Device2(int newTalkTime, int newStandbyTime, float powerCent){cout<<"Initialize device 2 by constructor with parameters in Device2."<<endl;talkTime=newTalkTime;standbyTime=newStandbyTime;power=powerCent;}void Device2::showProperty(){cout<<"The property of the device:"<<endl;cout<<"talk time: "<<talkTime<<"hours"<<endl;cout<<"standbyTime: "<<standbyTime<<"hours"<<endl;}void Device2::showPower(){cout<<"Power: "<<power<<endl;}DeviceNew::DeviceNew(){cout<<"Initialize device new by default constructor in DeviceNew."<<endl;weight=0.56;}DeviceNew::DeviceNew(float newWeight, int vol, bool onORoff, int newTalkTime, int newStandbyTime, float powerCent):Device2(newTalkTime, newStandbyTime, powerCent),Device1(vol, onORoff){cout<<"Initialize device new by constructor with parameters in DeviceNew."<<endl;weight=newWeight;}float DeviceNew::getWeight(){return weight;}int main(){DeviceNew device;    // 生命一個衍生類別cout<<"The weight of the device is "<<device.getWeight()<<endl;      // getWeight()函數是DEVICE_NEW類自身定義的device.showVol();          // showVol()函數是從DEVICE1類繼承下來的device.showProperty();     // showProperty()函數是從DEVICE2類繼承下來的return 0;}

運行結果:

Initialize device 1 by default constructor in Device1.
Initialize device 2 by default constructor in Device2.
Initialize device new by default constructor in DeviceNew.
The weight of the device: 0.56
Volume is 5
The property of the device:
talk time: 10 hours
standbyTime: 300 hours

二、多重繼承的建構函式

多重繼承下衍生類別建構函式和單繼承下衍生類別建構函式相似,必須同時負責調用該衍生類別所有基類的建構函式;

衍生類別建構函式的形參表必須滿足所有基類建構函式所需參數(或者使用常量運算式調用基類建構函式);

若基類的建構函式帶有參數,則由衍生類別建構函式通過初始化列表的方式將參數傳遞給基類建構函式;

衍生類別建構函式格式如下:

<衍生類別名> (<形參表>): <初始化列表>{        <衍生類別建構函式>}

其中,若衍生類別調用基類的預設建構函式,則初始化列表為空白;若不為空白,則初始化列表的文法形式為:<基類名1>(<參數表1>),<基類名2>(<參數表2>),...

衍生類別建構函式的執行順序是先執行所有基類的建構函式,再執行衍生類別本身的建構函式,處於同一層次的各基類建構函式的執行順序取決於定義衍生類別時所指定的各基類順序,與衍生類別建構函式中所定義的初始化列表的順序無關。即執行基類建構函式的順序取決於定義衍生類別時指定的基類的順序。

如上例,在定義衍生類別時,採用以下繼承方式:class DeviceNew: public Device1, public Device2 ,它決定了基類建構函式的調用次序是先Device1,後Device2;

修改main函數為:

int main(){DeviceNew device(0.7,3,false,10,250,80);cout<<"The weight of the device: "<<device.getWeight()<<endl;device.showVol();device.showProperty();return 0;}

運行結果:

Initialize device 1 by constructor with parameters in Device1.
Initialize device 2 by constructor with parameters in Device2.
Initialize device new by constructor with parameters in DeviceNew.
The weight of the device: 0.7
Volume is 3
The property of the device:
talk time: 10 hours
standbyTime: 250 hours

三、多重繼承中存在的問題:名字衝突

名字衝突指在多個基類中具有相同名字的成員時,在衍生類別中這個名字會產生二義性。

如,在執行device.showPower();語句時,編譯器無法確定要調用的是從哪個基類繼承下來的showPower()函數(是Device1類還是Device2類?),因此產生二義性;

解決辦法有二:

1 用作用於操作符::明確衍生類別對象要訪問的是從哪個基類繼承下來的成員

device.Device1::showPower();device.Device2::showPower();

2 在衍生類別中重定義有名字衝突的成員

在衍生類別DeviceNew中重定義showPower()函數:

woid showPower(){Device1::showPower();Device2::showPower();}

當通過衍生類別對象調用showPower()函數時,首先檢查在衍生類別中是否定義了該函數,若定義了,調用衍生類別中的showPower()函數;

四、重複繼承

當衍生類別的多個基類具有相同的祖先時,會出現重複繼承的情形,即一個類重複多次繼承了某個祖先類;

如,Derived類通過其兩個基類Base1和Base2重複繼承了Base類兩次,Derived類對象的儲存空間中會包含從Base1和Base2中繼承下來的資料成員,而Base1和Base2中又包含從Base類中繼承下來的資料成員,故Derived類對象的儲存儲存空間中包含了Base類中非待用資料成員的兩個副本,致使二義性。

#include <iostream>using namespace std;class Base{public:void setData(int newData){data=newData;}protected:int data;};class Base1: public Base{public:void setData1(int newData, int newData1){data=newData;data1=newData1;}protected:int data1;};class Base2: public Base{public:void setData2(int newData, int newData2){data=newData;data2=newData2;}protected:data2=newData2;};class Derived: public Base1, public Base2{public:void setData3(int newData, int newData1, int newData2){data=newData;   // 對data訪問有二義性data1=newData1; data2=newData2;}};int main(){Derived dObj;dObj.setData3(3,4,5);return 0;_}

解決該問題方法有二:

1 採用範圍運算髮::明確選擇哪個副本中的資料

class Derived: public Base1, public Base2{public:void setData3(int newData, int newData1, int newData2){Base1::data=newData;Base2::data=newData;data1=newData1;data2=newData2;}};

2 採用虛基類方法,使衍生類別對象的儲存空間中只保留被重複繼承的祖先類的一個對象副本

虛基類和普通基類的區別是在繼承控制保留字之前加virtual,當用virtual限定的基類被重複繼承時,只在衍生類別對象的儲存空間中保留其資料的一個副本;

class Base1: virtual public Base{    // Base是其虛基類public:void setData1(int newData, int newData1){data=newData;data1=newData1;}protected:int data1;};class Base2: virtual public Base{    // Base是其虛基類public:void setData2(int newData, int newData2){data=newData;data2=newData2;}protected:data2=newData2;};


繼承與組合

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.