GeekBand極客班C++面向對象高級編程(上)第三周筆記

11.組合與繼承

. 遇到復雜問題時鸯隅,需要類與類相關聯(lián)澜建,即面向對象思想

Composition復合

. 表示has-a, (里面有個類)

. 復合類似一種類與類的包含關系


template <class T, class Sequence = deque(T)>

class queue

{

...

protected : ? ? ? ? ? ? ? ? ? ? ? ? //給子類提供接口

? ? Sequence c; ? ? ? ? ? ? ? ? //底層容器

public : ? ? ? ? ? ? ? ? ? ? ? ? ? ? //以下操作函數(shù)完全利用c的操作函數(shù)完成

? ? bool empty() const {return c.empty() ; }

? ? size_type size() const {return c.size() ; }

? ? reference front() {return c.front() ;}

? ? reference back() {return c.back() ; } ? ? ? ?//deque是兩端可進出,queue是末端今前端出fifo

? ? void push (const value_type& x) { c.push_back(x) ; }

? ? void pop() {c.pop_front() ; }

} ; ? ? ? ?//這時候所有的功能deque都可以完成蝌以,則deque借用后炕舵,不需要自己寫新功能了


. 圖示時,用實心菱形加箭頭◆→表示跟畅,箭頭指向的一端為被包涵的類咽筋,實心菱形一端為容器

. 這時候,容器可以借用另外函數(shù)的功能實現(xiàn)自己的功能

. 復合可以將其他類函數(shù)改裝成為自己需要的函數(shù)徊件,即可看作一種Adapter

. 復合可以嵌套

. 從內(nèi)存角度:復合所占大小Sizeof要把所包涵的類一層一層計算進去

Composition復合關系下的構造和析構

. Container◆→Component

. 構造時奸攻,由內(nèi)而外,Container的構造函數(shù)先調用Component的構造函數(shù)虱痕,再執(zhí)行自己


Container::Container(...) : Component() {...} ; ? ? ? ? ? ? //調用的是Component的默認構造函數(shù)


. 析構時睹耐,由外而內(nèi),先執(zhí)行自己部翘,再調用Component的析構函數(shù)


Container :: ~Container(...) { ... ~Conponent() } ;? ? ??


. 編譯器會幫忙調用Component構造和析構函數(shù)硝训,但只能調用默認的

. 如果不想調用默認構造函數(shù),需要自己寫Component構造函數(shù)的調用

Delegation委托. Composition by reference

. 如果是有指針的類,而指針指向另一個類

. 圖示時窖梁,用空心菱形加箭頭◇→表示赘风,箭頭指向的一端為被類包涵的指針所指向的類


class StringRep ;

class String

{

public:

? ? String() ;

? ? String(const char* s) ;

? ? String(const String& s) ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//拷貝構造

? ? String &operator = (const String& s) ; ? ? ? ? ? ? ?//拷貝賦值

? ? ~String() ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //析構

...

Private :

? ? StringRep* rep ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//pimpl

} ;


. 以指針為方式調用另一個類,即為Delegation窄绒,也可成為Composition by reference

. 當用指針相連時贝次,數(shù)據(jù)生命不一致,與Composition相區(qū)別

. →一端當需要時才去創(chuàng)建彰导,◇一端只是對外的接口蛔翅,當需要動作時都調用→一端的類去實現(xiàn)

. 這種寫法叫做pimpl(pointer to implimentation),又叫做Handle/Body

. 當這樣寫類時位谋,前面接口可以保持不變山析,指針后面真正實現(xiàn)的類可以切換,不會影響客戶端

. 又叫做編譯防火墻

. reference counting掏父,例如當有三個object共享同一個rep(改變內(nèi)容互不影響copy on write)

Inheritance繼承笋轨,表示is-a


struct _List_node_base ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//以struct為例子

{

? ? _List_node_base* _M_next ;

? ? _List_node_base* _M_prev ;

} ;

template<typename _Tp>

struct _List_node ?: public _List_node_base ? ? ? ? //子類,將struct從父類繼承過來

{

? ? _Tp _M_data ;

} ;


. 圖示時赊淑,用空心三角形加直線表示?-爵政,橫線一端表示子類,?一端表示父類

. 繼承方式有三種陶缺,public钾挟、protected、private

.. public繼承可以被任意實體訪問

.. protected繼承只允許子類及本類的成員函數(shù)訪問

.. private繼承只允許本類的成員函數(shù)訪問

. 從內(nèi)存角度饱岸,父類數(shù)據(jù)被完整繼承下來到子類掺出,子類對象中包涵父類成分

inheritance繼承關系下的構造和析構

. Derived-?Base

. 由于是也是包含關系,所以與Component類似

. 構造由內(nèi)而外苫费,Derived構造函數(shù)先調用Base的默認構造函數(shù)汤锨,然后再執(zhí)行自己


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


. 析構由外而內(nèi)


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


. base class的dtor必須是virtual,否則會出現(xiàn)undefined behaviour

. 也由編譯器自動完成

12.虛函數(shù)與多態(tài)

Inheritance with virtual functions 帶虛函數(shù)的繼承

