轉自“http://blog.csdn.net/love_gaohz/article/details/8082681#
C語言的對象化模型
物件導向的特徵主要包括:
.封裝,隱藏內部實現
.繼承,複用現有代碼
.多態,改寫對象行為
採用C語言實現的關鍵是如何運用C語言本身的特性來實現上述物件導向的特徵。
1.1 封裝
封裝是一種資訊隱蔽技術,它體現於類的說明,是對象的重要特性。封裝使資料和加工該資料的
方法(函數)封裝為一個整體,以實現獨立性很強的模組,使得使用者只能見到對象的外特性(對象
能接受哪些訊息,具有那些處理能力),而對象的內特性(儲存內部狀態的私人資料和實現加工能
力的演算法)對使用者是隱形。封裝的目的在於把對象的設計者和對象者的使用分開,使用者不必
知曉行為實現的細節,只須用設計者提供的訊息來訪問該對象。
在C語言中,大多數函數的命名方式是動詞+名詞的形式,例如要擷取一個semaphore,會命名
成take_semaphore,重點在take這個動作上。物件導向編程中剛好相反,命名為rt_sem_take,即名詞+動詞的形式,重點在名詞上,體現了一個對象的方法。另外對於某些方法,僅局限在對象內部使用,它們將採用static修辭把作用範圍局限在一個檔案的內部。通過這樣的方式,把一些不想讓使用者知道的資訊屏蔽在封裝裡,使用者只看到了外層的介面,從而形成了物件導向中的最基本的對象封裝實現。
一般屬於某個類的對象會有一個統一的建立,析構過程。
.對象記憶體資料區塊已經存在,需要對它進行初始化 – rt_sem_init;
.對象記憶體資料區塊還未分配,需要建立並初始化 – rt_sem_create。
可以這麼認為,對象的建立(create)是以對象的初始化(init)為基礎的,建立動作相比較而言多了個
記憶體配置的動作。
相對應的兩類析構方式:
.由rt_sem_init初始化的semaphore對象 – rt_sem_detach;
.由rt_sem_create建立的semaphore對象 – rt_sem_delete.
1.2 繼承
繼承性是子類自動共用父類之間資料和方法的機制。它由類的派生功能體現。一個類直接繼承其
它類的全部描述,同時可修改和擴充。繼承具有傳遞性。繼承分為單繼承(一個子類只有一父類)
和多重繼承(一個類有多個父類,當前RT-Thread的對象系統不能支援)。類的對象是各自封閉的,
如果沒繼承性機制,則類對象中資料、方法就會出現大量重複。繼承不僅支援系統的可重用性,而
且還促進系統的可擴充性。
類似的實現代碼如下程式清單:
/* 父類 */
struct parent_class
{
int a, b;
char *str;
};
/* 繼承於父類的子類 */
struct child_class
{
struct parent class p;
int a, b;
};
/* 操作樣本函數*/
void func()
{
struct child_class obj, *obj_ptr; /* 子類對象及指標 */
struct parent_class *parent_ptr; /* 父類指標 */
obj_ptr = &obj;
/* 取父指標 */
parent_ptr = (struct parent*) &obj;
/* 可通過轉換過類型的父類指標訪問相應的屬性 */
parent ptr->a = 1;
parent ptr->b = 5;
/* 子類屬性的操作 */
obj ptr->a = 10;
obj ptr->b = 100;
}
在上面代碼中,注意child_class結構中第一個成員p,這種聲明方式代表child_class類型的資料中
開始的位置包含一個parent_class類型的變數。在函數func中obj是一個child_class對象,正像這個
結構類型指示的,它前面的資料應該包含一個parent_class類型的資料。在第21行的強制類型賦值
中parent_ptr指向了obj變數的首地址,也就是obj變數中的p對象。好了,現在parent_ptr指向的是
一個真真實實的parent類型的結構,那麼可以按照parent的方式訪問其中的成員,當然也包括可以
使用和parent結構相關的函數來處理內部資料,因為一個正常的,正確的代碼,它是不會越界訪
問parent結構體以外的資料。
經過這基本的結構體層層相套包含,對象簡單的繼存關係就體現出來了:父物件放於資料區塊的最
前方,代碼中可以通過強制類型轉換獲得父物件指標。
1.3 多態
對象根據所接收的訊息而做出動作。同一訊息為不同的對象接受時可產生完全不同的行動,這種
現象稱為多態性。利用多態性使用者可發送一個通用的資訊,而將所有的實現細節都留給接受訊息
的對象自行決定,如是,同一訊息即可調用不同的方法。例如:抽象裝置具備介面統一的讀寫介面。
串口是裝置的一種,也應支援裝置的讀寫。但串口的讀寫操作是串口所特有的,不應和其他裝置
操作完全相同,例如操作串口的操作不應應用於SD卡裝置中。
多態性的實現受到繼承性的支援,利用類繼承的層次關係,把具有通用功能的協議存放在類層次
中儘可能高的地方,而將實現這一功能的不同方法置於較低層次,這樣,在這些低層次上產生的
對象就能給通用訊息以不同的響應。
物件模型採用結構封裝中使用指標的形式達到物件導向中多態的效果,例如:
/* 抽象父類 */
struct parent_class
{
int a;
/* 反映不同類別屬性的方法 */
void (*vfunc)(int a);
}
/* 抽象類別的方法調用 */
void parent_class_vfunc(struct parent_class *self, int a)
{
assert(self != NULL);
assert(slef->vfunc != NULL);
/* 調用對象本身的虛擬函數 */
self->vfunc(a);
}
/* 繼承自parent class的子類 */
struct child_class
{
struct parent_class parent;
int b;
};
/* 子類的建構函式 */
void child_class_init(struct child_class* self)
{
struct parent_class* parent;
/* 強制類型轉換獲得父類指標 */
parent = (struct base_class*) self;
assert(parent != NULL);
/* 設定子類的虛擬函數 */
parent->vfunc = child_class_vfunc;
}
/* 子類的虛擬函數實現 */
static void child class vfunc(struct child class*self, int a)
{
self->b = a + 10;
}