重讀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版
相關文章
- 封裝、繼承和多型封裝繼承多型
- c++學習(1)--C++封裝、繼承、多型C++封裝繼承多型
- go語言中的封裝,繼承和多型Go封裝繼承多型
- 面向2-封裝、繼承、多型封裝繼承多型
- aardio 實現封裝繼承多型封裝繼承多型
- python極簡教程07:封裝、多型和繼承Python封裝多型繼承
- [c++] 繼承和多型整理二C++繼承多型
- 封裝和繼承封裝繼承
- Java入門教程九(封裝繼承多型)Java封裝繼承多型
- java封裝繼承以及多型(含程式碼)Java封裝繼承多型
- Java的三大特性:封裝、繼承、多型Java封裝繼承多型
- 繼承和多型繼承多型
- 多型和繼承多型繼承
- 物件導向三大特性-----封裝、繼承、多型物件封裝繼承多型
- 物件導向三大特徵(封裝/繼承/多型)物件特徵封裝繼承多型
- c# 中的封裝、繼承、多型詳解C#封裝繼承多型
- JAVA物件導向基礎--封裝 繼承 多型Java物件封裝繼承多型
- Go語言封裝、繼承、介面、多型和斷言的案例Go封裝繼承多型
- [JAVA] Java物件導向三大特徵:封裝、繼承、多型Java物件特徵封裝繼承多型
- Python 繼承 和 多型Python繼承多型
- OOP的多型和繼承OOP多型繼承
- C++ 繼承、多型、虛擬函式C++繼承多型函式
- js:物件導向程式設計,帶你認識封裝、繼承和多型JS物件程式設計封裝繼承多型
- java 的 四 個 基 本 特 性 ——封裝 繼承 多型 抽象Java封裝繼承多型抽象
- 物件導向的三個基本特徵是:封裝、繼承、多型物件特徵封裝繼承多型
- C中的繼承和多型繼承多型
- 繼承與多型繼承多型
- 【Java】瘋狂Java基礎(一)——物件導向的特徵:繼承、封裝和多型Java物件特徵繼承封裝多型
- java核心思想物件導向三大特性(封裝,繼承,多型)Java物件封裝繼承多型
- 封裝和多型封裝多型
- Python中的繼承和多型Python繼承多型
- 太極1:繼承和多型2繼承多型
- java繼承與多型Java繼承多型
- 繼承+多型+抽象類繼承多型抽象
- python 基礎之繼承、重寫、多繼承Python繼承
- 物件導向基礎(1)--繼承 多型 重構物件繼承多型
- C#中繼承和多型的研究C#中繼繼承多型
- 【Java學習筆記】繼承和多型Java筆記繼承多型