【C++溫故知新】詳解C++中的類的繼承

這是C++類重新復(fù)習(xí)學(xué)習(xí)筆記的第 七 篇巷蚪,同專題的其他文章可以移步:http://www.reibang.com/nb/39156122

類繼承的語法

類的繼承允許通過繼承的方式生成新類魁索,繼承自的類為基類抒巢,繼承自基類的類成為派生類互亮,類的繼承寫法如下:

class derivedClass : public/protect/private baseClass
{
// statements
}

其中基類前有一個訪問限定符寿烟,不寫的時候默認(rèn)為private淑际,但是我們主要使用public脸侥,又稱作公有繼承建邓,三個不同的訪問限定詞的區(qū)別在于:

  • 公有繼承(public):當(dāng)一個類派生自公有基類時,基類的公有成員也是派生類的公有成員睁枕,基類的保護(hù)成員也是派生類的保護(hù)成員官边,基類的私有成員不能直接被派生類訪問,但是可以通過調(diào)用基類的公有和保護(hù)成員來訪問
  • 保護(hù)繼承(protecte): 當(dāng)一個類派生自保護(hù)基類時外遇,基類的公有和保護(hù)成員將成為派生類的保護(hù)成員
  • 私有繼承(private):當(dāng)一個類派生自私有基類時注簿,基類的公有和保護(hù)成員將成為派生類的私有成員

例如我們定義一個基類再定義一個派生類:

class baseClass
{
private:
    baseInt;
    baseDouble;
public:
    baseClass(const int pInt = 1, cosnt double pDouble = 2.5);
    int function(double d);
}
 
baseClass :: baseClass(const int pInt = 1, cosnt double pDouble = 2.5)
{
    baseInt = pInt;
    baseDouble = pDouble;
}
 
baseClass :: function(double d)
{
    cout << d;
    return 1;
}
 
 
class derivedClass : public baseClass
{
private:
    derivedChar;
public:
    derivedClass(const int pInt, const double pDouble, const char pChar);
    void anotherFunction();
}
 
derivedClass :: derivedClass(const int pInt, const double pDouble, const char pChar) : baseClass(pInt,pDouble)
{
    derivedChar = pChar;
}
 
void derivedClass :: anotherFunction()
{
    cout << "I am the derived class.";
}

從這個例子中,我們可以看到跳仿,基類有兩個私有變量诡渴,baseIntbaseDouble,兩個方法菲语,一個構(gòu)造函數(shù)用于給兩個私有變量賦值妄辩,一個用于充數(shù)。派生類繼承自基類山上,于是派生類derivedClass就擁有了基類的兩個公有方法眼耀,但是它不能訪問基類的兩個私有變量。派生類又定義了自己的一個新私有變量佩憾,同時有自己的構(gòu)造函數(shù)和一個用于充數(shù)的函數(shù)哮伟「苫ǎ可見繼承的作用在于方便地進(jìn)行代碼的重用以及組織管理項目設(shè)計。

基類和派生類的關(guān)系

類的繼承是“is-a”的關(guān)系楞黄,或者說是“is-a-kind-of”池凄,即派生類對象也是一個基類對象。

  • 派生類是可以調(diào)用基類的protectedpublic修飾的成員變量和方法的鬼廓,而派生類也可以定義自己的變量和方法修赞。
  • 同時,派生類還可以重載基類的方法桑阶,即聲明一個和基類中相同名稱的成員變量柏副,但是在派生類中對其進(jìn)行重新的定義。如果基類和派生類同時擁有同名同變量參數(shù)同返回值但是定義不同的函數(shù)蚣录,在使用基類對象調(diào)用該函數(shù)時割择,調(diào)用的是基類的函數(shù),在使用派生類的對象調(diào)用該函數(shù)時萎河,調(diào)用的是派生類的定義荔泳。
  • 基類指針可以在不進(jìn)行顯式類型轉(zhuǎn)換的情況下指向派生類對象;基類引用可以在不進(jìn)行顯式類型轉(zhuǎn)換的情況下引用派生類對象虐杯。C++要求的引用和指針類型與賦給的類型匹配的規(guī)則對繼承來說例外玛歌。然而,這種例外只是單向的擎椰,不可以將基類對象和地址賦給派生類引用和指針支子。基類指針或引用只能用于調(diào)用基類方法达舒。

多態(tài)公有繼承

多態(tài)即一個派生類繼承自一個基類后值朋,希望可以定義一個和基類中相同名稱、參數(shù)列表巩搏、返回值的函數(shù)昨登,但這個函數(shù)的定義卻與基類中的不同,即一種派生類對基類方法的重載贯底。

