面向對象的特性
面向對象(Object Oriented,縮寫爲OO)是現代軟件技術的精髓
三個基本的特性:封裝、繼承與多態。
所謂封裝,就是將某些東西包裝和隱藏起來,讓外界無法直接使用,只能通過某些特定的方式才能訪問。OO將萬物都視爲“對象”(Object),任何對象都具有特性和行爲。我們將其特性稱爲“成員變量” (MemberVarible),將其行爲稱之爲“成員函數"(Member Function),被封裝的特性只能通過特定的行爲去訪問。
大家都見過旅館裏常用的一種茶葉吧,就是用紙袋把茶葉包裝起來再系是一根線。用的時候只需要將其放在水杯裏泡就行。這樣的好處是不會將茶葉渣和茶垢弄的滿杯子都是。
好!這就是一個封裝的例子。
我們喝茶的目的是享受茶葉的香冽;所以茶葉的味道(Flavour)就是茶葉所具有的最
重要特性之一;可是我們無法直接享受它的清香,因爲被外面的紙袋“封裝”起來了。唯一的辦法就是“泡”(Dilute),將茶袋扔在開水中泡,它的味道就出來了,融入水中。
。並且Flavour是私有(Private)的,我們不能直接把它吞進肚子去,而只能通過成員函
數Dilute才能享受Flavour。
下面用C++代碼來描述這個例子:
Class CTea
{
Private:
Cstring m_Flavour; //味道
Cstring m_Color; //顏色
...... //等等其它屬性
Private:
Void CTea(); //構造函數
Void ~CTea(); //析構函數
Public:
Cstring Dilute();//沏茶
...... //等等其它方法
}
Cstring CTea::Dilute()
{
//怎樣泡出味道來的代碼
}
這就是封裝。通過將對象的某些屬性聲明爲Private隱藏起來,只能使用其提供的特定
方法去訪問。
2、繼承(Inheritance)
如果只是封裝,那麼非面嚮對象語言也能部分的做到。比如在C中,用結構(Struct)、
VB中用自定義類型(Type)也能封裝一些變量。
OO最有吸引力的特性是繼承。通俗的說後代具有祖先的某些特點就叫繼承,當然後代還可以具有自己獨有的特徵。舉個例子吧,菜刀。
菜刀(cutlery)是鋼(Steel)做的,鋼是一種金屬(Metal),金屬則是大千世界裏的一種物質(Substance)。所以菜刀的一些特性可以追溯到物質具有的一般屬性。正是因爲這個道理,MFC中所有類均從CObject繼承而來。
這就是繼承。菜刀直接繼承了鋼的特性,鋼又繼承了金屬的特性,......下面的代碼描
述了這種複雜而有獨特的繼承關係:
Class CSubstance
{
Private:
int m_color;
void CSubstance();
void ~CSubstance();
//......(我是學文科的,具體屬性說不上來)
}
Class CMetal:Public CSubstance
{
void CMetal();
void ~CMetal();
//......
}
Class CSteel:Public CMetal
{
void CSteel();
void ~CSteel();
//......
}
Class CCutlery:Public CSteel
{
private:
Cstring m_Blade;
void CCutlery();
void ~CCutlery();
//......
Public:
void Cut();
}
這裏,CSubstance被稱爲基類(Base class),其它被稱爲衍生類(Derived class)。衍生類與基類是“Is kind of”的關係。子類與其祖先類之間複雜的函數調用關係不在本文討論之列。
繼承是一種樹狀的層次關係。子類在繼承祖先類的成員變量和成員函數的同時也可以
定義自己的成員變量和成員函數。比如,Metal 除了繼承了Substance的一般特性外,還具有自己的屬性諸如可延展性;CCutlery在繼承CSteel的特性後還具有自己的成員諸如“刀刃”(Blade)、“鋒利”(Sharpness)、行爲有如“切”(Cut)等。
面向對象技術是對現實生活的抽象,你可以用生活中的經驗去思考程序設計的邏輯。
3、多態性(Polymorphism)
討論多態之前先要明白什麼是“虛擬”(Virtual)。C++/MFC就是用虛擬這種方式實現多態的。爲什麼“虛擬”這個概念?看下邊的例子:
Class Cincect //昆蟲類
{
private:
int m_foot; //腳的數量
...... //其它成員變量
private:
void Cincect();
void ~Cincect();
public:
void Bite()//咬人
{
...... //怎樣咬人的代碼,比如張開嘴啃
}
}
我把Bite(咬)這個動作在基類中定義爲一般化動作。可是,不是所有昆蟲咬
人的方法都一樣(況且還有的根本就不咬人呢,比如蜻蜓),比如蚊子是用嘴那個
吸管叮人而螞蟻是用嘴去夾。
從昆蟲這個類別衍生出以下兩個類別:Cant(螞蟻)、Cmosquito(蚊子)。
class Cant :public Cincect //螞蟻類
{
......
}
class Cmosquito :public Cincect //蚊子類
{
......
}
它們都繼承了Cincect的所有成員,當然也繼承了Bite()這個動作。現在就有問題了:
同樣繼承自昆蟲,當我們使用Bite()這個動作時怎麼才能區分螞蟻和蚊子各自的獨有的咬人方式呢?
方法之一是用“::”符號指明具體引用的是那一個,但這樣明顯失去了靈活性;
另一種方法就是“虛擬”。使用關鍵字virtual將Bite()聲明爲虛擬函數,然後在每個
衍生類中重新定義,描述它們各自的咬人方法,調用的時候也不會都一種結果啦。於是上邊的例子可以改寫爲:
{
private:
int m_foot; //腳的數量
...... //其它成員變量
private:
void Cincect();
void ~Cincect();
public:
virtual Bite(){}//咬人,但我們只聲明這個成員函數,
//卻讓它什麼動作都不做,讓衍生類自己去定
//義各自的咬人方法
}
class Cant :public Cincect //螞蟻類
{
......
virtual Bite();
}
Cant::Bite()
{
...... //螞蟻具體的咬人方式
}
class Cmosquito :public Cincect //蚊子類
{
......
virtual Bite();
}
Cmosquito::Bite()
{
...... //蚊子具體的咬人方式
}
所以,虛擬的目的是隻在基類中將一般化動作聲明一個成員函數的原型而不做
具體定義,讓衍生類自己去定義。
這就是面向對象的特徵之三:多態性。基類的同一個成員在不同的衍生類中可以具
有不同的形態,更好地抽象和描述大千世界中的諸多“對象”。