在C语言里进行面向对象设计--模拟运行时识别
时间:2010-11-19 来源:teiller2008
我们知道, C++ 里的虚函数实际上不是与对象绑定,而是与类绑定的。也就是说,一个类一个虚函数表,而不是一个对象一个虚函数表。所以,如果一个类的虚函数较多时,像前文那样定义接口:
struct IStream
{
void (*write)(IStream* pStream, char* pstr);
void(*read)(IStream* pStream, char buf, ssize_t size);
void (*flush)(IStream* pStream);
…
};
就会很浪费,因为每个对象都有 sizeof(struct IStream) 这么大。但是我们知道,虚函数表是与类绑定的,而不是与对象绑定的,那么我们可以改进这个设计。
struct IStreamClass
{
void (*write)(IStream* pStream, char* pstr);
void(*read)(IStream* pStream, char buf, ssize_t size);
void (*flush)(IStream* pStream);
…
};
基类的定义改为:
struct IStream{
{
struct IStreamClass* vtbl;
}
如果需要实现一个 FileStream 的话,可以定义一个具体类。
struct FileStream
{
struct IStream base;
FILE* f;
}
FileStreamClass 可以使用一个全局变量来定义:
extern struct IStreamClass class_FileStream;
因为这个虚函数表是与类绑定的,所以有了它,我们还可以增加运行时识别的功能!
int isFileStream(struct IStream* pStream){
return pStream->vtbl == class_FileStream;
}
这样设计还能够增加集成层次。比如 FileStream 本身又定义了一个虚函数 vFunc ,有一个类 AFileStream 继承自 FileStream ,则可以在 FileStream.h 中增加这样的定义:
***FileStream.h***
struct FileStreamClass{
struct IStream stream_class;
void (*vFunc)();
} ;
extern FileStreamClass class_FileStream;
***FileStream.c***
FileStreamClass class_FileStream = {
{
FileStream_write,
FileStream_read,
FileStream_flush
…
},
FileStream_vFunc
};
AFileStream 的定义为下面这样。
***AFileStream.h***
Struct AFileStream{
struct FileStreamClass* vtbl;
…
};
extern FileStreamClass class_AFileStream;
AFileStream.c 的内容就不用写了。
这样以来,你仍然可以用 FileStream->vtbl 当做 struct IStream 使用。
这个运行时识别的设计不支持动态创建的功能。 C++ 的动态创建功能, MFC 的设计就算是很好的了,但是那个需要构造函数的自动调用功能, C 里面没有这种功能。