首先丰辣,派生類可以重載基類的方法,如果派生類使用基類相同的函數(shù)和函數(shù)定義禽捆,那么就不需要再在派生類中聲明該函數(shù)笙什,即共有的函數(shù)需要放在基類中。如果派生類想對基類的函數(shù)進(jìn)行新的定義睦擂,則需要在派生類中對其再次進(jìn)行聲明并定義得湘,定義時也需要表明定義的是那個類的函數(shù)。如 baseClass::function()derivedClass::function() 這樣顿仇。

虛方法(virtual method)淘正,需要使用關(guān)鍵詞 virtual 修飾基類中的函數(shù)摆马,如下面這樣:

virtual void function(int i);

它的作用如下:當(dāng)基類和派生類都有定義過某個相同方法后,我們需要確定調(diào)用的是哪個類下的方法鸿吆,特別是當(dāng)方法是通過引用或指針而不是對象調(diào)用的囤采。

  • 如果沒有使用關(guān)鍵字 virtual ,程序?qū)⒏鶕?jù)引用類型或指針類型選擇方法
  • 如果使用了關(guān)鍵字 virtual 惩淳,程序?qū)⒏鶕?jù)引用或指針指向的對象的類型來選擇方法
  • 如果有派生類重載了基類的方法蕉毯,一般需要將基類的析構(gòu)函數(shù)設(shè)置成virtual的以保證釋放派生類對象時能夠按照正確的順序調(diào)用析構(gòu)函數(shù)
// 不使用virtual
BaseClass baseClass();
DerivedClass derivedClass();
BaseClass & reference1 = baseClass; // 指向baseClass的類型是BaseClass的引用變量
BaseClass & reference2 = derivedClass; // 指向derivedClass的但是類型是BaseClass的引用變量
reference1.function(); // 會根據(jù)引用的類型即BaseClass調(diào)用BaseClass下的function方法
reference2.function(); // 會根據(jù)引用的類型即BaseClass調(diào)用BaseClass下的function方法
 
// 使用virtual
BaseClass baseClass();
DerivedClass derivedClass();
BaseClass & reference1 = baseClass; // 指向baseClass的類型是BaseClass的引用變量
BaseClass & reference2 = derivedClass; // 指向derivedClass的但是類型是BaseClass的引用變量
reference1.function(); // 會根據(jù)引用指向的類型即BaseClass調(diào)用BaseClass下的function方法
reference2.function(); // 會根據(jù)引用指向的類型即DerivedClass調(diào)用BaseClass下的function方法

抽象基類

抽象基類(abstract base class,ABC)是一種特殊的基類思犁,從概念上講代虾,將所有派生類的公用的方法進(jìn)行抽象匯總聲明(定義)到的一個類中,這種設(shè)計下的類可以視作一個抽象基類激蹲。但是真正的抽象基類應(yīng)該是至少包含了一個純虛函數(shù)(pure virtual function)的類棉磨,這種類不能聲明對應(yīng)的對象,只能作為基類学辱。

純虛函數(shù)是一種只在抽象基類中給出原型乘瓤,但是部給出定義的函數(shù),更像是一個接口策泣,由所有的派生類對純虛函數(shù)根據(jù)自己類的需求來實現(xiàn)其定義衙傀。純虛函數(shù)的寫法是在虛函數(shù)后面以 =0 結(jié)尾

virtual double pureVirtualFunction(int i) const = 0;

應(yīng)用這種方式,可以將所有派生類共有但是卻又各自有著不同實現(xiàn)的方法抽象到一個基類中萨咕,提供其原型但是不對其進(jìn)行定義(也只有純虛函數(shù)C++允許不給出定義)统抬,然后使得各個派生類自己給出其定義。

私有繼承

私有繼承即繼承的基類使用private修飾符修飾的繼承任洞,如果沒有訪問限定符的修飾蓄喇,默認(rèn)也是私有繼承发侵,私有繼承是一種“has-a”的關(guān)系交掏。

class DerivedClass : private BaseClass{ }
class DerivedClass : BaseClass{ }

私有繼承使得基類的公有成員、保護(hù)成員都被成為派生類的私有成員刃鳄,這就使得基類的那些方法都不能再被派生類的實例化對象使用盅弛,而只能被派生類的成員函數(shù)在類內(nèi)部使用。即派生類部繼承基類的接口叔锐。

這里比較了三種繼承之間的區(qū)別:

特征 公有繼承 保護(hù)繼承 私有繼承
公有成員變成 派生類的公有成員 派生類的保護(hù)成員 派生類的私有成員
保護(hù)成員變成 派生類的保護(hù)成員 派生類的保護(hù)成員 派生類的私有成員
私有成員變成 只能通過基類接口訪問 只能通過基類接口訪問 只能通過基類接口訪問
能否隱式向上轉(zhuǎn)換 只能在派生類中 不能

