1. 程式人生 > >類的概念及其使用

類的概念及其使用

總結以下有關類的概念,用於加深對類的理解。可能有不足或錯誤之處,歡迎指出,大家共同進步。

1、引言

介紹類,繞不過去兩個概念:面向過程程式設計、面向物件程式設計(Object Oriented Programming)。其中面向過程的程式設計以C語言為代表的程式設計方式,面向過程程式的設計方式是:確定程式需要哪些過程,然後採用最合適的演算法來實現;強調的是程式實現的過程以及實現過程中採用的演算法。這程式開發的前期,採用這種方式是最直接也是最簡單的,因為當時面對的程式規模還比較小,採用面向物件的設計方式能夠滿足基本的需要。隨著程式的規模越來越大,採用面向物件的方式已不能很好滿足程式設計的需要,此時,提出了OOP的概念。OOP與強調過程的程式設計方式不同,它強調的是物件

,把程式中的組成分解成一個個物件,然後通過物件之間的操作或互動來滿足程式的需要。

與面向過程相比,OOP具備以下幾個重要的特性:

1)、抽象

2)、封裝和隱藏

3)、多型

4)、繼承

5)、程式碼重用性

為了實現這些特性並把它們組合在一起,提出了類的概念。

2、類的概念

何為類?類的英文表達為class,直譯過來的意思就是“型別”,即,一個類是自定義的一種型別

這就不得不提及OOP中“抽象”的特性。世界是無序和複雜的,簡單來說,一個人的組成就非常複雜,到目前為止,在醫學上還在不斷地探索人體的奧祕。而在OOP中,如果要表達一個人,我們不可能把所有與人體相關的組成或行為都表現出來,那樣會非常複雜和繁瑣,所以我們就需要化繁為簡,找出人具備的明顯的特徵組合起來來表達“人”,這種化繁為簡的過程就可以稱之為抽象

。通過抽象的分析,一個“人”可能包括:具有腦袋、五官、四肢、膚色等組成屬性,具備直立行走、使用工具等行為。經過抽象,可以把“人”分析為一組具有多種屬性、多種行為的描述,而在OOP中,類就是用來組合抽象出的屬性和行為,從而形成一個用來描述“人”的一種型別。這種組合的過程其實可以理解為對類的封裝的隱藏,通過封裝後的類可以有效地用來表達一個“人”這種實體。

3、物件的概念

那麼何為物件呢?我們現在已經知道類是一種型別,而物件就是該型別例項化出來的一個實體,該實體可以成為類的物件。比如“人”是一個類,那麼我們每一個人個體都可以稱之為一個個物件,一個型別為“人”的物件,我們都具備該類描述的屬性和行為。物件是類的一種例項化,在OOP中,當定義好類之後,操作的主要就是物件。

4、類的繼承

繼承是類的一個很重要的概念,是實現OOP特性中程式碼重用性的一種手段。正如其字面意思,通過繼承,不僅可以使用原始類中一些屬性和行為,同時也可以對繼承的類進行擴充套件,形成新的屬性和行為。

繼承,又可以稱之為派生,當從一個類派生出另外一個類時,原始類稱之為基類(超類),繼承類則稱之為派生類。整合的方式有三種:Public、Protected、Private,分別對應類中的三種類型的屬性。繼承的形式如下所示:

class CChildClass : public CBaseClass
{...}

其中CChildClass屬於派生類,CBaseClass屬於基類,繼承方式為公有繼承,即是是公有繼承也不能直接呼叫基類的私有成員,只能通過基類的公有方法進行呼叫。關於繼承有以下需要注意的幾點:

1)、建構函式和解構函式。需要注意的是,當建立派生類物件是,程式首先建立基類的物件。這也就是說,首先呼叫的是基類的建構函式,然後再呼叫派生類的建構函式。而解構函式的呼叫順序剛好相反,是先呼叫派生類的解構函式然後再呼叫基類的解構函式;

