C語言面向對象編程(一):封裝與繼承

最近在用 C 作項目,以前用慣了 C++ ,轉回頭來用C 還真有點不適應。 C++ 語言中自帶面向對象支持,如封裝、繼承、多態等面向對象的基本特徵。 C 本來是面向過程的語言,自身沒有內建這些特性,但咱們仍是能夠利用 C 語言自己已有的特性來實現面向對象的一些基本特徵。接下來咱們就一一來細說封裝、繼承、多態、純虛類等面向對象特性在 C 語言中如何實現,而且給出實例。程序員

    這篇文章中咱們先說封裝和繼承。框架

    先來看封裝。函數

    所謂封裝,通俗地說,就是一個姑娘化了妝,只給你看她想讓你看的那一面,至於裏面是否颳了骨、墊了東西,不給你看。說到封裝就得說隱藏,這是對兄弟概念;其實我理解隱藏是更深的封裝,徹底不給你看見,而封裝多是猶抱琵琶半遮面。封裝在 C++ 語言中有 protected 、 private 關鍵字在語言層面上支持,而 C 語言中沒有這些。 C 有結構體( struct ),其實能夠實現封裝和隱藏。spa

    在 QT 中,爲了更好的隱藏一個類的具體實現,通常是一個公開頭文件、一個私有頭文件,私有頭文件中定義實現的內部細節,公開頭文件中定義開放給客戶程序員的接口和公共數據。看看 QObject (qobject.h ),對應有一個 QObjectPrivate (qobject_p.h ) ,其餘的也相似。而代碼框架以下:.net

[cpp] view plain copy指針

  1. QObject{  orm

  2. public:  對象

  3.     xxx  blog

  4.     xxx  繼承

  5. private:  

  6.     QObjectPrivate * priv;  

  7. };  

    咱們在 C 語言中徹底能夠用一樣的方法來實現封裝和隱藏,只不過是放在結構體中而已。代碼框架以下:

[cpp] view plain copy

  1. struct st_abc_private;  

  2. struct st_abc {  

  3.     int a;  

  4.     xxx;  

  5.     void (*xyz_func)(struct st_abc*);  

  6.   

  7.     struct st_abc_private * priv;  

  8. };  

    上面的代碼,咱們只前向聲明結構體 struct st_abc_private ,沒人知道它裏面具體是什麼東西。假如 struct st_abc 對應的頭文件是 abc.h ,那麼把 st_abc_private 的聲明放在 abc_p.h 中,abc.c 文件包含 abc_p.h ,那麼在實現 struct st_abc 的函數指針 xyz_func 時如何使用 struct st_abc_private ,客戶程序員根本無須知道。

    這樣作的好處是顯而易見的,除了預約義好的接口,客戶程序員徹底不須要知道實現細節,即使實現通過重構徹底重來,客戶程序員也不須要關注,甚至相應的模塊連從新編譯都不要——由於 abc.h 自始至終都沒變過。

    上面代碼有個問題,客戶程序員如何獲得 struct st_abc 的一個實例,他不知道 struct st_abc_private 如何實現的呀。 C 中沒有構造函數,只好咱們本身提供了:咱們能夠在 abc.h 中聲明一個相似構造函數的函數來生成 struct st_abc 的實例,名字就叫做 new_abc() ,函數原型以下:

[cpp] view plain copy

  1. struct st_abc * new_abc();  

    至於實現,咱們放在 abc.c 中,客戶程序員不須要知道。相應的,還有個相似析構函數的函數,原型以下:

[cpp] view plain copy

  1. void delete_abc(struct st_abc *);  


    到如今爲止,封裝和隱藏就實現了,並且很完全。接下來看繼承。

    什麼是繼承?在面向對象層面上不講了,只說語法層面。語法層面上講,繼承就是派生類擁有父類的數據、方法,又添了點本身的東西,所謂子承父業,發揚光大。在 C 語言中能夠用結構體的包含來實現繼承關係。代碼框架以下:

[cpp] view plain copy

  1. struct st_base{  

  2.     xxx;  

  3. };  

  4.   

  5. struct st_derived{  

  6.     struct sb_base base;  

  7.     yyy;  

  8. };  

    代碼上就是這麼簡單,不過有一點要注意:第一點就是派生類(結構體)中必定要把父類類型的成員放在第一個。

    繼承在語法層面上看,有數據成員、函數,數據成員經過上面的方法自動就「繼承」了,至於函數,在結構體表示爲函數指針,其實也是一個數據成員,是個指針而已,也會自動「繼承」。之因此還要在這裏列出來講明,是由於 C++ 中有一個很重要的概念:重載。要在 C 中完整實現有點兒麻煩。

    重載,咱們常說的重載大概有三種含義:

  • 其一,函數重載,指函數名字同樣,參數個數、類型不同的函數聲明和實現。因爲 C 編譯器的緣故,不支持。不過這個影響不大。

  • 其二,重定義或者說覆蓋,指派生類中定義與基類簽名同樣(名字、返回值、參數徹底同樣)的非虛函數,這樣派生類的中的函數會覆蓋基類的同簽名函數,經過成員操做符訪問時沒法訪問基類的同簽名函數。

  • 其三,虛函數重寫,指在派生類中實現基類定義的虛函數或純虛函數。虛函數是實現多態的關鍵,能夠在結構體中使用函數指針來表達,但要徹底實現,也很麻煩。

    咱們日常在交流時一般不明確區分上面三種類型的重載,這裏出於習慣,也不做區分。
    好了,第一篇就到這裏,有時間會往下續。

網友評論:

其二,重定義或者說覆蓋,指派生類中定義與基類簽名同樣(名字、返回值、參數徹底同樣)的非虛函數,這樣派生類的中的函數會覆蓋基類的同簽名函數,經過成員操做符訪問時沒法訪問基類的同簽名函數。
我以爲,覆蓋不須要簽名同樣,只要函數名相同就能實現覆蓋。

相關文章
相關標籤/搜索