組合與繼承
Composition(復(fù)合)
??????? 一個(gè)類(lèi)包含另一個(gè)類(lèi)(has-a),可以通過(guò)Adapter改造一個(gè)類(lèi),即一個(gè)對(duì)象為另一個(gè)對(duì)象的子對(duì)象(成員)。
?????? ?在構(gòu)造時(shí)由內(nèi)而外,先調(diào)用內(nèi)部類(lèi)的構(gòu)造函數(shù),在構(gòu)造自己;在析構(gòu)時(shí),先析構(gòu)自己再析構(gòu)內(nèi)部類(lèi)。
Delegation(委托)
?????? ?一個(gè)類(lèi)里面有一個(gè)指針指向另一個(gè)類(lèi),當(dāng)前類(lèi)委托另一個(gè)類(lèi)實(shí)現(xiàn)各種操作。可以多個(gè)對(duì)象里面的指針指向同一個(gè)委托對(duì)象。兩個(gè)類(lèi)的生命周期不一致。
? ? ? ? Handle/Body模式:將類(lèi)的接口和類(lèi)的實(shí)現(xiàn)相互分開(kāi),對(duì)實(shí)現(xiàn)類(lèi)的修改不會(huì)影響對(duì)外的使用。其相比繼承而言有更大的靈活度。
Inheritance(繼承)
??? class class_name2:?access_specifiers?class_name1
??? {
??? }
??????? class1完全包含class2的內(nèi)容,calss1為基類(lèi),class2為派生類(lèi)。其構(gòu)造與析構(gòu)順序和復(fù)合關(guān)系一樣,不同的是對(duì)于編譯器來(lái)說(shuō)class2對(duì)象本身就是一個(gè)class1對(duì)象。class1的引用和指針完全可以綁定到class2對(duì)象上。即
class2 cl2;
class1 &cl1=cl2;
class1 *p=&cl2;
??????? 完全是合法的。但是cl1還是一個(gè)class1的引用,p還是一個(gè)class1的指針,所以需要一個(gè)class2類(lèi)型的指針和引用的地方,不能用p和cl1來(lái)代替。
虛函數(shù)與多態(tài)
??????? 基類(lèi)的析構(gòu)函數(shù)必須為virtual(虛函數(shù))。
??????? non-virtual函數(shù):不能被子類(lèi)重新定義(override);
??????? virtual函數(shù):基類(lèi)已經(jīng)默認(rèn)定義了,但是希望子類(lèi)重新定義;
??????? pure virtual函數(shù):沒(méi)有默認(rèn)定義,子類(lèi)必須重新定義;對(duì)于含有純虛函數(shù)的類(lèi)成為抽象類(lèi),不能產(chǎn)生實(shí)例,只能被派生類(lèi)繼承,且如果派生類(lèi)沒(méi)有重新定義的話(huà),派生類(lèi)也為一個(gè)抽象類(lèi)。
繼承+復(fù)合關(guān)系下的構(gòu)造與析構(gòu)
?????? 構(gòu)造時(shí)先構(gòu)造Component,然后構(gòu)造基類(lèi)Base,最后才構(gòu)造Derived。析構(gòu)時(shí)先調(diào)用Derived的析構(gòu)函數(shù),然后析構(gòu)Base最后進(jìn)行Component的析構(gòu)。
經(jīng)測(cè)驗(yàn)Base的構(gòu)造函數(shù)會(huì)先于Component的構(gòu)造函數(shù)調(diào)用;析構(gòu)時(shí)Component的析構(gòu)函數(shù)先于Base的析構(gòu)函數(shù)調(diào)用。
委托+繼承(composite)
???
??????? 為了讓composite中的容器能夠放入primitive和composite兩種不同的類(lèi),那么可以讓這兩個(gè)類(lèi)有同一個(gè)基類(lèi)(繼承)。然后基類(lèi)的指針(委托)可以指向任何一個(gè)派生類(lèi)。
??????? 基類(lèi)不知道會(huì)派生出哪些派生類(lèi),而有需要知道派生出的類(lèi)的名稱(chēng)。在寫(xiě)出派生類(lèi)之后,派生類(lèi)創(chuàng)建一個(gè)自己這種靜態(tài)對(duì)象(LSAT),然后把自己的靜態(tài)對(duì)象添加到基類(lèi)的addPrototype里面的指針中。然后需要?jiǎng)?chuàng)建某一個(gè)派生類(lèi)對(duì)象時(shí)通過(guò)相應(yīng)的靜態(tài)對(duì)象中的clone函數(shù)復(fù)制一個(gè)當(dāng)前對(duì)象,然后為復(fù)制出的對(duì)象的變量賦值完成一個(gè)對(duì)象的創(chuàng)建。
??????? 參考資料sourcemaking.com/design_patterns/prototype;該網(wǎng)站列出了一些參考示例,使用prototype的目的,與存在的問(wèn)題。
作業(yè)過(guò)程中面臨的問(wèn)題與解決方案
???????? 在本次作業(yè)中有兩套不同的解決方案,一種是按照普通的繼承與復(fù)合關(guān)系完成的;另一種是通過(guò)prototype設(shè)計(jì)模式進(jìn)行作業(yè)(未完成)。
??????? 在普通的繼承關(guān)系中存在以下問(wèn)題:
??????? 1. 作業(yè)中要求通過(guò)一個(gè)傳統(tǒng)數(shù)組保存兩種類(lèi)型的對(duì)象,但是對(duì)于數(shù)組的描述中明確表示了數(shù)組是相同類(lèi)型的數(shù)據(jù)按照一定的數(shù)序排列。
???????? 解決方案是,由于兩中類(lèi)都是同一個(gè)類(lèi)Shape的派生類(lèi),而Shape形指針又完全可以指向Shpae及其派生類(lèi)的對(duì)象,所以可以創(chuàng)建一個(gè)Shape形指針數(shù)組,以達(dá)到題目要求。
??????? 2. 由于數(shù)組中是保存的Shape形指針,當(dāng)指針解引用時(shí)也被編譯器當(dāng)作了一個(gè)Shape形的對(duì)象。在輸出流操作符<<重載時(shí),為了使std::cout滿(mǎn)足操作習(xí)慣不能采用成員函數(shù)形式重載。而在普通函數(shù)重載時(shí)就要為Circle與Rectangle各自進(jìn)行一個(gè)重載,參數(shù)如下:
std::ostream& operator <<(std::ostream &os, Rectangle const &re);
std::ostream& operator <<(std::ostream &os, Circle const &ce);
可以看到區(qū)別它們之間不同的為形參列表,但是我的對(duì)象是由Shape指針保存的,不能有效區(qū)分,導(dǎo)致不能調(diào)用相應(yīng)的流操作符。
??????? 解決方案為:可以采用以下兩種方案,將expression轉(zhuǎn)換為type_id對(duì)象的指針或引用
? dynamic_cast<type-id>(expression)
? static_cast < type-id > ( expression )
在進(jìn)行由派生類(lèi)道基類(lèi)的轉(zhuǎn)化時(shí),它們倆沒(méi)什么區(qū)別。但是當(dāng)由基類(lèi)向派生類(lèi)轉(zhuǎn)化時(shí),dynamic_cast具有類(lèi)型檢查,比如expression確實(shí)指向一個(gè)派生類(lèi)對(duì)象時(shí),沒(méi)問(wèn)題返回一個(gè)派生類(lèi)的指針,當(dāng)expression沒(méi)有指向type-id類(lèi)時(shí)返回一個(gè)空指針,可用于判斷。而static_cast由于沒(méi)有類(lèi)型檢查所以下行轉(zhuǎn)換時(shí)時(shí)不安全的。
??????? 3. 對(duì)于**ptr指針作為函數(shù)形參時(shí)const的使用如下:
? ? ? ? ? const MyStructure *? ? ? *ppMyStruct;
??????? // ptr --> ptr --> const MyStructure
??????? MyStructure *const *ppMyStruct;
??????? // ptr --> const ptr --> MyStructure
??????? MyStructure *? ? ? *const ppMyStruct;
?????? ?// const ptr --> ptr --> MyStructure
??????? 如上,如果ptr所指的指針與指針?biāo)傅闹颠€有ptr本身都不會(huì)在函數(shù)中改變的話(huà),可以采用 const *const *const ptr;這種方式。沒(méi)有找到對(duì)這種用法的相關(guān)建議是否應(yīng)該少用之類(lèi)的,但是這種用法確實(shí)對(duì)于理解有一定的困難。
在使用prototype設(shè)計(jì)模式進(jìn)行作業(yè)
? ? ? ? 1. 這種設(shè)計(jì)模式強(qiáng)調(diào)我們?cè)谶\(yùn)行時(shí)去創(chuàng)建一個(gè)對(duì)象時(shí),不需要完全的重新初始化對(duì)象,并且不需要知道對(duì)象屬于哪一個(gè)派生類(lèi)也能夠創(chuàng)建出相應(yīng)的對(duì)象。但是如果兩個(gè)派生類(lèi)所需的構(gòu)造函數(shù)形參列表不同,就不能通過(guò)*shape=class_name()來(lái)改變一個(gè)對(duì)象里面的值,和上面一樣也要通過(guò)類(lèi)型的強(qiáng)制轉(zhuǎn)換“dynamic_cast(expression)”。而像示例代碼這樣添加一個(gè)draw()純虛函數(shù),用以改變對(duì)象的值,但是純虛函數(shù)在復(fù)寫(xiě)的時(shí)候又要求形參列表要和基類(lèi)一樣,而circle和rectangle所需的參數(shù)數(shù)量肯定是不一樣的,最后只有采用笨辦法dynamic_cast改變值。但是網(wǎng)上說(shuō)用了dynamic_cast就代表類(lèi)設(shè)計(jì)上就有問(wèn)題,一個(gè)完好的類(lèi)的設(shè)計(jì)是不需要用到dynamic_cast的~~~~
? ? ? ? 2. 這種設(shè)計(jì)模式在規(guī)模較小的程序中,代碼復(fù)雜度提升太大,為了實(shí)現(xiàn)這個(gè)模式而添加的代碼都差不多和普通的一個(gè)派生類(lèi)的所有代碼行數(shù)相當(dāng)。反而不如普通的直接繼承更方便,也許是我反應(yīng)慢,照著(sourcemaking.com)里面的示例代碼編寫(xiě)了一個(gè)以這種模式為基礎(chǔ)的作業(yè),感覺(jué)有時(shí)候自己都看不懂了~~~~
總結(jié):本章最難的我認(rèn)為就數(shù)設(shè)計(jì)模式這部分了,特別是prototype這種,看示例代碼好像很簡(jiǎn)單,但是一到自己寫(xiě)這種設(shè)計(jì)模式的代碼的時(shí)候,就完全不知道該寫(xiě)什么了呢。反而不明不白的寫(xiě)了好多完全沒(méi)有用的代碼。