IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    C的面向对象风格代码

    Yukang (moorekang@gmail.com)发表于 2011-08-16 00:00:00
    love 0
    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->print(first);
        other->print(other);
        assert(!first->equal(first,other));
        Obj_drop(first);
        Obj_drop(other);
        return 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。


沪ICP备19023445号-2号
友情链接