C++筆記三(面向?qū)ο缶幊躺希?/h2>

十一 組合和繼承

OOD(Object Oriented Design)
(1)基于對象:單一類的寫法;
(2)面向?qū)ο螅侯惻c類之間的關(guān)系,包含三種:Composition(復(fù)合)、Delegation(委托)、 Inheritance(繼承)。

11.1 Composition表示has-a

14.png

如上圖所示,queue類中有deque類,稱為queue has-a deque即類的復(fù)合。
這也是一種設(shè)計模式Adapter,queue類擁有deque類,而且queue所有的函數(shù)功能都借用deque類來實(shí)現(xiàn)。deque功能非常全,queue根據(jù)用戶開放部分的功能,queue是一種adapter。


15.png

這是復(fù)合在內(nèi)存中的表現(xiàn),queue類有deque類,deque類有Itr類,sizeof的計算如圖所示:


16.png

類之間在復(fù)合關(guān)系下構(gòu)造函數(shù)與析構(gòu)函數(shù)的順序?yàn)椋?br> (1)構(gòu)造由內(nèi)而外。container的構(gòu)造函數(shù)首先調(diào)用Component的default構(gòu)造函數(shù),然后才執(zhí)行自己。
Container::Container(...):Component(  ){ ... };

(2) 析構(gòu)由外而內(nèi)。container的析構(gòu)函數(shù)首先執(zhí)行自己,然后才調(diào)用component的析構(gòu)函數(shù)。

Container::~Container(...){ ... ~Component(  ) };

11.2 Delegation(委托) (Composition by reference)

17.png

(1)如圖所示左邊有指向右邊的指針,這種叫委托。它和復(fù)合的區(qū)別是復(fù)合內(nèi)外部是一起出現(xiàn),兩者生命周期同步;而委托則是允許外部先創(chuàng)建出來,等需要的時候再把內(nèi)部寫好,兩者生命周期不同步。
(2) 圖示寫法叫做Handle/Body或者pointer implementation(pimpl)。左邊是對外的接口,右邊是功能的實(shí)現(xiàn),右邊不影響左邊,右邊可以更改甚至指向別的類,具有良好的彈性。

11.3 Inheritance(繼承)表示is-a

18.png

子類從父類繼承了數(shù)據(jù)和函數(shù)(函數(shù)繼承的是調(diào)用權(quán))。


19.png

類之間在繼承關(guān)系下構(gòu)造函數(shù)與析構(gòu)函數(shù)的順序?yàn)椋?br> (1)構(gòu)造由內(nèi)而外。Derived的構(gòu)造函數(shù)首先調(diào)用Base的default構(gòu)造函數(shù),然后才執(zhí)行自己。

Derived::Derived(...):Base(  ){ ... };

(2) 析構(gòu)由外而內(nèi)。Derived的析構(gòu)函數(shù)首先執(zhí)行自己,然后才調(diào)用Base的析構(gòu)函數(shù)。Base class的析構(gòu)必須是virtual,否則會出現(xiàn)undefined behavior!

Derived::~Derived(...){ ... ~Base(  ) };

十二 虛函數(shù)與多態(tài)

12.1 虛函數(shù)及其應(yīng)用

繼承最有價值的是和虛函數(shù)的搭配使用。成員函數(shù)從虛函數(shù)的角度出發(fā)分為三種:
(1)non-virtual函數(shù):你不希望derived class重新定義(override,重寫)它;
(2)virtual函數(shù):你希望derived class重新定義(override,重寫)它,且你對它已有默認(rèn)定義;
(3)pure virtual函數(shù):你希望derived class一定要重新定義(override,重寫)它,且你對它沒有默認(rèn)定義。

虛函數(shù)應(yīng)用實(shí)例如下:


20.png

舊文件的開啟與讀取,check file name 、search file 、open file任何人寫的都差不多,可以事先寫,但是因?yàn)槊總€人讀取的文件類型不一樣,所以讀取這個動作不能事先寫。框架如下:


21.png

應(yīng)用框架CDocument中已經(jīng)事先寫好check file name 、search file等函數(shù),CMyDoc是CDocument的子類,沒法事先寫的Serialize()設(shè)計為虛函數(shù)(可能為空函數(shù),也可能為純虛函數(shù)),使用流程如圖箭頭所示,創(chuàng)建一個子類的對象,通過子類的對象調(diào)用父類的函數(shù),遇到Serialize()時父類函數(shù)去找子類的定義,然后再將剩余的動作完成,將Serialize()延緩到子類去定義,這種用法就叫做Template Method(23種設(shè)計模式之一,Method是java中的函數(shù))。
這種設(shè)計模式適合做框架,MFC大量用到Template Method。