. 語法形式百框,函數(shù)前加virtual

. non-virtual函數(shù)闲礼,不希望derived class派生類(子類)重新定義(override,復寫)

. virtual函數(shù)铐维,希望derived class重新定義柬泽,但它自己有默認定義

. pure virtual函數(shù),希望derived class一定要重新定義方椎,它自己沒有默認定義


class Shape

{

public :

? ? virtual void draw() const = 0 ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//pure virtual

? ? virtual void error(const std :: string& msg) ; ? ? ? ? ? ? ? ? ?//impure virtual

? ? int objectID() const ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //non-virtual

...

}

class Rectangle : publicShape{...} ;


. 有時候純虛函數(shù)也可以有定義

. 在類中考慮到繼承問題時聂抢,要考慮搭配虛函數(shù)

. 很多時候在不同軟件中钧嘶,都有某功能相類似棠众,例如文件編輯的軟件中的打開功能,這時候寫一個父類來解決相同操作步驟,將特殊部分列為虛函數(shù)闸拿,以此來提高效率

. 父類可能很久前就寫好的空盼,實際運行main時通過子類對象調用父類函數(shù)


CDocument::OnFileOpen() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //(1)

{

...

? ? Serialize() ? ? ? ? ? ?//函數(shù)中做了一些固定動作把其中的一些關鍵部分延緩到子類去給定,以后由子類寫出

...

}

? ? class CMyDoc :public CDocument ? ? ? ? ? ? ? ? ? ? //(2)

{

virtual Serialize() {...}

} ;

main()

{

? ? CMyDoc myDoc ;

...

? ? myDoc.OnFileOpen() ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //CDocument::OnFileOpen(&myDoc);

} ? ? ? ? ? ? ?//調用順序新荤,通過(2)進入到(1)開始調用揽趾,到S函數(shù)時,調用(2)中virtual苛骨,再回(1)繼續(xù)


. 通過子類調用父類函數(shù)

. 父類中的關鍵動作可延緩到子類去操作篱瞎,叫做Template Method (不是指的模板)

. 在框架中,會設計出同類固定功能痒芝,將無法決定的功能留為虛函數(shù)留給子類去定義

. MFC就是一種Template Method

. 在上面栗子中俐筋,調用Serialize時,編譯器在做這樣的動作:


this->Serialize() ;

(*(this->vptr)[n])(this) ;


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

. Derived既含父類又含Component時严衬,

. Derived含父類澄者,其父類又含Component時,一層一層構造和析構即可

Delegation+Inheritance 委托+繼承

. 委托+繼承的功能最強大


class Observer

{

public :

? ? virtual void update(Subject* sub请琳,int value)=0 ; ? ? ? ? //將來可以派生不同的子類

} ;

class Subject ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//需要很多觀察器粱挡,與Observer是委托關系

{

? ? int m_value ;

? ? vector<Observer*>m_views ; ? ? ? ? ? ? ? //準備一個容器,里面可以放好多Observer的指針

public :

? ? void attach(Observer* obs) ? ? ? ? ? ? ? ? ?//提供注冊功能(還要有注銷功能俄精,栗子沒給出)

? ? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //附著一個Observer

? ? ? ? m_views.push_back(obs) ; ? ? ? ? ? ? //放到容器中

? ? }

? ? void set_val(int value) ? ? ? ? ? ? ? ? ? ? ? ? //

? ? {

? ? ? ? m_value+value;

? ? ? ? notify() ;

? ? }

? ? void notify()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //遍歷并通知所有Observer更新信息

? ? {

? ? ? ? for(int i=0;i<m_views.size();i++)

? ? ? ? m_views[i]->update(this,m_value);

? ? }

}


13.委托相關設計

. 若要寫一個file system或者window system询筏,先要理清需要構造的層次關系,再考慮需要那些class和關系

Composite:以file system為例:

. 先要準備一個primitive嘀倒,也可稱為file

. 另外要準備一個Composite屈留,一個容器,可以容納很多file测蘑,也可以放很多他自己

. Composite還需要一個可以添加Primitive也可以添加他自己的一個函數(shù)

. 這時候需要寫一個Primitive和Composite共同的父類灌危,即Component

. Component中可以寫一個添加函數(shù),這時候Composite就可以委托他實現(xiàn)添加功能

. 這種方法即為設計模式Composite碳胳,是一個委托+繼承模式

. 代碼如下


class Component

{

? ? int value ;

public :

? ? Component(int val){value=val;}

? ? virtual void add(Component*){} ? ? ? ? ?//需要讓Composite重新定義add功能勇蝙,所以寫為虛函數(shù)

} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //但不能是純虛函數(shù),因為Primitive不能有動作

class Composite :public Component ? ?

{

? ? vector<Component*>c; ? ? ? ? ? ? ? ? ? ? //做一個容器存放Component

public :

? ? Composite(int val): Component(val){}

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

...

}

class Primitive :publicComponent

{

public :

? ? Primitive(int val) :Component(val){}

} ;


Prototype

. 框架被建好的時候挨约,因為定義需要被子類來定義味混,這時候不能new,需要new的class name被還沒創(chuàng)建

