Polymorphism in C(用C实现多态)

Polymorphism in C(用C实现多态)
2007年02月13日 星期二 01:17

       最近一直喜欢浏览国外网站,感觉收获很多,所以我跟苞谷说,看中文网站是无奇不有,看外文网站才知道世界之大.Polymorphism in C是我摘抄+修改+体会+翻译过来的好东东.有助于理解多态.

      多态是什么?就是我们调用一个公共接口(相同函数名字)时,根据调用者的类型调用不同的函数,看过我之前写的C++向C的转化的人其实知道,函数名字相同只是我们肉眼看见的相同,编译器优化后,意味函数所属的class不一样,函数名字也要变化,显然函数的入口地址都是不一样的.C++中的RTTI和dynamic binding就是做这个事情.当然C也可以实现,C是C++的鼻祖嘛.

      这个图中的Structs就是相当于一个class,所有有个class-like,然后每个structs里面有一个公共方法method,这个公共方法就在Common Interface里面,调用者调用这个method不用管它能否调到它想要的那个method,在C++中,可以让你的类型同你这个method绑定在一起,所以很方便,调用者直接call这个method就能call到你想用的那个方法.
      其实多态就是函数指针实现的,因为C也有指针,C也可以做到.
/*file:Poly.h*/
#ifndef _POLY_H                                                                                       
#define _POLY_H                                                                                      
        struct POLY_STRUCT                                                                       
        {                                                                                                         
        void (*poly)( );    // 多态函数,common interface                            
        };
        /* 初始化一个类(相当于),然后把这个公共函数的具体实现跟类型绑一起 */
        struct POLY_STRUCT *InitPoly( void (*poly)() );
        
        /* 释放一个类(相当于析构函数)*/
        void DestroyPoly( struct POLY_STRUCT *poly );
#endif
/*file:Poly.c*/
struct POLY_STRUCT *InitPoly( void (*poly)() )
{
 struct POLY_STRUCT *result = (struct POLY_STRUCT*)
                                  malloc( sizeof( struct POLY_STRUCT ) );
 if(result != NULL)
  result->poly = poly;
 return result;
}

void DestroyPoly( struct POLY_STRUCT *poly ){ if(poly != NULL) free(poly); }
/*file:main.c* /
/*下面是两个用父类指针表示的两个子类对象*/
struct POLY_STRUCT* example_poly1;
struct POLY_STRUCT* example_poly2;
/*定义两个函数,代表这两个子类中common interface的不同实现*/
void poly_example1_method(){printf("EXAMPLE1/n");}
void poly_example2_method(){printf("EXAMPLE2/n");}
int main(int argc, char *argv[])
{
    example_poly1 = InitPoly(poly_example1_method);
    example_poly2 = InitPoly(poly_example2_method);    
    
    /*父类指针调用相同的函数,但是结果呢?不一样*/
    (*(example_poly1->poly))();
    (*(example_poly2->poly))();

    DestroyPoly(example_poly1);
    DestroyPoly(example_poly2);
  system("PAUSE"); 
  return 0;
}
结果:
EXAMPLE1
EXAMPLE2
      结论就是,函数指针导致了多态,它付出的 代价就是程序机制的复杂性和不必要的代码开销,它的 优点就是,将俗点就是呈现一致的函数名给我们(实际上编译时这个名字还是要变化),因为一致性,所以重用性和扩展性也更好了.
       我喜欢那句话:优化性和可扩展性需要我们去平衡.这里也是.

用C实现C++的多态

