GeekBand C++ 第三周

11. 組合與繼承

Object Oriented Programming,Object Oriented Design,OOP,OOD

1. Composition(復合),表示has-a

Composition

實心菱形+箭頭 Class A 指向 Class B,則Class A has-a Class B,即為復合關系。

Class A has-a Class B,但是Class A沒有添加任何新的功能,只是開放了部分Class B的功能,這種情況表現出了Adapter設計模式。

Composition關系下的構造和析構

構造由內而外
??Container的構造函數首先調用Component的default構造函數,然后才執行自己的。當需要編譯器自己生成時,則需要調用default構造函數,如果不需要調用default構造函數,則需要自己在initialization list寫明。

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

析構由外而內
??Container的析構函數首先執行自己的,然后才調用Component的default析構函數。

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

2. Delegation(委托),Composition by reference。

Delegation

空心菱形+箭頭 Class A 指向 Class B,則Class A Delegation Class B。

Class A 中有一個 Class B 的 pointer。任何時候,A可以調用B的方法完成任務。更恰當的表述方法是 composition by reference,它和composition的區別:

當Class A 和Class B 是composition關系時,有了A,則同時有了B,生命周期同步。而delegation,也許先有了A,此時僅僅有了B的指針,當需要時,才去創建B,生命周期不同步。

在delegation下,Class A 僅僅是對外的接口,而真正的實現都放在Class B中,這種寫法稱作pimpl(point to implantation)也稱為Handle/Body。

Class A內部的指針,不僅僅局限于指向Class B,而且當B需要變動時,Class A不用重新編譯,僅僅需要重新編譯Class B,所以此種寫法又稱為編譯防火墻。

3. Inheritance(繼承),表示is-a

Inheritance

直線+空心三角形,由子類指向父類,表示inheritance關系。
??繼承關系有三種,public,protect,private。其中public繼承,表示is-a的關系。

struct _List_node_base{
    _List_node_base* _M_next;
    _list_node_base* _M_prev;
}:
typeplate<typename _Tp>
struct _List_node
    : public _List_node_base{
    _Tp _M_data;    
};

上段代碼使用了public繼承,因為只有數據,沒有方法,表示子類繼承了父類的數據。

Inheritance關系下的構造和析構
??Class Derived 繼承 Class Base,此時我們稱Derived object 中含有 Base Part。

構造由內而外
??Derived的構造函數首先調用Base的default構造函數,然后才執行自己。

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

析構由外而內
??Derived的析構函數首先執行自己,然后才調用Base的default析構函數。

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

base class 的 dtor 必須是 virtual,否則會出現 undefined behavior。

12 - 14 . 虛函數與多態

1. Inheritance(繼承) with virtual functions(虛函數)

virtual functions分為三種:

  • non-virtual函數:你不希望derived class重新定義(override,復寫)它。
  • virtual函數:你希望derived class重新定義(override,復寫)它,且你對它已經有默認定義。
  • pure virtual函數:你希望derived class一定要重新定義(override,復寫)它,你對它沒有默認定義。
class Shape{
public:
    virtual void draw() const = 0;//pure virtual
    virtual void error(cosnt std::string& msg);//impure virtual
    int objectID() cosnt;//non-virtual
};

class Rectangle: public Shape{...};
class Ellipse: public Shape{...};

2. Template Method

Template Method

父類CDocument中的OnFileOpen()函數無法確定Serialize()中的具體行為,因此將它聲明為虛函數,由子類去完成,在實際的OnFileOpen()函數執行過程中,調用的Serialize()也完全取決于子類。則對于父類CDocument中的OnFileOpen()函數,是一種在Application framework,此種寫法被稱為Template Method。

對于虛函數Serialize()的調用,真正的this指針是CMyDoc類型的,當調用時,也同樣是通過this指針來調用,因此當調用虛函數Serialize()時,調用的是CMyDoc的Serialize(),而非父類的Serialize()。

#include<iostream>
using namespace std;

class CDocument{
public:
    void OnFileOpen(){
        //這是個算法,每個cout輸出代表一個實際動作
        cout << "dialog..." << endl;
        cout << "check file status..." << endl;
        cout << "open file..." << endl;
        Serialize();
        cout << "close file..." << endl;
        cout << "..." << endl;
    }
    
    virtual void Serialize(){ };
};

class CMyDoc : public CDocument{
public:
    virtual void Serialize(){
        //只有應用程序本身才知道如何讀取自己的文件(格式)
        cout << "CMyDoc::Serialize()" << endl;
    }
};

int main(){
    CMyDoc myDoc;
    myDoc.OnFileOpen();
    return 0;
}

輸出結果:

dialog...
check file status...
open file...
CMyDoc::Serialize()
close file...
...

3. Inheritance+Composition關系下的構造和析構

第一種情況:

#include<iostream>
using namespace std;

class Component{
public:
    Component(){ cout << "Component ctor called" << endl; }
    ~Component(){ cout << "Component dtor called" << endl; }
};

class Base{
public:
    Base(){ cout << "Base ctor called" << endl; }
    virtual ~Base(){ cout << "Base dtor called" << endl; }
};

class Derived : public Base{
public:
    Derived(){ cout << "Derived ctor called" << endl; }
    ~Derived(){ cout << "Derived dtor called" << endl; }
private:
    Component component;
};

int main(){
    Derived *pDerived = new Derived;
    delete pDerived;
    getchar();
    return 0;
}

運行結果:

Base ctor called
Component ctor called
Derived ctor called
Derived dtor called
Component dtor called
Base dtor called