多重繼承

多重繼承(Multiple Inheritance)也是“is-a”的關(guān)系挪鹏,它允許一個類繼承自多個類,只需要將繼承的類使用逗號隔開即可愉烙,像下面這樣:

class DerivedClass : public BaseClass1, public BaseClass2 {……}
class DerivedClass : public BaseClass1, BaseClass2 {……} // BaseClass2 is a private base

多重繼承中每一個被繼承的基類都需要設(shè)置訪問限定符讨盒,根據(jù)需要可以使用不同的訪問限定符,不寫默認(rèn)為private

例如設(shè)置一個基類Worker表示工人步责,然后工人可以是歌手也可以是服務(wù)員返顺,我們使用兩個類繼承自這個基類禀苦,Singer和Waiter,最后遂鹊,我們可以定義一個既是歌手有時服務(wù)員的類振乏,所以它同時繼承自Singer和Waiter,他們的關(guān)系就像下邊這樣:

class Worker {……}
class Singer : public Worker {……}
class Waiter : public Worker {……}
class SingingWaiter : public Singer, public Waiter {……}
一個多重繼承

虛基類

首先多重繼承導(dǎo)致了一個問題就是秉扑,當(dāng)一個SingingWaiter的實例繼承自Singer和Waiter時慧邮,也就間接地兩次繼承了Worker,也就是說一個SingingWaiter的實例結(jié)構(gòu)應(yīng)該是如下這樣的:

在不使用虛基類的時候

這樣引發(fā)的問題就是舟陆,當(dāng)我們把派生類對象的地址賦給一個基類的指針時就無法區(qū)分是賦給哪個基類误澳,導(dǎo)致二義性:

SingingWaiter sw;
Worker * psw = &sw;

因為sw中包含兩個Worker對象秦躯,從而有兩給地址可以選擇脓匿,于是正確的寫法應(yīng)該是:

Worker * psw1 = (Waiter *) &sw;
Worker * psw2 = (Singer *) &sw;

所以虛基類(Virtual Base Classes)將解決這個問題。虛基類使得從多個類派生出的對象只繼承一個基類對象宦赠,需要在類繼承的聲明中使用virtual關(guān)鍵詞陪毡,virtual和public的次序無所謂:

class Singer : virtual public Worker {……}
class Waiter : public virtual Worker {……}
class SingingWaiter : public Singer, public Waiter {……}

現(xiàn)在,SingingWaiter對象就只包含Worker對象的一個副本勾扭,從本質(zhì)上說是毡琉,繼承的Singer和Waiter共享一個Worker對象,而不是各自引入一個Worker對象的副本妙色,從而可以使用多態(tài)桅滋。

在使用虛基類的時候

轉(zhuǎn)載請注明出處,本文永久更新鏈接:https://blogs.littlegenius.xin/2019/08/28/【C-溫故知新】七類的繼承/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末身辨,一起剝皮案震驚了整個濱河市丐谋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌煌珊,老刑警劉巖号俐,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異定庵,居然都是意外死亡吏饿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門蔬浙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猪落,“玉大人,你說我怎么就攤上這事畴博”考桑” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵俱病,是天一觀的道長官疲。 經(jīng)常有香客問我杂曲,道長,這世上最難降的妖魔是什么袁余? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任擎勘,我火速辦了婚禮,結(jié)果婚禮上颖榜,老公的妹妹穿的比我還像新娘棚饵。我一直安慰自己,他們只是感情好掩完,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布噪漾。 她就那樣靜靜地躺著,像睡著了一般且蓬。 火紅的嫁衣襯著肌膚如雪欣硼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天恶阴,我揣著相機(jī)與錄音诈胜,去河邊找鬼。 笑死冯事,一個胖子當(dāng)著我的面吹牛焦匈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昵仅,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼缓熟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了摔笤?” 一聲冷哼從身側(cè)響起够滑,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吕世,沒想到半個月后彰触,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡寞冯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年渴析,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吮龄。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咆疗,靈堂內(nèi)的尸體忽然破棺而出漓帚,到底是詐尸還是另有隱情,我是刑警寧澤午磁,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布尝抖,位于F島的核電站毡们,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏昧辽。R本人自食惡果不足惜衙熔,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搅荞。 院中可真熱鬧红氯,春花似錦、人聲如沸咕痛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茉贡。三九已至塞栅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腔丧,已是汗流浹背放椰。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留愉粤,地道東北人庄敛。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像科汗,于是被迫代替她去往敵國和親藻烤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容