boolan/C++面向對象高級編程 part3

C++面向對象高級編程 part3

@(boolan C++)[C++]


概述

面向對象的三種關系

  1. composition 組合
  2. delegation 委托
  3. inheritance 繼承

組合與繼承

1. composition 組合 has a

template <class T> {
class queue {
...
protected:
    deque<T> c;

public:
    bool empyt(){ return c.empty()} 
    size_type size() {return c.size();}
    reference front() { return c.front();}
    reference back() {return c.back();}
    void push(const value_type& x) {c.push_back(x);}
    void pop()(c.pop_front();)
}

composition 是has a 的關系。

composition的關系表示法:

1509872201654.png

Adapter模式

Adapter模式: 新的類類型組合包含已有的類型對象,新的類型的功能完全由已有的類型實現,新的類型是已有類型的功能的簡化(類似 adapter的功能)。

2.composition 關系下的構造和析構

內存結構

1509872239077.png

構造和析構順序

構造順序由內而外,析構順序由外而內。

  1. Container首先調用Component的default構造函數,后調用自己的構造函數。
    圖中紅色部分為編譯器行為
1509872269289.png
  1. Container首先調用自己的析構函數,后調用Component的析構函數。
1509872282635.png

注意??:
構造函數的默認行為,編譯器默認在構造函數的初始化列表中調用成員對象的default構造函數。所以在初始化列表中顯式初始化成員對象效率要高于在class body內初始化成員函數(避免了重復初始化的動作)。

3. Delegation/委托。 Composition by reference

delegation 即Composition by reference。
通常不講pointer,僅講reference。

// file string.hpp
class stringrep;

class string {
public:
....
private:
    stringrep* rep;  // pimpl
};
// string.cpp
#include "string.hpp" 

namespace {
class stringrep {
friend class string;
int count;
char* rep;
};
}

Delegation的關系表示法

1509872298319.png

delegation 雖然能夠訪問某對象,但其成員對象是指向某對象的指針。指針成員指向的對象的創建和析構都不一定由我控制。

delegation中指針成員的生命周期和其所指對象的生命周期不一致。compositon中生命周期一致。

pImpl模式/ (Handle/Body)

p for pointer。

  1. pImpl將實現的聲明分離,提供了靈活性。這種靈活性來源于delegation中指針成員生命周期與其指向對象的不一致。

string的引用計數和copy on write

1509872311553.png

引用計數:string中采用引用計數的方式在內容相同對象間共享數據。
copy on write:共享數據的對象間如果有人要改寫自己的數據,則copy共享的數據到新分配的內存(目的是不破壞共享數據)。

copy on write 應用:string 對象間拷貝不會創建新的內存,因為有引用計數機制,僅在copy之后要改寫對象才會創建新內存(copy on write)。

4. Inheritance/ 繼承. Is - a

繼承,委托,組合都是面向對象。

struct _List_node_base{
    _List_node_base* _M_next;
    _List_node_base* _M_prev;
}

template<typename _Tp>
struct _List_node :public _List_node_base
{
    _Tp _M_data;
}

繼承的表示方法

1509872324297.png

T表示 Template class

public繼承

  1. public繼承 is-a關系。
  2. 繼承的價值在于與虛函數搭配。

派生類成員對基類成員的訪問

  1. 基類的private成員,只有基類和基類的友元可以訪問。
  2. 基類的public,protected成員,派生列表中使用的訪問標號決定該成員在派生類中的訪問級別

派生列表中的訪問標號

訪問標號僅影響基類的public,potected成員在派生類中的訪問級別。

  1. public繼承:基類成員在派生類中的訪問級別保持不變。
  2. prtoceted繼承: 基類的public,protected成員在派生類中為protected成員。
  3. private繼承:基類的public,protected成員在派生類中為private成員。

::無論以何種方式繼承,派生類對基類成員的訪問權限一致,繼承類型僅影響派生類用戶基類成員的訪問級別。::

Inheritance關系下的構造和析構

內存模型

1509872337816.png

構造和析構順序

構造由內而外, 析構由外而內。類似于compositon的順序。

  1. Derived 的構造函數,先調用base的default構造函數,后調用自己的構造函數。base的default構造函數在derived的構造初始化列表中被默認調用
    Derived::Dervied(…): Base() {};

  2. Derived的析構函數,先執行自己的析構函數,后調用base的析構函數。
    Derived::~Derived(…) {… ~Base()};

base的析構函數必須是virtual

如果多態基類的析構函數是non-virtual的,會造成“局部對象銷毀”,僅銷毀了基類的對象。


虛函數與多態

1. derived class 繼承了 base class 的哪些東西?

  1. 內存數據
  2. 函數的調用權

derived class 繼承了base class 的函數調用權,所以 derived class 可以調用base class的函數。

2. Inheritance with virtual function

注意??:
virutal函數的設計取決于derived class 是否想要重新定義(override/ 覆蓋)base class的已有定義。這里要區分重載(overload)和覆蓋(override)

virtual function

class Shape {
public:
    virutal void draw() const = 0;  // pure virtual
    virtual void error(const std::string& msg);  // impure virtual
    int objectID() const;  // non-virtual
  1. non-virtual : 不希望derived class 重新定義(override / 覆蓋)它(base class function member)。
  2. virtual: 希望derived class重新定義它,且它已有默認定義。
  3. pure virtual:希望derived class一定要重新定義它,且它沒有默認定義。

理解???♂?:

  1. 虛函數的聲明在base class中指定, 控制derived class對接口的繼承能力。
  2. 虛函數使derived class繼承了base class 接口的同時,讓dervied class具有進化該接口行為的能力。

注意??:

  1. 不能創建具有純虛函數類型的對象。
  2. 繼承于純虛類的dervied class 中具有純虛類對象。

template method

class CDocument {
public:
    virutal Serialize(){};
    OnFileOpen() {  // template method
        ...
        Serialize();
    }
}

class CMyDocument : public CDocument {
    virtual Serialize() {....}
}

....

int main () {
    CMyDocument doc;
    doc.OnFileOpen();
}

OnFileOpen就是template method;

template method:

template method的做法將已實現base類型的部分功能,延緩實現,將其交由derived class 實現。
將Application Framework框架和Application實現分離。

理解

  1. template method即 ,在其實現中調用base class virtual function的base class non-virtual function 。
  2. 好處:
    derived class 可以復用base class 中non-virtual function實現中通用的框架/流程/接口,但針對不同derived class object的調用 non-virtual function 的行為略有差異(差異在non-virtual function中調用virtual function)。

virtual function調用過程

注意下圖中this指針的作用。

1509872370689.png

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

內存模型/UML關系

1509872400702.png

構造由內而外,析構由外而內

  1. Derived 的構造函數首先調用base的default 構造函數
    然后調用Component的default構造函數
    最后調用自己的構造函數。
    Derived::Derived(...) : Base(),Component() {...};

  2. Derived 首先調用自己的析構函數,
    然后調用Component的析構函數,
    最后調用base的析構函數
    Derived::~Derived(){... ~Componet(),~Base()};


委托+繼承設計

設計思想:用composition(組合)/delegation(委托)/inheritance(繼承)三個工具,去設計解決現實問題的方法。

理解 1:委托應用于設計的靈活性在于對象創建的靈活性,delegation class a對象可以通過指針的方式間接擁有某對象,該被擁有的對象創建方式可以很動態。

理解 2: 委托+繼承強化了委托的應用,鑒于base指針可以指向derived class對象。

0. Obeserver

class Subject {
    int m_value;
    vector<Observer*> m_views;
public:
    void attach(Observer* obs) {
        m_views.push_back(obs);
    }
    void set_value(int value) {
        m_value = value;
        notify();
    }
    void notify() {
        for(int i = 0; i < m_views.size(); ++i)
            m_views[i]->update(this, m_value);
    }
}


class Observer {
public:
    virtual void update(Subject* sub, int value) = 0;
}

UML關系圖

1509872417801.png

1. 類似文件系統的問題解決

文件系統問題?

如何設計目錄的數據結構?目錄中既有文件類型,又包含目錄類型。

用composite設計模式/ delegation + inheritance 解決問題

1509872430936.png
  1. composite解決的問題?用一種結構能夠同時保存多種不同類型的數據。
  2. base類指針數組,解決上述問題,注意必須是指針數組,因為指針的大小固定。

example code

class Primitive : public Component {
public:
    Primitive(int val): Component(val){}
};

class Component {
    int value;
public:
    Component(int val) {value = val;}
    virutal void add(Component*) {}
};

class Composite: public Component {
    vector<Component*>c;
public:
    Composite(int val):Component(val) {}

    void add(Componet* elem) {
        c.push_back(elem);
    }
}

2. Prototype

UML

1509872444226.png
  1. 靜態成員的表示方法,在成員的名稱下面添加下劃線
  2. 數據成員的表示方法,成員名稱在前,類型在后。

Prototype要解決的問題

  1. 在base類種如何創建未來才會設計的類型對象。在不知道對象類型的前提下創建對象。
  2. 主要發生在框架設計中,框架設計者不知道未來使用者的類型。

注意??:

  1. prototype中,是在dervied類自己創建對象,不是使用者創建dervied對象,所以dervied類中包含一個static derived類對象。
  2. derived類自己創建對象并注冊到base類中。
  3. prototype中,base類中創建derived類對象,而不是base類/dervied類對象中的base對象

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容