今天來看一看關(guān)于面向?qū)ο蟮木幊痰囊恍﹥?nèi)容,也就是關(guān)于類和類之間的關(guān)系传惠。類和類之間的關(guān)系其實一種抽象的描述現(xiàn)實世界的方法卦方。
現(xiàn)實生活中的事物不都和之前提到的String或者Complex一樣與其他事務(wù)沒有太多的聯(lián)系盼砍,其實從不同角度看到事物往往會得出不同的結(jié)論浇坐。比如近刘,生物界的劃分經(jīng)常是以界門綱目科屬種這樣的思路來劃分的臀晃,找出其共性案淋,然后再***向上抽象 ***得出一個概述性的大類险绘。比如隆圆,生物學(xué)上渺氧,人被分類為真核總界 動物界 后生動物亞界 后口動物總門 脊索動物門 脊椎動物亞門 羊膜總綱 哺乳綱 真獸亞綱 靈長目 類人猿亞目 狹鼻猴次目 類人猿超科 人科 人亞科 人族 人屬 智人種;貓被分類為真核總界 動物界 后生動物亞界 后口動物總門 脊索動物門 脊椎動物亞門 羊膜總綱 哺乳綱 真獸亞綱 食肉目 貓亞科 貓屬 貓種(引自百度百科)慨默。對比貓和人不難發(fā)現(xiàn)弧腥,在真獸亞綱之前和人類沒有區(qū)別虾攻,但是從目開始霎箍,人屬于靈長目漂坏、而貓咪屬于食肉目顶别,從才分道揚(yáng)鑣驯绎。那這些分類對于解釋這個世界到底有什么幫助嗎条篷?這個幫助是很自然的赴叹,比如每往下細(xì)分一個層次乞巧,就意味著區(qū)分的細(xì)節(jié)越細(xì)绽媒,而越往上則越來越抽象。又比如猎提,真核生物中分四個界:原生生物界、真菌界棺聊、植物界和動物界限佩。就拿最熟悉的植物界和動物界來看祟同,他們的區(qū)別是顯著的耐亏,但卻別的點(diǎn)往往是粗糙的沪斟。
畢竟我對生物學(xué)沒有太深的了解,但是這樣一種分類方法在認(rèn)識和解釋世界的過程中卻是通用的槽奕。我們希望通過計算機(jī)來人和世界結(jié)合粤攒,那么還是需要對這樣的認(rèn)識方法有一些了解夯接。
C++相較于C的發(fā)展盔几,主要是出現(xiàn)了對象的概念逊拍,而對象是由各式各樣的class構(gòu)造而成,那么class和class之間的關(guān)系就變得特別重要世曾。因為人和人之間就像是對象和對象之間的關(guān)系肿轨,他們都是同一種class所構(gòu)造而成的,但是屬性卻不相同(性別椒袍、名 字驼唱、名族、膚色驹暑、身高玫恳、體重、性格优俘,生命周期都不一定相同)京办,雖然存在著這些差異,但每個人都還是屬于人類的 (class 人)帆焕。但是人和貓的卻別不僅僅是對象之間的差別了惭婿,更是物種之間的差別了。但是這兩個class之間也并不是完全沒有相同點(diǎn),比如這兩個物種小的時候都得喝奶等等的相同點(diǎn),把這喝奶的的這個特點(diǎn)向上抽取,那么就得到了一個新的class(哺乳綱)[ 可以是** 抽象類 ** ],人和貓這兩個class和新產(chǎn)生的class之間發(fā)生關(guān)系就行了,因為這足夠表示出這倆的關(guān)系了。最后怒坯,我們會得到一棵像樹一樣龐大的一個體系归敬,通過這個體系限番,我們就能很方便的解釋節(jié)點(diǎn)(class之間的關(guān)系)躯舔。
類的基本思想是** 數(shù)據(jù)抽象(data abstraction)** 和 ** 封裝(encapsulation)。數(shù)據(jù)抽象就是一種依賴于 接口(Interface)和 實現(xiàn)(implementation)**。類的接口包括用戶所能執(zhí)行的操作;類的實現(xiàn)則包括類的數(shù)據(jù)成員、負(fù)責(zé)接口實現(xiàn)的函數(shù)體以及定義類所需的各種私有函數(shù)。
通過這樣的抽象缠局,不能方便描述class之間的關(guān)系妙啃,還更好的將其中的數(shù)據(jù)進(jìn)行封裝。再拿人和貓來舉例铭拧,貓身上通常都一身漂亮的皮毛搀菩,但人身上基本沒有(如果說人身上的汗毛其實不一定比貓少,那么這就是種詭辯了)。這樣會有一個巨大的好處,讓貓身上的毛很直接的就屬于貓咪吭练,而不會出現(xiàn)在人類的class中。這就實現(xiàn)了非常好的*** 封裝 ***的效果。
這樣的操作圆到,對于數(shù)據(jù)的封裝有著巨大的好處富稻,但是哪怔,這之間也又帶有了一個巨大的方便篷扩,比如,貓咪身上的毛,是貓咪自己的私有屬性宅楞,這時候饲趋,同樣貓也可以具有一個函數(shù)换棚,這個函數(shù)專門用于操作他的私有成員屬性(貓身上的毛),而人則不需要這樣的函數(shù)(方法)爆侣,原因是因為沒有人類這樣的私有成員屬性无虚。
那么為了描述這樣的關(guān)系离唬,我們主要討論class之間的3中關(guān)系嘱函。
三種關(guān)系
-
Inheritance(繼承)
-
表示is-a的關(guān)系
struct _List_node_hase { _List_node_base * _M_next; _List_node_base * _M_prev; }; template <typename _Tp> struct _List_node : public _List_node_base { _Tp _M_data; };
-
UML圖
-
父類的數(shù)據(jù)在public的繼承關(guān)系下祖灰,能夠被完全繼承
父類函數(shù)的調(diào)用權(quán)也可以被子類進(jìn)程
-
Inheritance關(guān)系下的構(gòu)造和析構(gòu)
- 構(gòu)造:由內(nèi)而外
- Derived的構(gòu)造函數(shù)首先調(diào)用Base的default構(gòu)造函數(shù)咖祭,然后才執(zhí)行自己
- 析構(gòu):由外而內(nèi)
- Derived的析構(gòu)函數(shù)首先執(zhí)行自己,然后才調(diào)用Base的析構(gòu)函數(shù)
- 構(gòu)造:由內(nèi)而外
-
虛函數(shù)
- non-virtual函數(shù):不希望Derived class重新定義(override)
- virtual函數(shù):希望Derived class 重新定義,且已有默認(rèn)的定義
- pure virtual函數(shù):希望Derived class一定要重新定義(override),并且沒有默認(rèn)定義
-
Composition(復(fù)合)
has-a的關(guān)系
-
我把他定義為人和長在身上的器官的關(guān)系
template<class T, class Sequence = deque<T> > class queue { .... protected : Sequence c; //底層容器 public: //以下完全利用c的操作函數(shù)完成 bool empty() const { return c.empty(); } //其中的操作均為調(diào)用 Sequence c;中所實現(xiàn)的功能 size_type size() const { return c.size(); } .... }
- 在queue中驾凶,持有一個
Sequence c
的對象狭郑,queue和Sequence之間的關(guān)系就是Composition -
其中UML圖為
queue 和 sequence 為Composition的關(guān)系- 其中黑色菱形一段表示持有另外一個class的對象
- Adapter設(shè)計模式主要使用了Composition來實現(xiàn)黄绩。Adapter可以很好的將部分功能封裝和開放
- 在queue中驾凶,持有一個
-
Composition所占用內(nèi)存的大小等于他持有的所有的成員所占內(nèi)存的總和
-
Composition關(guān)系下的構(gòu)造和析構(gòu)
- 構(gòu)造:由內(nèi)而外(類似于打包裹)
- Container的構(gòu)造函數(shù)首先調(diào)用Component的default構(gòu)造函數(shù),然后才執(zhí)行自己的構(gòu)造函數(shù)
Container:: Container(...) : Component()//Component()由編譯器完成添加
- 如果不滿意調(diào)用默認(rèn)構(gòu)造函數(shù)那么需要自己來寫需要的構(gòu)造函數(shù)在初始化列表
- Container的構(gòu)造函數(shù)首先調(diào)用Component的default構(gòu)造函數(shù),然后才執(zhí)行自己的構(gòu)造函數(shù)
- 析構(gòu):由外而內(nèi)(相當(dāng)于拆包裹)
- Container的析構(gòu)函數(shù)首先執(zhí)行桑谍,然后才會調(diào)用Component的析構(gòu)函數(shù)
Container:: ~Container(...){ ... ~Component(); } //~Component()有編譯器添加
- Container的析構(gòu)函數(shù)首先執(zhí)行桑谍,然后才會調(diào)用Component的析構(gòu)函數(shù)
- 只需要將自己的構(gòu)造和析構(gòu)完成即可奠骄,順序會有編譯器自動來完成
- 構(gòu)造:由內(nèi)而外(類似于打包裹)
從生命周期的角度來看,這兩個對象之間的關(guān)系為“同生共死”
我理解Composition類似于人身上的器官含鳞,這些器官由他自己的功能影锈,比如我們需要吸收能力來維持生命,那么我們只需要調(diào)用我們自己的消化系統(tǒng)的消化功能(
消化系統(tǒng).消化(食物)蝉绷;
)鸭廷,類似于這樣的過程,我們就完成了吸收營養(yǎng)的過程熔吗,但實際這個過程不是人類這個class完成的辆床。并且一旦人死去,這些器官也會死去(這里可不討論捐獻(xiàn)移植拔荨K显亍!V械)咨堤,并且也是人先死,之后的器官一個一個死去晒他。
-
Delegation(委托)
Composition by reference(不持有需要的對象吱型,只持有目標(biāo)對象的指針)
-
我把這個關(guān)系定義為我們和東西關(guān)系
class StringRep; class String{ ..... private: StringRep* rep;//持有目標(biāo)對象的指針 }
-
UML圖示為
用指針相連,所以兩個對象的生命周期不同
對于這個關(guān)系陨仅,我把他理解為我們和東西關(guān)系津滞,比如我們和手機(jī),我們需要和遠(yuǎn)處的朋友聊天灼伤,我們可以用手機(jī)來完成(只需要調(diào)用手機(jī)的打電話函數(shù)
手機(jī)->打電話(電話號碼);
)触徐,就相當(dāng)于我們用我們持有的手機(jī)的指來打了一個電話。那為什么是指針呢狐赡,因為我們并不是真正擁有一部手機(jī)撞鹉,我們擁有的只是他的使用權(quán)和處置權(quán)(說白了就是他們都是我們的身外之物,生不帶來死不帶去,在過去鸟雏,大家都共用一部電話享郊,那時候只有使用權(quán),也就反應(yīng)了指針?biāo)笇ο蟮奶攸c(diǎn)孝鹊,可以多個指針變量同時指向一個內(nèi)存中的對象)炊琉。我們可以換手機(jī)(也就是讓我們持有的指針指向另外一個手機(jī)),我們?nèi)绻麤]有手機(jī)的時候又活,那么這個指針指向的就是一個NULL苔咪;我們想要擁有一個手機(jī)的前提是我們必須買一個(也就是為了持有該對象的前提是必須要手動創(chuàng)建)。同樣柳骄,我們和這個對象的生命周期并沒有綁定的關(guān)系团赏,我們死了,手機(jī)還可以用耐薯,手機(jī)壞了舔清,我們健康的活著。所以Delegation的關(guān)系就是一種生不帶來死不帶去的關(guān)系可柿,不過和我們與東西之間的關(guān)系的區(qū)別在于鸠踪,這個類里面有這個指針才有資格擁有這個東西的使用權(quán),如果沒有啊复斥,那也是求不來的营密。這里面有個很大的好處就是,我們可以不斷的換手機(jī)目锭,而對于你的朋友來說他們知不知道你還沒換沒手機(jī)其實不重要评汰,但對你來說,你可以換一臺更好的手機(jī)痢虹,更漂亮的手機(jī)被去。搜易通過Delegation可以作為一個接口,背后通過他持有的指針進(jìn)行實現(xiàn)所需要的功能奖唯,也就是相當(dāng)于惨缆,朋友只要通過號碼就能聯(lián)系你,實現(xiàn)了聯(lián)系的功能丰捷,但這個功能與你用哪部手機(jī)沒多大關(guān)系坯墨,也就實現(xiàn)了不同功能的切換,這也就相當(dāng)于(Handle/Body)的設(shè)計模式的一種現(xiàn)實生活的表現(xiàn)病往,非常方便的幫你完成了實現(xiàn)類的升級捣染。-
淺談幾種設(shè)計模式
這些類與類之間的關(guān)系,構(gòu)成了大量的設(shè)計模式停巷,由于之后專門有課程討論設(shè)計模式的問題耍攘,在這里簡單使用圖例來表示幾種設(shè)計模式榕栏,來說明三種關(guān)系的使用場景。- Template Method(模版方法)
- Observer模式
- Composite(組合模式)
- Prototype(原型模式)