白馬非馬
----繼承
作者:HolyFire
古人云:“白馬非馬”,為何如是說呢。
這里有個講究,白馬是指一種白色的馬,而非馬里的馬指的是各種各樣的馬,有黑馬,棕嗎,五花馬等等,這是在顏色上有所區別,馬還有產地,品種等等區別。這里給了我們一個啟示,各種各樣的馬可以找出很多共同點,稱之為馬,符合這些特點的動物就是馬了,所以這些共同點不能多也不能少,多了不能同用于所有的馬,少了表達不出馬來。要區別一只馬需要很多信息,比如一只黑色的蒙古馬,在這里,我除了描述一只馬外,還要指出它是黑色的,產地是蒙古,這里還沒有加上馬的品種。
我在《芥子須彌-----封裝》里曾經提及,事物可以劃分成屬性和方法的集合。那么我們是不是可以對這個集合進行再加工呢,我們只要在里面加上一點別的什么,那他就可以變成別的事物了,想象一下在馬的身上加上鹿的角,呵呵呵呵,可笑嗎。那么我們說一些嚴肅的,知道我們中華民族的圖騰嗎,龍!龍是在蛇的頭上加上鹿的角,身上加上虎的腿腳,尾巴是魚的尾巴,等等。但是不是隨隨便便就能造就龍這樣偉大的象征的,不好好設計,就會成為畫蛇添足。
雖然是簡單和相加,但也是有不同的方式的。一般來說有兩種方式:關聯和衍生。
關聯:假設原來的類是用一個袋子將屬性和方法包起來,那是用關聯描述了多個類之間的關系,最簡單的情況就是用一個更大的袋子將兩個小袋子包起來,成為一個新的類。這種關聯叫做組合。
衍生:衍生呢就是用一個大的袋子將一個小的袋子與一些屬性和方法包起來,成為一個新的類
A
C
B
a:關聯
A
D
b:衍生
我還是用C++來表示一下
class A{
//...屬性+方法
};
class B{
//...屬性+方法
};
//關聯后的新類C
class C{
A a;
B b;
};
//衍生后的新類D
class D : public A{
//...屬性+方法
};
看起來關聯(組合)比較容易理解,即將類作為一個屬性,然后形成一個新的類,這和原來類的使用沒什么區別,自己定義的類就是一個類型,這正是封裝要做的。
衍生—在原來的類的基礎上加上一些屬性和方法產生一個新類,看起來新穎誘人,在面包上灑一些芝麻,吃起來更有香味,不是嗎。
如何好好利用這一方法,就是我要說的----繼承,只要用心去做,面包也會非常美味。(這里提及的都是共有繼承也就是class Y: public X;的形式)
上面的方法說起來簡單,要是用好它,不是件容易的事。
首先D和A有相同的部分就是A
第二,D有A沒有的部分
光光這兩點還說明不了繼承的用途
我們加上一個新類E,他也是與類A衍生得來的
class E : public A{
//...屬性+方法
};
這樣我們就清楚一點了,A是D和E的共同點
耶~~~~!我們終于發現這樣做的好處了。
類D和類E有相同的部分A,而這里A只被處理了一次,也就是說我們可以少處理一個和A一模一樣的工作,如果有許多個類都是從A衍生而來的,那么我們就發達了,問題是,如何讓更多的類可以與類A衍生得到有用并好用的類。
這里老祖宗又顯靈了,白馬非馬說的就是這個道理,馬正是這個類A,加上顏色,產地,品種,就可以描述各種各樣的馬了,加上健康等信息還能說明馬的狀態,如一只活蹦亂跳的黑色蒙古馬。
在我們的知識里有很多已知的共同點,碗,瓷器,風,人,花草,我們將他們成為統稱。
我們可以下一個定義了:繼承--就是將事物之間相同和相似的部分歸納出來。
我們來看一下基類A和子類D的關系,可以發現D中含有A,也就是說可以從一個D類的對象中提取出一個A類的對象來,這種現象就是繼承的向下性,馬可以是白馬,而白馬就不能說成是馬這個種類的代表。表現在C++中如何處理呢。
D d;
A a = d;
這里將會產成一個A類型的臨時變量,它是由d中類A那部分組成的
[
d的組成
][類A的部分]--à臨時變量da;
…
]
A a = da;
這種現象叫做切片
再看另一種情況
D * d = new D;
A * a = d;
這時候A * a = d;只涉及到指針的賦值,沒有對象的創建,所以不會引起切片,這里是將d在內存中的地址保存在a這個指針里,而描述指針指向的對象是一個類A的對象。由于,D中關聯A的信息,C++編譯器使得這樣的操作是合法的,也是可以理解的。
我們可以把繼承A的D稱作為A的某個類型,是類A的一種(或者說,D是一個A),就象白馬是一種馬一樣(白馬是一匹馬)。
繼承非常強大,使用它能得到很多好處,但是世界上沒有包治百病的,過分的運用繼承也會帶來麻煩。可以看出繼承是一種靜態的關系,在編譯期間很多東西都定下來了,這也提高了效率,但是要改變很困難,所以靈活性還不夠,要想清楚,你的目的是什么,再決定使用關聯還是繼承。
|