近期重新看了一下C++,一是感覺(jué)清晰了許多,二是覺(jué)得若是換個(gè)角度看的話,會(huì)有不一樣的體會(huì),并且也容易記住C++中的一些特性。本文就試圖將集合論中的相關(guān)知識(shí)引入到C++的封裝、繼承、多態(tài)上,讓我們對(duì)它有個(gè)新的認(rèn)識(shí)。
一、封裝
C語(yǔ)言中,代碼之間的關(guān)系都是函數(shù)式的調(diào)用。這里面牽扯到對(duì)數(shù)據(jù)的操作,若操作的都是局部變量,那一切都太平了。但若是幾個(gè)函數(shù)操作同一個(gè)非局部變量,考慮到模塊化,那么就要將變量和操作變量的函數(shù)整合在一起,這就是C++中的封裝。
C++里面引入了class的概念,目的是封裝數(shù)據(jù)和數(shù)據(jù)上的操作,使其成為一個(gè)獨(dú)立的模塊。若是將這個(gè)獨(dú)立的模塊(代碼和數(shù)據(jù))想象成集合,那個(gè)class A的集合為:
此時(shí)若再引入一個(gè)class B,則有下面四種可能性,情況三、四實(shí)際上類似。
情況一,只需要封裝就足夠了。處理情況二、三、四時(shí),為了考慮代碼共享,需要引入繼承機(jī)制。
二、繼承
我們先考慮情況二,由于A和B有公共代碼(成員函數(shù)或者是成員變量),故通??紤]將公共的部分定義為class C,然后由A、B去繼承它。
對(duì)于情況三、四,我們不需要演變,直接讓A繼承B,或者B繼承A即可。
若此時(shí)引入class D,那么情況就會(huì)復(fù)雜很多。簡(jiǎn)單起見(jiàn),以情況二為擴(kuò)展,考慮添加class D后的某一種。后續(xù)你會(huì)發(fā)現(xiàn),情況三、四類似。
此時(shí),最合理的方式是引入四個(gè)類,class E, class F, class G, class H。E為基類,F(xiàn)、G、H為一級(jí)子類、A、B、D為二級(jí)子類。
但是,這種解決方案有問(wèn)題:
1.若是再添加class I,class J,那復(fù)雜度就可想而知了。
2.雖然代碼冗余是消除了,但是引入了四個(gè)類,也著實(shí)有點(diǎn)多,更嚴(yán)重的話會(huì)導(dǎo)致“類泛濫”。
為了能統(tǒng)一解決添加的類D,我們將圖四拆分成D和A,以及D和B的關(guān)系。這樣就轉(zhuǎn)化為圖2中的一種:情況二。
圖6中,class H表示D和A的公共部分,class G表示D和B的公共部分。此種解法雖然有代碼冗余,但簡(jiǎn)單了許多,事實(shí)上,我們很多時(shí)候處理類,就是這么處理的。
在這種情況下,若是添加class I,class J,都可以轉(zhuǎn)化為新添加類和已有類之間的單獨(dú)關(guān)系,即圖2中的四種情況。
同時(shí),也可以發(fā)現(xiàn),我們無(wú)法在類的繼承結(jié)構(gòu)中完全消除代碼冗余,原因是多個(gè)類的情況下,實(shí)在是比較復(fù)雜。
當(dāng)我們?cè)谑褂眠@些包含繼承結(jié)構(gòu)的類的時(shí)候,考慮圖2的情況三,若B繼承自A,那么實(shí)際上B也可以當(dāng)A用的,這很好理解,本來(lái)A就是B的一部分。但若是想讓A代表B呢(實(shí)際上就是B對(duì)象,只是用的時(shí)候當(dāng)A用),為了完美解決這個(gè)問(wèn)題,就要引入多態(tài)了。
三、多態(tài)
由前面的分析可知,類之間的關(guān)系都可以簡(jiǎn)化為圖2的情況。圖2的情況三中,A當(dāng)B用(實(shí)際上只有B對(duì)象)又分為以下三種情況。第三種情況有點(diǎn)別扭,可能是需求決定的吧。
1.使用B中的A部分。直接使用A操作即可。
2.使用B中的非A部分。需要將A轉(zhuǎn)化為B才可使用。
3.B覆蓋定義A的公共接口或者成員變量。當(dāng)B作為A使用的時(shí)候,A中的公共接口或者成員變量是在非A中的,實(shí)現(xiàn)這一機(jī)制的就是多態(tài)。
C++中,基類定義虛函數(shù),子類可以重新實(shí)現(xiàn)它,以實(shí)現(xiàn)多態(tài)。令人奇怪的是,沒(méi)有虛成員變量的概念,我覺(jué)得可能有以下幾個(gè)原因:
1.沒(méi)必要提供虛成員變量。父類的成員變量屬于存儲(chǔ)空間,可以直接用。不像函數(shù),屬于代碼無(wú)法直接替換。
2.可能編譯器要實(shí)現(xiàn)這個(gè)會(huì)比較復(fù)雜吧。
3.封裝的概念是少暴露成員變量,只暴露接口。因此,好的類的設(shè)計(jì)是沒(méi)有公共的成員變量的,也就不存在虛成員變量一說(shuō)了。但是,從完整性的角度而言,應(yīng)該提供虛成員變量的。