Yukang's Page

C的面向对象风格代码

2011-08-16

OO是一种编程范型,而不只是特定语言的特定支持,所以用C来实现也是可行的。最近碰到的一部分代码都是用C实现的面向对象风格,可能是参考了Python里面的实现,Python内部实现的基本对象这块也全是这样的代码。在这里做一个小小的总结。

C语言里面没有语言层面的面向对象支持,那OO中的三个基本要素封装、继承、多态如何实现?C里面最强大的东西是指针,指针中最神奇的是void指针,这是一切的基本。首先来看封装,如何通过实例来调用方法,而对内部数据进行隐藏。完全可以写一些struct,然后写对应的函数来针对这个struct来操作,我们需要更进一步,把数据和方法绑定起来。这样写初看起来并没什么好处,后面会发现,通过函数指针去找对应的函数是多态的关键。

//object.h 
typedef struct _obj{
char name[MAXLEN];
int ref_cnt;
int value;
void (destructor) (void thiz);
void (print) (const void thiz);
int (equal) (const void thiz, const void* other);
}Obj;

//object.c destruct,print,equal定义为static

Obj Obj_new(int value)
{
Obj
o = malloc(sizeof(Obj));
strcpy(o->name,“baseobj”);
o->ref_cnt = 1;
o->value = value;
o->destructor = &destruct;
o->print = &print;
o->equal = &equal;
assert(o);
return o;
}

//使用方法
{
Obj first = Obj_new(1);
Obj
other = Obj_new(2);

first-&gt;<span style="color: #ffa500;">print</span>(first);
other-&gt;<span style="color: #ffa500;">print</span>(other);
<span style="color: #ffa500;">assert</span>(!first-&gt;<span style="color: #ffa500;">equal</span>(first,other));
<span style="color: #ffa500;">Obj_drop</span>(first);
<span style="color: #ffa500;">Obj_drop</span>(other);
<span style="color: #00ffff;">return</span> 0;

}
对于继承C当然也没原生的支持,可以在子类的定义中写入父类中的成员变量和成员函数,这里如果父类中定义的时候就是宏,直接拿过来就是。所以把父类的定义重新改写一下,分为DATA类型和TYPE类型,在Python里面就是这样,PyObject和PyVarObject是一切其他对象都包含有的。下面是一个例子People继承Object,Student继承People。

#define PEOPLE_DATA \
OBJ_DATA \
int age;\
char fullname[100];
//OBJ_DATA必须放在子类新的数据成员前面,只有这样才能把子类的指针强制转换成父类指针 或者转化为Object指针
#define PEOPLE_TYPE \
OBJ_TYPE \
void (sleep)(void thiz);

typedef struct _People_Type{
PEOPLE_TYPE
}People_Type;

extern const Object_Type Object_type;
extern const People_Type People_type;

typedef struct _People{
const People_Type* methods;
PEOPLE_DATA
}People;
这里sleep为新增加的子类方法,fullname为新增加的成员变量。注释部分为特别注意的,只有在保证内存的里面数据的分布前面部分都是一样的(一个methods指针和obj_data部分)才能进行指针之间的强制转换时候不出问题。例子里面的Student类也是类似的继承People类,这里可以看到sleep这个方法不好弄,因为在People那里申明为static了,这里想复用就麻烦,所以只有再自己写一个(即使实现是一样的),这也是C++内部帮用户做好的。可以看到通过type里面的函数指针的不同,不同对象相同的方法实现就不同了,因此实现了多态。

最后我们可以写一个基于计数的指针管理,在持有一个指针的时候调用Obj_pick,用完以后执行Obj_drop。

void Obj_pick(const void thiz)
{
assert(thiz);
Object
o = (Object*)thiz;
o->ref_cnt++;
}

void Obj_drop(const void thiz)
{
Object
o = (Object)thiz;
const Object_Type
p;
if( –o->ref_cnt <= 0){
for( p = o->methods; p; p=p->father){
if(p->destructor)
p->destructor(o);
}
}
free(o);
}
按照这种OO的风格的C代码感觉要清晰一些,至少我习惯了。不过还是看个人品位吧,这样的代码风格是我另外一个同事所鄙视的。关于用C实现OO风格,还有一本比较好的书叫做Object-oriented Programming with ANSI-C,感兴趣可以看看。

上面的代码在这里下载:https://github.com/chenyukang/ooc

使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章