面向對象編程(OOP),最早是C++、java等面向對象語言的一個核心特點,之後發展成了一種編程思想。
面向對象編程的主要特點是,把屬性(數據)與方法(函數)按照類型綁定,並按照類型之間的關係分層分模塊設計,通過基類指針和虛函數實現多態。
C++的OOP機制是最完善的,也是最複雜的。
java則在這個基礎上做了裁剪,並且屏蔽了對指針的直接處理,語法更簡化。
OOP隨著時間發展成了一種編程思想,也就不再受限於某幾種程式語言了。
C風格的面向對象編程,是隨著Linux系統的流行而逐漸流行起來的一種設計模式。最早,在Linux驅動工程師裡被廣泛使用。
C風格的面向對象編程,特點是:
1,用函數指針表示方法,
2,用結構體表示類,
3,用結構體的成員變量表示屬性,
4,用結構體之間的包含關係,表示類的繼承關係,
5,用open、create、alloc和close、release、free這類名字的函數來表示構造函數和析構函數。
一般是把所有方法的函數指針放在一個結構體裡,形成一個方法集來模擬虛函數表,並且給它起個字符串名字來表示該虛函數表關聯的子類。
把不同子類的公共屬性作為結構體的成員變量,結構體本身就是基類。
子類的特有成員變量則放在基類的void* data指針裡,具體內容由子類定義,並在子類的「構造函數」裡初始化。
代碼大概是這樣:
typedef struct socket_s socket_t;
typedef struct sockops_s sockops_t;
struct socket_s
{
int fd; //文件句柄
void* data; //子類屬性數據
sockops _t* ops; //子類虛函數表
};
struct sockops_s
{
char* name; // 子類的名字
int (*open)(socket_t* this); //構造
int (*close)(socket_t* this);//析構
int (*send)(socket_t* this);//send方法
int (*recv)(socket_t* this);//recv方法
};
C沒有new、delete關鍵字,需要給出兩個全局函數來代替。
int sock_open(socket_t** pthis, char* porto);
int sock_close(socket_t* this);
sock_open裡的二級指針用於返回創建的子類對象,字符串變量proto用來表示子類的網絡協議,例如tcp、udp、http、rtmp等等,其實就是子類的類型名字。
形參this,模擬的就是C++的this指針。
受限於C的語法,this指針必須明確寫出來,而不是隱含著。也可以不叫this,C允許給形參起別的名字,例如context。
基類指針p調用子類對象的send方法時,就是:
p->ops->send();
通過「虛函數表」顯示調用。
p->ops在sock_open()時設置,而且之後不可以再修改。
C語言沒有機制阻止程式設計師修改p->ops的指向,只能作為約定。
在非初始化的代碼裡修改函數指針,是很不好的編程習慣。
因為一旦某個函數指針的值依賴代碼的執行路徑,而不只是初始化設置,那麼出了BUG之後就不容易確定函數指針指向的真正函數,查起來也就比較麻煩。
C沒有異常(exception),創建是否成功要看open()函數的返回值。
當然,沒有異常也就不存在構造函數拋出異常該怎麼辦這個問題。
可以在sock_open()返回之後直接用if判斷返回值就可以了。