四. 如何构造一个对象
    我们用下面的结构来描述一个对象:
    typedef struct _ClassType
    {
        char* name; /*对象名,唯一*/
        int   object_size;/*对象大小,创建实列时分配内存要用到*/
        int   class_size; /*全部虚函数大小,如sizeof(CObjectClass)*/
        void* vclass; /*虚函数指针*/
        void (*InitClassCallback)(void*); /*给对象的虚函数赋值*/
        void (*InitObjectCallback)(void*);/*对象实例化时,给实例成员赋初值,有点类似构造函数*/
        struct _ClassType*  parent; /*父对象*/
        struct _ClassType*  next; /*单向链表,指向下一对象*/
    }ClassType;
   
    #define [MAX_CLASS_NUM 128
    static ClassType classes[MAX_CLASS_NUM]; /*对象池*/
    static ClassType *used_classes = NULL;
    static ClassType *free_classes = NULL;
    这里只定义了128个对象,数目可能修改或者改成动态分配。

    初始化,全部链接到空闲链表中。
    void InitObjectLib(void)
    {
        int i;
        memset(classes, 0, sizeof(ClassType)*MAX_CLASS_NUM);
        for( i=0;i<MAX_CLASS_NUM-1;i++ )
        {
            classes[i].next = &(classes[i+1]);
        } 
        free_classes = &(classes[0]);
    }

    这个函数既是获取对象的虚函数指针,也是注册对象。
    这里要注意type为static,一个对象只注册一次,下次获取时直接取type。
    RegisterClassType()第一个参数为对象的描述信息,第二个参数为父对象的
    函数指针,我们来看一下它的定义:
    #define TOP_OBJECT_TYPE 0
    #define BASE_OBJECT_TYPE GetObjectType()
    因为CObject为基对象,它没有父对象,所以这里第二个参数为0(TOP_OBJECT_TYPE)。
    现在假设有一个CObject子对象CSubObject,那么注册它时应该这样写:
    type = RegisterClassType(&classinfo, BASE_OBJECT_TYPE);
    由此可能看到,这里其实是一个递归:当注册CSubOject里会用到参数BASE_OBJECT_TYPE,
    既GetObjectType(),这样就可以把CObject也注册上。
    简单的说:如果C继承于B,B继承于A,现在假设A、B还未注册,当注册C时,通过递归
    也能把B、A注册上。

    int GetObjectType(void)
    {
        static int type = 0;

        if( type==0 )
        {
            static ClassInfo classinfo =
            {
                "OBJECT", /*对象名,唯一*/
                sizeof(CObject),
                sizeof(CObjectClass),
                (pInitObjectCallback)InitObject,
                (pInitClassCallback)InitObjectClass,
            };

            type = RegisterClassType(&classinfo, TOP_OBJECT_TYPE);
 }

        return type;
    }

    我们再来看一看InitObject和InitObjectClass,
    static void InitObjectClass(CObjectClass *vclass)
    {
        if(vclass==NULL)
            return;
        vclass->destory = DestoryObject;/*对虚函数赋值*/
    }

    static void InitObject(CObject *object)
    {
        if(object==NULL)
            return; 
        object->ref_counter = 1;
    }

    这两个函数在GetObjectType()被传进去,那么又是在哪儿被调用的呢?
    让我们看RegisterClassType()
    int RegisterClassType(ClassInfo *classinfo, int parent_type)
    {
        int type = 0;
        ClassDesc *current, *parent;

        parent = (ClassType *)parent_type;

        if( classinfo->name==NULL )
        {
            TRACE("RegisterClassType(): class name is NULL/n");
            return type;
        }
        /*检查是否有对象名重复*/
        current = used_classes;
        while(current)
        {
            if(strcmp(current->name, classinfo->name)==0 )
            {
                TRACE("RegisterClassType(): class name is redefined/n");
                unlock();
                return type;
            }
            current = current->next;
        }

        /*创建对象*/
        current = free_classes;
        if(current)
        {
            free_classes = free_classes->next;
            current->name = classinfo->name;
            current->parent = parent;
            current->class_size = classinfo->class_size;
            current->InitClassCallback = classinfo->InitClassCallback;
            current->InitObjectCallback = classinfo->InitObjectCallback;
            current->vclass = malloc(current->class_size);
            if( current->vclass==NULL )
            {
                TRACE("RegisterClassType(): malloc vclass failed/n");
            }
            memset( current->vclass, 0, current->class_size);
            current->vclass->class_type = (int)current;
            if( parent )/*子对象的虚函数全部指向父对象的虚函数*/
            {
                memcpy(current->vclass, parent->vclass, parent->class_size);
            }
            对子对象新添加的虚函数赋值,也可以对继承于父对象的虚函数重赋值。
            if( current->InitClassCallback )
                current->InitClassCallback(current->vclass);
            链接到对象池中
            current->next = used_classes;
            used_classes = current;
        }
        else
        {
            TRACE("RegisterClassType(): ERROR, no class in class pool/n");
        }
 
        type = (int)current; 注意:type为ClassType类型的指针。
        return type;
    }

   
五. 创建对象的实例

 CObject *NewObject(void) /*创建一个实例*/
 {
  return NewClassType(BASE_OBJECT_TYPE);
 }

 void *NewClassType(int class_type)
 {
  ClassType *pclass = NULL;
  void *object = NULL;

  if( pclass==0 )
   return NULL;
 
  pclass = (ClassType*)class_type;
  /*为实例分配内存空间*/
  object = (CObject*)malloc(pclass->object_size);
  if( object )
  {
   /*实例的虚函数指针指向它的对象的虚函数指针*/
   ((CObject*)object)->vclass = pclass->vclass;
   /*调用父对象的初始化函数,这个函数是递归函数,
   先初始化基对象,再初始化子对象,再初始化子子对象...*/
   InitParentObject(pclass, object);
  }

  return object;
 }

 static void InitParentObject(ClassType *current, void *object)
 {
  if( current )
  {
   if( current->parent )
    InitParentObject(current->parent, object);
   if( current->InitObjectCallback )
    current->InitObjectCallback(object);
  }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值