C++面向?qū)ο蟾呒?jí)編程 part3
@(boolan C++)[C++]
概述
面向?qū)ο蟮娜N關(guān)系
- composition 組合
- delegation 委托
- 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 的關(guān)系漆际。
composition的關(guān)系表示法:
Adapter模式
Adapter模式: 新的類(lèi)類(lèi)型組合包含已有的類(lèi)型對(duì)象却紧,新的類(lèi)型的功能完全由已有的類(lèi)型實(shí)現(xiàn)衡奥,新的類(lèi)型是已有類(lèi)型的功能的簡(jiǎn)化(類(lèi)似 adapter的功能)曲尸。
2.composition 關(guān)系下的構(gòu)造和析構(gòu)
內(nèi)存結(jié)構(gòu)
構(gòu)造和析構(gòu)順序
構(gòu)造順序由內(nèi)而外,析構(gòu)順序由外而內(nèi)。
- Container首先調(diào)用Component的default構(gòu)造函數(shù),后調(diào)用自己的構(gòu)造函數(shù)俊马。
圖中紅色部分為編譯器行為。
- Container首先調(diào)用自己的析構(gòu)函數(shù)肩杈,后調(diào)用Component的析構(gòu)函數(shù)柴我。
注意??:
構(gòu)造函數(shù)的默認(rèn)行為,編譯器默認(rèn)在構(gòu)造函數(shù)的初始化列表中調(diào)用成員對(duì)象的default構(gòu)造函數(shù)扩然。所以在初始化列表中顯式初始化成員對(duì)象效率要高于在class body內(nèi)初始化成員函數(shù)(避免了重復(fù)初始化的動(dòng)作)艘儒。
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的關(guān)系表示法
delegation 雖然能夠訪(fǎng)問(wèn)某對(duì)象索守,但其成員對(duì)象是指向某對(duì)象的指針晕窑。指針成員指向的對(duì)象的創(chuàng)建和析構(gòu)都不一定由我控制抑片。
delegation中指針成員的生命周期和其所指對(duì)象的生命周期不一致卵佛。compositon中生命周期一致。
pImpl模式/ (Handle/Body)
p for pointer敞斋。
- pImpl將實(shí)現(xiàn)的聲明分離截汪,提供了靈活性。這種靈活性來(lái)源于delegation中指針成員生命周期與其指向?qū)ο蟮牟灰恢隆?/strong>
string的引用計(jì)數(shù)和copy on write
引用計(jì)數(shù):string中采用引用計(jì)數(shù)的方式在內(nèi)容相同對(duì)象間共享數(shù)據(jù)植捎。
copy on write:共享數(shù)據(jù)的對(duì)象間如果有人要改寫(xiě)自己的數(shù)據(jù)衙解,則copy共享的數(shù)據(jù)到新分配的內(nèi)存(目的是不破壞共享數(shù)據(jù))。
copy on write 應(yīng)用:string 對(duì)象間拷貝不會(huì)創(chuàng)建新的內(nèi)存焰枢,因?yàn)橛幸糜?jì)數(shù)機(jī)制蚓峦,僅在copy之后要改寫(xiě)對(duì)象才會(huì)創(chuàng)建新內(nèi)存(copy on write)舌剂。
4. Inheritance/ 繼承. Is - a
繼承,委托,組合都是面向?qū)ο蟆?/p>
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;
}
繼承的表示方法
T表示 Template class
public繼承
- public繼承 is-a關(guān)系。
- 繼承的價(jià)值在于與虛函數(shù)搭配暑椰。
派生類(lèi)成員對(duì)基類(lèi)成員的訪(fǎng)問(wèn)
- 基類(lèi)的private成員霍转,只有基類(lèi)和基類(lèi)的友元可以訪(fǎng)問(wèn)。
- 基類(lèi)的public一汽,protected成員避消,派生列表中使用的訪(fǎng)問(wèn)標(biāo)號(hào)決定該成員在派生類(lèi)中的訪(fǎng)問(wèn)級(jí)別
派生列表中的訪(fǎng)問(wèn)標(biāo)號(hào)
訪(fǎng)問(wèn)標(biāo)號(hào)僅影響基類(lèi)的public,potected成員在派生類(lèi)中的訪(fǎng)問(wèn)級(jí)別召夹。
- public繼承:基類(lèi)成員在派生類(lèi)中的訪(fǎng)問(wèn)級(jí)別保持不變岩喷。
- prtoceted繼承: 基類(lèi)的public,protected成員在派生類(lèi)中為protected成員监憎。
- private繼承:基類(lèi)的public纱意,protected成員在派生類(lèi)中為private成員。
::無(wú)論以何種方式繼承枫虏,派生類(lèi)對(duì)基類(lèi)成員的訪(fǎng)問(wèn)權(quán)限一致妇穴,繼承類(lèi)型僅影響派生類(lèi)用戶(hù)對(duì)基類(lèi)成員的訪(fǎng)問(wèn)級(jí)別。::
Inheritance關(guān)系下的構(gòu)造和析構(gòu)
內(nèi)存模型
構(gòu)造和析構(gòu)順序
構(gòu)造由內(nèi)而外隶债, 析構(gòu)由外而內(nèi)腾它。類(lèi)似于compositon的順序。
Derived 的構(gòu)造函數(shù)死讹,先調(diào)用base的default構(gòu)造函數(shù)瞒滴,后調(diào)用自己的構(gòu)造函數(shù)。base的default構(gòu)造函數(shù)在derived的構(gòu)造初始化列表中被默認(rèn)調(diào)用
Derived::Dervied(…): Base() {};
Derived的析構(gòu)函數(shù)赞警,先執(zhí)行自己的析構(gòu)函數(shù)妓忍,后調(diào)用base的析構(gòu)函數(shù)。
Derived::~Derived(…) {… ~Base()};
base的析構(gòu)函數(shù)必須是virtual
如果多態(tài)基類(lèi)的析構(gòu)函數(shù)是non-virtual的愧旦,會(huì)造成“局部對(duì)象銷(xiāo)毀”世剖,僅銷(xiāo)毀了基類(lèi)的對(duì)象。
虛函數(shù)與多態(tài)
1. derived class 繼承了 base class 的哪些東西笤虫?
- 內(nèi)存數(shù)據(jù)
- 函數(shù)的調(diào)用權(quán)
derived class 繼承了base class 的函數(shù)調(diào)用權(quán)旁瘫,所以 derived class 可以調(diào)用base class的函數(shù)。
2. Inheritance with virtual function
注意??:
virutal函數(shù)的設(shè)計(jì)取決于derived class 是否想要重新定義(override/ 覆蓋)base class的已有定義琼蚯。這里要區(qū)分重載(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
- non-virtual : 不希望derived class 重新定義(override / 覆蓋)它(base class function member)。
- virtual: 希望derived class重新定義它遭庶,且它已有默認(rèn)定義宁仔。
- pure virtual:希望derived class一定要重新定義它,且它沒(méi)有默認(rèn)定義峦睡。
理解???♂?:
- 虛函數(shù)的聲明在base class中指定翎苫, 控制derived class對(duì)接口的繼承能力权埠。
- 虛函數(shù)使derived class繼承了base class 接口的同時(shí),讓dervied class具有進(jìn)化該接口行為的能力煎谍。
注意??:
- 不能創(chuàng)建具有純虛函數(shù)類(lèi)型的對(duì)象弊知。
- 繼承于純虛類(lèi)的dervied class 中具有純虛類(lèi)對(duì)象。
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的做法將已實(shí)現(xiàn)base類(lèi)型的部分功能秩彤,延緩實(shí)現(xiàn),將其交由derived class 實(shí)現(xiàn)事哭。
將Application Framework框架和Application實(shí)現(xiàn)分離漫雷。
理解
- template method即 ,在其實(shí)現(xiàn)中調(diào)用base class virtual function的base class non-virtual function 鳍咱。
- 好處:
derived class 可以復(fù)用base class 中non-virtual function實(shí)現(xiàn)中通用的框架/流程/接口降盹,但針對(duì)不同derived class object的調(diào)用 non-virtual function 的行為略有差異(差異在non-virtual function中調(diào)用virtual function)。
virtual function調(diào)用過(guò)程
注意下圖中this指針的作用谤辜。
3. Inheritance + Composition 關(guān)系下的構(gòu)造和析構(gòu)
內(nèi)存模型/UML關(guān)系
構(gòu)造由內(nèi)而外蓄坏,析構(gòu)由外而內(nèi)
Derived 的構(gòu)造函數(shù)首先調(diào)用base的default 構(gòu)造函數(shù),
然后調(diào)用Component的default構(gòu)造函數(shù)丑念,
最后調(diào)用自己的構(gòu)造函數(shù)涡戳。
Derived::Derived(...) : Base(),Component() {...};
Derived 首先調(diào)用自己的析構(gòu)函數(shù),
然后調(diào)用Component的析構(gòu)函數(shù)脯倚,
最后調(diào)用base的析構(gòu)函數(shù)
Derived::~Derived(){... ~Componet(),~Base()};
委托+繼承設(shè)計(jì)
設(shè)計(jì)思想:用composition(組合)/delegation(委托)/inheritance(繼承)三個(gè)工具渔彰,去設(shè)計(jì)解決現(xiàn)實(shí)問(wèn)題的方法。
理解 1:委托應(yīng)用于設(shè)計(jì)的靈活性在于對(duì)象創(chuàng)建的靈活性推正,delegation class a對(duì)象可以通過(guò)指針的方式間接擁有某對(duì)象恍涂,該被擁有的對(duì)象創(chuàng)建方式可以很動(dòng)態(tài)。
理解 2: 委托+繼承強(qiáng)化了委托的應(yīng)用植榕,鑒于base指針可以指向derived class對(duì)象再沧。
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關(guān)系圖
1. 類(lèi)似文件系統(tǒng)的問(wèn)題解決
文件系統(tǒng)問(wèn)題?
如何設(shè)計(jì)目錄的數(shù)據(jù)結(jié)構(gòu)尊残?目錄中既有文件類(lèi)型炒瘸,又包含目錄類(lèi)型。
用composite設(shè)計(jì)模式/ delegation + inheritance 解決問(wèn)題
- composite解決的問(wèn)題夜郁?用一種結(jié)構(gòu)能夠同時(shí)保存多種不同類(lèi)型的數(shù)據(jù)什燕。
- base類(lèi)指針數(shù)組粘勒,解決上述問(wèn)題竞端,注意必須是指針數(shù)組,因?yàn)橹羔樀拇笮」潭ā?/li>
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
- 靜態(tài)成員的表示方法庙睡,在成員的名稱(chēng)下面添加下劃線(xiàn)
- 數(shù)據(jù)成員的表示方法事富,成員名稱(chēng)在前技俐,類(lèi)型在后。
Prototype要解決的問(wèn)題
- 在base類(lèi)種如何創(chuàng)建未來(lái)才會(huì)設(shè)計(jì)的類(lèi)型對(duì)象统台。在不知道對(duì)象類(lèi)型的前提下創(chuàng)建對(duì)象雕擂。
- 主要發(fā)生在框架設(shè)計(jì)中,框架設(shè)計(jì)者不知道未來(lái)使用者的類(lèi)型贱勃。
注意??:
- prototype中井赌,是在dervied類(lèi)自己創(chuàng)建對(duì)象,不是使用者創(chuàng)建dervied對(duì)象贵扰,所以dervied類(lèi)中包含一個(gè)static derived類(lèi)對(duì)象仇穗。
- derived類(lèi)自己創(chuàng)建對(duì)象并注冊(cè)到base類(lèi)中。
- prototype中戚绕,base類(lèi)中創(chuàng)建derived類(lèi)對(duì)象纹坐,而不是base類(lèi)/dervied類(lèi)對(duì)象中的base對(duì)象