重讀C++之一:封裝、繼承和多態

  • 導讀

        前段時間重新看了一下C++,一是感覺清晰了許多,二是覺得若是換個角度看的話,會有不一樣的體會,並且也容易記住C++中的一些特性。本文就試圖將集合論中的相關知識引入到C++的封裝、繼承、多態上,讓我們對它有個重新的認識。
        從代碼的角度而言,我認爲計算機語言都不可不免的解決以下兩個問題:
        1.爲了構建大型的程序,需要將代碼模塊化。C++中,由類的封裝來實現。
        2.爲了減少代碼的冗餘,需要實現代碼共享。C++中,由類的繼承和多態來實現。

  • 封裝

        C語言中,代碼之間的關係都是函數式的調用。這裏面牽扯到對數據的操作,若操作的都是局部變量,那一切都太平了。但若是幾個函數操作同一個非局部變量,考慮到模塊化,那麼就要將變量和操作變量的函數整合在一起,這就是C++中的封裝。
        C++裏面引入了class的概念,目的是封裝數據和數據上的操作,使其成爲一個獨立的模塊。若是將這個獨立的模塊(代碼和數據)想象成集合,那個class A的集合爲:


圖一

        此時若再引入一個class B,則有下面四種可能性,情況三、四實際上類似。


圖二

        情況一,只需要封裝就足夠了。處理情況二、三、四時,爲了考慮代碼共享,需要引入繼承機制。

  • 繼承

        我們先考慮情況二,由於A和B有公共代碼(成員函數或者是成員變量),故通常考慮將公共的部分定義爲class C,然後由A、B去繼承它。


圖三

        對於情況三、四,我們不需要演變,直接讓A繼承B,或者B繼承A即可。
        若,此時引入class D,那麼情況就會複雜很多。簡單期間,以情況二爲擴展,考慮添加class D後的某一種。後續你會發現,情況三、四類似。


圖四

        此時,最合理的方式是引入四個類,class E, class F, class G, class H,如下圖。E爲基類,F、G、H爲一級子類、A、B、D爲二級子類。


圖五

        但是,這種解決方案有問題:
        1.若是再添加class I,class J,那複雜度就可想而知了。
        2.雖然代碼冗餘是消除了,但是引入了四個類,也着實有點多,更嚴重的話會導致“類氾濫”。
        爲了能統一解決添加的類D,我們將圖四拆分成D和A,以及D和B的關係。這樣就轉化爲圖二中的一種:情況二。


圖六

        圖六中,class H表示D和A的公共部分,class G表示D和B的公共部分。此種解法雖然有代碼冗餘,但簡單了許多,事實上,我們很多時候處理類,就是這麼處理的。
        在這種情況下,若是添加class I,class J,都可以轉化爲:新添加類和已有類之間的單獨關係,即圖二中的四種情況。
        同時,也可以發現,我們無法在類的繼承結構中完全消除代碼冗餘,原因是多個類的情況下,實在是比較複雜。
        當我們在使用這些包含繼承結構的類的時候,考慮圖二的情況三,若B繼承自A,那麼實際上B也可以當A用的,這很好理解,本來A就是B的一部分。但若是,想讓A代表B呢(實際上就是B對象,只是用的時候當A用),爲了完美解決這個問題,就要引入多態了。

  • 多態

        前面的分析可知,類之間的關係都可以簡化爲圖二的情況。圖二的情況三中,A當B用(實際上只有B對象)又分爲以下三種情況。第三種情況有點彆扭,可能是需求決定的吧。
        1.使用B中的A部分。直接使用A操作即可。
        2.使用B中的非A部分。需要將A轉化爲B纔可使用。
        3.B覆蓋定義A的公共接口或者成員變量。當B作爲A使用的時候,A中的公共接口或者成員變量是在非A中的,實現這一機制的就是多態。
        C++中,基類定義虛函數,子類可以重新實現它,以實現多態。令人奇怪的是,沒有虛成員變量的概念,我覺得可能有以下幾個原因:
        1.沒必要提供虛成員變量。父類的成員變量屬於存儲空間,是可以直接用。不像函數,屬於代碼無法直接替換。
        2.可能編譯器要實現這個會比較複雜吧。
        3.封裝的概念是少暴露成員變量,只暴露接口。因此,好的類的設計是沒有公共的成員變量的,也就不存在虛成員變量一說了。
        但是,從完整性的角度而言,應該提供虛成員變量的。

  • 總結

        上面的分析可以看出,引入集合,只是說明我試圖用一種簡單的方式來描述C++的封裝、繼承和多態。所有的源頭,都是因爲在C++中引入了封裝機制,也就是傳說中的面向對象。繼承和多態都是隨之而來的,順着這條路徑走下去,你會發現C++裏面的變量的可視性(public, protected, private)等都是源自於上面描述的集合之間的關係。而所有的這些,只是用來解決兩個問題:模塊化和代碼共享。

  • 參考資料

        1.C++ Primer 中文版 第4版

發佈了86 篇原創文章 · 獲贊 10 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章