由運行結果可以看出:

  • 構造由內而外,Derived首先調用了Base的默認構造函數,然后再調用Component的默認構造函數,最后調用自己的。
  • 析構由內而外,Derived首先調用自己的析構函數,然后再調用Component的析構函數,最后調用Base的析構函數。

第二種情況:

#include<iostream>
using namespace std;

class Component{
public:
    Component(){ cout << "Component ctor called" << endl; }
    ~Component(){ cout << "Component dtor called" << endl; }
};

class Base{
public:
    Base(){ cout << "Base ctor called" << endl; }
    virtual ~Base(){ cout << "Base dtor called" << endl; }
private:
    Component component;
};

class Derived : public Base{
public:
    Derived(){ cout << "Derived ctor called" << endl; }
    ~Derived(){ cout << "Derived dtor called" << endl; }
};

int main(){
    Derived *pDerived = new Derived;
    delete pDerived;
    getchar();
    return 0;
}

運行結果:

Component ctor called
Base ctor called
Derived ctor called
Derived dtor called
Base dtor called
Component dtor called

由運行結果可以看出:

  • 構造由內而外,Derived首先調用了Component的默認構造函數,然后再調用Base的默認構造函數,最后調用自己的。
  • 析構由內而外,Derived首先調用自己的析構函數,然后再調用Base的析構函數,最后調用Component的析構函數。

4. Delegation(委托)+Inheritance(繼承)

23個設計模式,最經典的面向對象程序設計。

Observer

當一個對象的改變需要同時改變其他對象的時候,而且它不知道具體有多少對象有待改變時,應該考慮使用觀察者模式。
??觀察者模式所做的工作其實就是在解除耦合。讓耦合的雙方都依賴于抽象,而不是依賴于具體。從而使得各自的變化都不會影響另一邊的變化。


Observer
  • Subject類,可翻譯為主題或抽象通知者,一般用一個抽象類或者一個借口實現。它把所有對觀察者對象的引用保存在一個聚集里,每個主題都可以有任何數量的觀察者。抽象主題提供一個借口,可以增加和刪除觀察者對象。

  • Observer類,抽象觀察者,為所有的具體觀察者定義一個借口,在得到主題的通知時更新自己。這個借口叫做更新接口。抽象觀察者一般用一個抽象類或者一個接口實現。更新接口通常包含一個Update()方法。

  • ConcreteSubject類,叫做具體主題或具體通知者,將有關狀態存入具體通知者對象;在具體主題的內部狀態改變時,給所有等級過的觀察者發出通知。通常用一個具體子類實現。

  • ConcreteObserver類,具體觀察者,實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。具體觀察者角色可以保存一個指向一個具體主題對象的引用。

Composite

組合模式:將對象組合成樹形結構以表示“部分-整體”的層次結構。Composite使得用戶對單個對象和組合對象的使用具有一致性。

有時候又叫做部分-整體模式,它使我們樹型結構的問題中,模糊了簡單元素和復雜元素的概念,客戶程序可以向處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。

組合模式讓你可以優化處理遞歸或分級數據結構。有許多關于分級數據結構的例子,使得組合模式非常有用武之地。關于分級數據結構的一個普遍性的例子是你每次使用電腦時所遇到的:文件系統。文件系統由目錄和文件組成。每個目錄都可以裝內容。目錄的內容可以是文件,也可以是目錄。按照這種方式,計算機的文件系統就是以遞歸結構來組織的。如果你想要描述這樣的數據結構,那么你可以使用組合模式Composite。


Composite
  • 抽象構件角色(component):是組合中的對象聲明接口,在適當的情況下,實現所有類共有接口的默認行為。聲明一個接口用于訪問和管理Component子部件。這個接口可 以用來管理所有的子對象。(可選)在遞歸結構中定義一個接口,用于訪問一個父部件,并在合適的情況下實現它。
  • 樹葉構件角色(Leaf):在組合樹中表示葉節點對象,葉節點沒有子節點。并在組合中定義圖元對象的行為。
  • 樹枝構件角色(Composite):定義有子部件的那些部件的行為。存儲子部件。在Component接口中實現與子部件有關的操作。
  • 客戶角色(Client):通過component接口操縱組合部件的對象。

Prototype

當有類似這種需求,需要去創建未來的class,但是卻不知道未來具體的calss名稱。那么可以要求派生的子類,自己創建一個自己,創建出的對象,可以被框架看到的,那么就可以用來當一種原型,當框架獲得原型之后,可以通過調用原型的clone函數來創建對象。

uml圖中表示類的框圖,框中變量帶下劃線的,則為static變量?,F在object name,再寫type name。函數名稱前帶‘+’則為public,‘#’代表protect,‘-’代表private。

類的static變量,一定謹記需要在類外定義,此時才給變量分配內存,所以稱之為定義,類內部應該是聲明。


Prototype

在上圖中,破折線以下的類,是以后創建的,創建之前,破折線以上的框架是不知道這個類的名稱,所以也無法創建,但是此時巧妙的使用了Prototype模式,這個問題迎刃而解。


Prototype

在Image類的定義中,有兩個重要的純虛函數需要子類實現,一個是returnType函數,用來返回子類的類型,用來區分不同的子類,另一個是clone函數,用來讓框架使用原型來創建新的對象。findAndClone函數則返回該類型新的對象(調用clone函數)。


Prototype3

在Image的子類定義中,需要兩個構造函數,一個是給static變量調用的,用來把原型加到框架中,而另一個構造函數是給clone函數使用的。如果只有一個默認構造函數,當調用clone函數是,則會造成原型重復加入框架,因此這里需要兩個構造函數。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容