在使用對象編程之前先要了解什麼是對象。
對象是指對數據和操作的抽象封裝。
優點:
高內聚, 使用方便
缺點:
相關的操作函數是有狀態的(也就是函數編程裏常說的副作用, 操作系統開發裏的不可重入函數),
不同時間點執行該函數,有可能會返回不同的結果), 不方便並行處理
C語言要實現一個對象,關鍵是解決數據和操作的封裝。
1> 數據的封裝主要是通過結構體來實現, 操作的封裝則是通過函數指針來實現。
2> 數據隱藏, 主要是通過萬能的void* 指針來實現。
對象的通用操作主要有以下3個
1> 創建, 對應new函數
2> 銷燬, 對應delete函數
3> 複製, 對應clone函數
C語言並不支持 字符串轉變量功能, RTTI需要自己加type屬性,並實現相應的工廠類。
以FFMPEG的URLProtocol類爲例:
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
/**
* Read data from the protocol.
* If data is immediately available (even less than size), EOF is
* reached or an error occurs (including EINTR), return immediately.
* Otherwise:
* In non-blocking mode, return AVERROR(EAGAIN) immediately.
* In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
* and return AVERROR(EAGAIN) on timeout.
* Checking interrupt_callback, looping on EINTR and EAGAIN and until
* enough data has been read is left to the calling function; see
* retry_transfer_wrapper in avio.c.
*/
int (*url_read)( URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
...
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
} URLProtocol;
上面的url_open等便對應一系列操作接口。
接下來看如何構建對象實例
URLProtocol ff_tcp_protocol = {
.name = "tcp",
.url_open = tcp_open,
.url_read = tcp_read,
.url_write = tcp_write,
.url_close = tcp_close,
.url_get_file_handle = tcp_get_file_handle,
.url_shutdown = tcp_shutdown,
.priv_data_size = sizeof(TCPContext),
.priv_data_class = &tcp_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
};
這裏的Tcp便是抽象URLProtocol的一個具體實例。
操作函數的實現如下:
int tcp_open(URLContext *h, const char *uri, int flags)
{
}
需要注意的是,由於URLContext 裏面封裝了URLProtocol指針,我們並沒有加URLProtocol* this指針。
與C++、Java等語法層面支持類語言不同,我們需要稍微做一些編譯器做的事情,自己手動將操作與數據綁定。
如果覺得自己加this指針很麻煩, 可以用C語言提供的宏,或Perl、Python等腳本語言做自動代碼生成,來提高效率。