2)、建構函式的初始化列表。當採用繼承的方式時,仍然可以通過初始化列表的形式初始化類的屬性,具體使用方式如下所示:

CChildClass: :CChildClass(int p1, char p2, string p3): CBaseClass(p2, p3)
{attr1 = p1; } 

當定義一個CChildClass類的物件時如:CChildClass *cc = new CChildClass(1, 'a', "Hello");就會按照如上方式進行屬性的初始化,先將p2和p3傳遞給基類的建構函式進行初始化操作,然後在執行派生類的建構函式進行屬性的初始化。

當然,還有另外一種形式的初始化列表,即所有的屬性都通過初始化列表的形式進行初始化,如下所示:

CChildClass: :CChildClass(int p1, char p2, string p3): CBaseClass(p2, p3),attr1(p1)
{...}

3)、思考一種情況,派生類同時也繼承了基類的行為(即函式),那麼如果,派生類的行為與基類不一樣,或者說每一個該基類的派生類的行為都不一樣,那該怎麼辦呢?

比如:現在有一個“車”的類,類名為CCar,它有一個名為Drive的行為,即:

class CCar

{...

public:

void Drive();

}

現有兩個子類,分別為CManualCar和CAutoCar,分別代表手動擋駕駛,和自動檔駕駛的車,那麼他們的繼承方式就為如下所示:

class CManualCar : public CCar
{...

}

class CAutoCar : public CCar

{...

}

CManualCar和CAutoCar分別繼承了基類的Drive行為,但是他們的Drive行為並不相同,那麼該如何解決呢?

當然可以採用在每一個派生類中再定義一個行為分別代表手動駕駛和自動駕駛,但是這樣做繼承的意義就不大,而且不符合程式碼重用的特性。這是就可以引用類的另外一個重要的概念了——多型。

5、類的多型

多型是類的一種很重要的特性。從字面意思上就可以理解,類的多型性,及類可以有多種形態。那麼實現多型的手段是什麼呢?虛擬函式。通過virtual來宣告虛擬函式,如下所示:

class CCar

{...

public:

virtual void Drive() const;     //const 關鍵字的作用是宣告該函式為“只讀”函式,不會修改類中資料成員的值

}

此時派生的兩個子類就可以採用如下形式:

class CManualCar : public CCar

{...

public:

virtual void Drive() const;

}

class CAutoCar : public CCar

{...

public:

virtual void Drive() const;

}

呼叫方式如下所示:

CCar car;      CManualCar manualCar;     CAutoCar autoCar;

car.Drive();    //呼叫的是CCar .Drive()

manualCar.Drive();    //呼叫的是CManualCar .Drive()

autoCar.Drive();      //呼叫的是CAutoCar .Drive()

關於虛擬函式的使用有以下需要注意的地方:

1)、虛解構函式。解構函式的呼叫事發生當delete釋放由new建立的物件時,對於派生類來說,當delete時,如果基類中的解構函式沒有虛化,則將只會呼叫基類中的解構函式。而如果基類的解構函式虛化了,則將會先呼叫派生類的解構函式,然後再呼叫基類的解構函式。虛化基類的解構函式就是為了保證析構的順序是正確的。

2)、純虛擬函式。純虛擬函式是更加高階一點的虛擬函式。虛擬函式在基類中也是要有實現的,而純虛擬函式在基類中是不需要實現的,因為純虛擬函式實際上是提供了一種藉口,所有的派生類都可以從介面中繼承對應的函式,對應有很多共性的物件而言,採用純虛擬函式還是比較合適的。純虛擬函式的宣告方式如下所示:

class CCar

{...

public:

virtual void Drive() const=0;

}

class CManualCar : public CCar

{...

public:

virtual void Drive() const;   //在派生類中則不需要加 =0

}

需要注意的是,擁有純虛擬函式的類成為抽象類,是不能建立該類的物件的,只能用作基類來使用。