OOC 物件導向 C 語言程式設計實踐

scottcgi發表於2016-08-29
物件導向是一種程式設計思想,雖然C並沒有提供物件導向的語法糖,但仍然可以用物件導向的思維來抽象和使用。這裡分享一套C物件導向的寫法,可以完成物件導向程式設計並進行流暢的抽象。這套寫法是在實踐中不斷調整的結果,目前已經比較穩定,進行了大量的功能編寫。
這套OOC有以下特性:
  • 沒有強行去模仿c++的語法設定,而是使用C的特性去物件導向設計
  • 實現繼承,組合,封裝,多型的特性
  • 一套命名規範去增加程式碼的可讀性

第一,封裝

在C中可以用struct來封裝資料,如果是方法,我們就需要用函式指標存放到struct裡面來模擬。


  • 資料我們封裝在Drawable結構裡,通過Create可以再堆上建立需要自己free,Init是在棧上建立
  • 函式封裝在ADrawable這個全域性單例物件裡,由於沒有this指標,所有方法第一個引數需要傳入操作物件
  • Create和Init方法將會管理,物件的資料初始化工作。如果物件含有其它物件,就需要呼叫其Create或Init方法

第二,繼承和組合

  • 繼承,就是在結構體裡,嵌入另一個結構體。這樣malloc一次就得到全部的記憶體空間,釋放也就一次。嵌入的結構體就是父類,子類擁有父類全部的資料空間內容。
  • 組合,就是在結構體,存放另一個結構體的指標。這樣建立結構體時候,要需要呼叫父類的Create方法去生成父類空間,釋放的時候也需要額外釋放父類空間。
  • 這裡parent就是組合,color就是繼承。
  • 繼承是一種強耦合,無論如何子類擁有父類全部的資訊。
  • 組合是一種低耦合,如果不初始化,子類只是存放了一個空指標來佔位關聯。
  • 可以看到,C裡面一個結構體可以,繼承任意多個父類,也可以組合任意多個父類。
  • color[1] 使用陣列形式,可以直接把color當做指標使用
子類訪問父類,可以直接通過成員屬性。那麼如果通過父類訪問子類呢 ?  通過一個巨集定義來實現這個功能。

這樣,我們就可以,通過Sprite的父類Drawable屬性,來獲得子類Sprite的指標。其原理,是通過offsetof獲得成員偏移量,然後用成員地址偏移到子類地址上。有了這個機制,我們就可以實現多型,介面等抽象層。

我們可以在介面函式中,統一傳入父類物件,就可以拿到具體的子類指標,執行不同的邏輯,讓介面方法體現出多型特性。

第三, 多型


當,我們把一個函式指標放入,結構體物件的時候。意味著,在不同的物件裡,Draw函式可以替換為不同的實現。而不是像在ADrawable裡面的函式只有一個固定的實現。在子類繼承Drawable的時候,我們可以給Draw賦予具體的實現。而統一的呼叫Draw(Drawable* drawable)的時候,就會體現出多型特性,不同的子類有不懂的實現。

 在Hero和Enemy的Create函式中,我們分別實現Draw(Drawable* drawable)函式。如果,我們有一個繪製佇列,裡面都是Drawable物件。傳入Hero和Enemy的Drawable成員屬性。在統一繪製呼叫中,drawable->Draw(drawable),就會分別呼叫Hero和Enemy不同的draw函式實現,體現了多型性。

第四,重寫父類方法

 在繼承鏈中,我們常常需要重寫父類方法,有時候還需要呼叫父類方法。

比如,SpriteBatch 繼承 Sprite, 我們需要重寫Draw方法,還需要呼叫Sprite的Draw方法。那麼我們就需要把Sprite的Draw方法公佈出來。
這樣,每個Sprite的Draw方法可以,通過ASprite的Draw訪問。

那麼,SpriteBatch就重寫了父類的Draw方法,也能夠呼叫父類的方法了。

第五,記憶體管理

 一個malloc對應一個free,所以Create出來的物件需要自己手動free。關鍵是,在於組合的情況。就是物件內有別的物件的指標,有些是自己malloc的,有些是共用的。其實,計數器是一個簡單的方案,但我仍然覺得複雜了。在想到更好的方案之前,我傾向於更原始的手動方法,給有需要記憶體管理的物件新增Release方法。

Drawable 含有兩個指標, 一個是parent可能別的物件也會使用,所以這個parent在Release函式中不能確定釋放。還有一個children這個陣列本身是可以釋放的,所以在Create函式裡,我們自己malloc的,都要在Release方法裡自己free。

所以,對於Create方法我們需要free + Release。對於Init 只需要呼叫Release方法就可以釋放完全了。那麼,parent這種公用的指標,就需要paren物件自己在合適的時機去釋放自己。肯定沒有計數器來的方便,但是這個足夠簡單開銷也很小。

相關文章