. 這時使派生的子類都可以new一個自己作為Prototype诫惭,讓框架可以看到Prototype的位置來接收它

. 創(chuàng)建子類時翁锡,安排一個靜態(tài)對象(圖示為加下劃線)作為原型,這個原型必須登記到父類框架端

.. 寫代碼時候線寫typename夕土,再寫objectname

. 父類要留有空間來給子類登記原型

. 靜態(tài)對象構造時馆衔,需要調用構造函數(shù)瘟判,做一個private數(shù)據(jù)(圖示為前加負號,protected圖示為前加#)

. 這時構造函數(shù)只能被自己調用角溃,這個構造函數(shù)需要調用父類添加原型函數(shù)把自己登記到父類框架端

. 父類中添加原型功能可以把得到的原型放入容器

. 子類還需要自己準備一個clone函數(shù)拷获,用來new自己,這時候通過原型對象可以調用clone

. 所有的子類都需要這樣來創(chuàng)建

. 因為靜態(tài)函數(shù)的調用需要classname减细,所以需要這樣做

. 代碼如下


#include<iostream>

enum imageType{LAST , SPOT};

class Image

{

public :

? ? virtual void draw()=0 ;

? ? static Image *findAndClone(imageType) ;

protected :

? ? virtual imageType returnType()=0 ;? ? ? ? ? ? ? ? ? ? ? //純虛函數(shù)匆瓜,一定要子類來寫

? ? virtual Image *clone()=0 ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

? ? static void addPrototype(Image *image)? ? ? ? ? ? //子類聲明后,會將他的原型登記過來

? ? {_prototypes[_nextSlot++]=image;}

private : ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//把添加功能登記的每個原型保存到這里

? ? static Image *_prototypes[10]; ? ? ? ? ? ? ? ? ? ? ? ? ? //這個數(shù)組是自己用來存放原型的容器

? ? static int _nextSlot;

} ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//class中靜態(tài)的data必須在class外進行一次定義

Image *Image::_prototypes[];

int Image::_nextSlot;

Image *Image::findAndClone(imageType type) ? ? ? //客戶需要Image對象實例時候調用這個公開靜態(tài)函數(shù)

{

? ? for(int i=0;i<_nextSlot;i++) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//在容器中尋找需要的class來clone出來

? ? ? ? if(_prototypes[i]->returnType()==type)

? ? ? ? ? ? return _prototypes[i]->clone();

}


class LandSatImage :public Image ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//繼承父類

{

public :

? ? imageType returnType(){return LAST ;}

? ? void draw(){cout<<"LandSatImage::draw"<<_id<<endl ; }?

? ? Image *clone(){return new LandSatImage(1) ; } ? ? ? ? ? ? ? ? ?// 用來new自己 未蝌,調用第二構造驮吱,參數(shù)任意

protected :

? ? LandSatImage(int dummy){_id = _count++; } ? ? ? ? ? ? //第二個構造函數(shù),用來給clone調用的構造函數(shù)

private :

? ? static LandSatImage _LandSatImage ? ? ? ? ? ? ? ? ? ? ? //創(chuàng)建靜態(tài)原型

? ? LandSatImage(){addPrototype(this) ; } ? ? ? ? ? ? ? ? ? ? //讓原型調用父類添加函數(shù)登記到父類端的構造函數(shù)

? ? int _id ;

? ? static int _count ;

} ;

LandSatImage LandSatImage::_landSatImage ;

int LandSatImage::_count = 1 ;


...

在各種設計模式中有很多抽象思考需要構思萧吠,在不斷寫代碼中進行進步

...

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糠馆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子怎憋,更是在濱河造成了極大的恐慌又碌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绊袋,死亡現(xiàn)場離奇詭異毕匀,居然都是意外死亡,警方通過查閱死者的電腦和手機癌别,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門皂岔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人展姐,你說我怎么就攤上這事躁垛。” “怎么了圾笨?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵教馆,是天一觀的道長。 經(jīng)常有香客問我擂达,道長土铺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任板鬓,我火速辦了婚禮悲敷,結果婚禮上,老公的妹妹穿的比我還像新娘俭令。我一直安慰自己后德,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布抄腔。 她就那樣靜靜地躺著瓢湃,像睡著了一般窟赏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箱季,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音棍掐,去河邊找鬼藏雏。 笑死,一個胖子當著我的面吹牛作煌,可吹牛的內(nèi)容都是我干的掘殴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粟誓,長吁一口氣:“原來是場噩夢啊……” “哼奏寨!你這毒婦竟也來了?” 一聲冷哼從身側響起鹰服,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤病瞳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后套菜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡设易,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了戏溺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旷祸。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嫌吠,靈堂內(nèi)的尸體忽然破棺而出掺炭,到底是詐尸還是另有隱情辫诅,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布涧狮,位于F島的核電站炕矮,受9級特大地震影響么夫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜肤视,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一档痪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邢滑,春花似錦腐螟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至摇予,卻和暖如春汽绢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侧戴。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工宁昭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酗宋。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓久窟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親本缠。 傳聞我的和親對象是個殘疾皇子斥扛,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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