在使用对象编程之前先要了解什么是对象。
对象是指对数据和操作的抽象封装。
优点:
高内聚, 使用方便
缺点:
相关的操作函数是有状态的(也就是函数编程里常说的副作用, 操作系统开发里的不可重入函数),
不同时间点执行该函数,有可能会返回不同的结果), 不方便并行处理
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等脚本语言做自动代码生成,来提高效率。