具體代碼實(shí)現(xiàn)(仿真):


22.png

12.2 繼承+復(fù)合關(guān)系下的構(gòu)造與析構(gòu)

23.png

在圖示繼承+復(fù)合關(guān)系下構(gòu)造函數(shù)與析構(gòu)函數(shù)的順序?yàn)椋?br> (1)構(gòu)造由內(nèi)而外。Derived的構(gòu)造函數(shù)首先調(diào)用Base的default構(gòu)造函數(shù),然后調(diào)用Component的構(gòu)造函數(shù),最后才執(zhí)行自己。

Derived::Derived(...):Base(  ),Component(){ ... };

(2) 析構(gòu)由外而內(nèi)。Derived的析構(gòu)函數(shù)首先執(zhí)行自己,然后調(diào)用Component的析構(gòu)函數(shù),最后調(diào)用Base的析構(gòu)函數(shù)。

Derived::~Derived(...){ ... ~Component(),~Base(  ) };
24.png

在圖示繼承+復(fù)合關(guān)系下構(gòu)造函數(shù)與析構(gòu)函數(shù)的順序?yàn)椋?br> (1)構(gòu)造由內(nèi)而外。Derived的構(gòu)造函數(shù)首先調(diào)用Component的default構(gòu)造函數(shù),然后調(diào)用Base的構(gòu)造函數(shù),最后才執(zhí)行自己。

Derived::Derived(...):Component(),Base(  ){ ... };

(2) 析構(gòu)由外而內(nèi)。Derived的析構(gòu)函數(shù)首先執(zhí)行自己,然后調(diào)用Base的析構(gòu)函數(shù),最后調(diào)用Component的的析構(gòu)函數(shù)。

Derived::~Derived(...){ ... ~Base(  ) ,~Component()};

12.3 多態(tài)實(shí)例

功能最強(qiáng)大的是委托+繼承設(shè)計方式。
委托+繼承設(shè)計實(shí)例1(Observer):


25.png

左邊為放數(shù)據(jù)的class,右邊為觀察的class,左邊可以有很多的右邊,左邊數(shù)據(jù)裝有指向右邊指針的容器,右邊可以被繼承,將來創(chuàng)建的子類is a Observer都可以放在容器里面,所以可以產(chǎn)生不同的Observer。
左邊要提供注冊和注銷的功能,如圖attach傳入Observer放到容器里頭。左邊還應(yīng)該有notify把所有Observer進(jìn)行遍歷,去通知Observer,內(nèi)容由Observer寫好,左邊調(diào)用。


26.png

如圖所示有多個窗口看同一份文件或者不同角度看同一份數(shù)據(jù)。實(shí)現(xiàn)代碼如下:
27.png

委托+繼承設(shè)計實(shí)例2(Composite):
28.png

文件系統(tǒng):Primitive代表文件,Composite是一個容器,容納很多個Primitive和Composite,所以設(shè)計一個父類Component,Primitive和Composite都is a Component,Component和Composite是委托的關(guān)系。add函數(shù)和容器類似,add不能設(shè)計為純虛函數(shù),因?yàn)镻rimitive不能定義。

委托+繼承設(shè)計實(shí)例3(Prototype):
我需要一個樹狀繼承體系,子類未來才被派生,不知道未來子類的名稱。讓派生的子類創(chuàng)建一個自己當(dāng)成原型Prototype,讓我有辦法去看到子類創(chuàng)建出來的的原型放在什么位置上。


29.png

在LandSatImage類里面放一個靜態(tài)的對象LAST,他的類型是LandSatImage(自己),構(gòu)造函數(shù)寫成私有的,通過私有的構(gòu)造函數(shù)調(diào)用addPrototype,將自己掛上去,addPrototype是父類寫的,它將得到的指針放到容器里頭去,這樣就可以使破折號下面創(chuàng)建的原型放到上面去,可以被上面看到。子類準(zhǔn)備一個函數(shù)clone,它new一個自己。破折號以上可以通過原型(這是一個對象)可以調(diào)用clone這個函數(shù),做出一個副本,如果沒有原型則不能。
clone不能是靜態(tài)函數(shù),因?yàn)殪o態(tài)函數(shù)的調(diào)用需要class name,這里沒有。
實(shí)現(xiàn)代碼如下:


30.png

31.png

32.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容