轻量级的面向对象C语言编程框架(程序员2010年4月技术文章)

   LW_OOPC是Light-Weight Object-Oriendted Programming in(with) C的缩写, 总共一个h文件,包含20个宏,约130行代码,非常的轻量级,却很好地支持了很多面向对象的特性,比如继承、多态,可以优美地实现面向接口编程。这个框架系由台湾的高焕堂先生以及他的MISOO团队首创,之后由我继续改进优化。最后,经高焕堂同意以LGPL协议开源。

 

  为什么要用面向对象

  面向过程方式开发的系统,代码复杂、耦合性强、难以维护,随着我们所要解决的问题越来越复杂,代码页变得越来越复杂,越来越难以掌控,而面向对象改编了程序员的思维方式,以更加符合客观世界的方式来认识世界,通过合理的运用抽象、封装、继承和多态,更好的阻止程序,从而很好地应对这种复杂性。

 

  为什么要在C语言中实现面向对象

  C语言以其简洁明快、功能强大的特点,深得开发人员的喜爱,尤其是在嵌入式开发领域,C语言更是占据了老大的地位。在我看来,语言只是工具,作为程序员,我们要做的是:选择合适的语言,解决恰当的问题。我们要尊重事实,考虑开发环境(软硬件环境),考虑团队成员的水平,从商用工程的角度讲,选择团队成员擅长的语言进行开发,风险要小很多。

  一些从JAVA/C#转到C的程序员,无法从面向对象切换到面向过程,但又必须与C语言同事们在遗留的C系统上开发软件,所以他们有时会非常困惑:C语言室面向过程的变成语言,如何实践面向对象,甚至面向接口编程呢?此时,就非常需要在C语言中实现面向对象的手段,而LW_OOPC正是应对这一难题的解决之道。

 

 LW_OOPC宏介绍

 简而言之,LW_OOPC是一套C语言的宏,都定义在1个.h文件中(lw_oopc.h,约130行代码)。如果需要内存泄露检测支持以及调试打印支持,那么还需要1个.c文件(lw_oopc.c,约145行代码)。LW_OOPC是一种C语言编程框架,用于支持在C语言中进行面向对象编程。

  下面,先通过一个简单的示例来展示LW_oopc这套宏的使用方法。我们要创建这样一些对象:动物(Animal),鱼(Fish),狗(Dog),车子(Car)。显然,鱼和狗都属于动物,都会动。车子也会动,但是车子不是动物。会动是这些对象的共同特征,但是,显然他们不属于一个家族。因此,我们首先考虑抽象出一个接口(IMoveable),以描述会动这一行为特征。

  INTERFACE(IMoveable)

  {

    void (*move)(IMoveable* t);

   };

  INTERFACE宏用于定义接口,其成员(方法)均是函数指针类型。

  然后,我们分析Animal,它应该是抽象类还是接口呢?动物都会吃,都需要呼吸,如果仅仅考虑这两个特征,显然可以把Animal定义接口。这里,为了展示抽象类在LW_OOPC中如何应用。我们让Animal拥有昵称和年龄属性,并且让动物和我们打招呼(sayHello方法),但是我们不允许用户直接创建Animal对象,这里,我们把Animal定位抽象类。

  ABS_CLASS(Animal)

  {

     char name[128];//动物的昵称(假设小于128个字符)

     int age;//动物的年龄

     void (*setName)(Animal* t,const char* name);//设置动物的昵称

     void (*setAge)(Animal* t,const char* name);//设置动物的年龄

     void (*setHello)(Animal* t);//动物打招呼

     void (*eat)(Animal* t);//动物都会吃(抽象方法,由子类实现)

     void (*breathe)(Animal* t)//动物都会呼吸(抽象方法,由子类实现)

     void (*init)(Animal* t,const char* name,int age);

  }

  ABS_CLASS宏用于定义抽象类、紧接着,我们来定义Fish类(Dog类的定义与Fish类雷同),它继承动物,然后还实现了IMoveable接口:

  CLASS(Fish)

  {

    EXTENDS(Animal);//继承Animal抽象类

    IMPLEMENTS(IMoveable);//实现IMoveable接口

    void (*init)(Fish* t,const char*name,int age);//初始化昵称和年龄

  };

   下面,我们来定义Car,车子不是动物,但可以Move,因此,让Car实现IMoveable接口即可:

   CLASS(Car)

   {

      IMPLEMENTS(IMoveable);//实现IMoveable接口(车子不是动物,但可以Move)

   };

   接口、抽象类、具体类的定义都已经完成了。下面,我们开发实现它们。接口是不需要实现的,所以IMoveable没有对应的实现代码。Animal是抽象动物接口,是半成品,所以需要提供半成品的实现:

   /*设置动物的昵称*/

   void Animal_setName(Animal* t,const char* name)

   {

      strcpy(t—>name,name);

   }

   /*设置动物的昵称*/

   void Animal_setAge(Animal* t,const char* name)

   {

      t—>age=age;

   }

   /*动物和我们打招呼*/

   void Aniaml_sayHello(Animal* t)

   {

      printf("Hello!我是%s,今年%d岁了!/n",t—>name,t—>age);

   }

   /*初始化动物的昵称和年龄*/

   void Animal_init(Animal* t,Const char* name,int age)

   {

      t—>setName(t,name);

      t—>setAge(t,age);

   }

   ABS_CTOR(Animal)

   FUNCTION_SETTING(setName,Animal_setName);

   FUNCTION_SETTING(setAge,Animal_setAge);

   FUNCTION_SETTING(sayHello,Animal_sayHello);

   FUNCTION_SETTING(init,Animal_init);

   END_ABS_CTOR 

  

   ABS_CTOR表示抽象类的构造函数定义开始。LW_OOPC中的CTOR系列宏(CTOR/END_CTOR,ABS_CTOR/END_ABS_CTOR)除了给对象分配内存,紧接着要为结构体中的函数指针成员赋值,这一过程可以成为函数绑定,由FUNCTION_SETTING宏来完成。

   下面看Fish类的实现(Dog类的实现与Fish类的实现雷同,篇幅所限,代码略省):

   /*鱼的吃行为*/

   void Fish_eat(Animal* t)

   {

       printf("鱼吃水草!/n");

   }

   /*鱼的呼吸行为*/

   void Fish_breathe(Animal* t)

   {

      printf("鱼用腮呼吸!/n");

   }

   /*鱼的移动行为*/

   void Fish_move(IMoveable* t)

   {

      printf("鱼在水里游!/n")

   }

    /*初始化鱼的昵称和年龄*/

    void Finsh_init(Fish* t,const char* name,int age)

   {

       Animal* animal = SUPER_PTR(t,Animal);

       animal—>setAge(animal,age);

   }

   CTOR(Fish)

   SUPER_CTOR(Animal);

   FUNCTION_SETTING(Animal.eat,Fish_eat);

   FUNCTION_SETTING(Animal.breathe,Fish_breathe);

   FUNCTION_SETTING(IMoveable.move,Fish_move);

   FUNCTION_SETTING(init,Fish_init);

   END_CTOR

  

   上面的代码片段中,SUPER_PTR表示“向上转型”,将对象指针向上转型成直接父类或者直接接口类型的指针,SUPER_CTOR宏表示调用其直接父类的构造函数(类似Java语言中的super调用,在这里,其实质是要先调用父类的函数绑定过程,再调用自身的函数绑定过程)。

   Car类的实现:

   void Car_move(IMoveable* t)

   {

       printf("汽车在开动!/n");

   }

   CTOR(Car)

   FUNCTION_SETTING(IMoveable.move,Car_move);

   END_CTOR

   下面,我们来实现main方法:

   #include "animal.h"

   int main()

   {

      //创建鱼对象

     Fish* fish=Fish_new();

     //创建狗对象

    Dog* dog = Dog_new();

    //创建车子对象

    Car* car= Car_new();

    Animal* animals[2] = {0};//初始化动物容器

    IMoveable* moveObjs[3] = {0};//初始化可移动物体容器

    int i=0;

    int j=0;

    //对象初始化

    fish—>init(fish,"小鲤鱼",1);

    dog—>init(dog,"牧羊犬",2);

    //将fish和dog放入父类型的容器

    animals[0]=SUPER_PTR(fish,Animal);

    animals[1]=SUPER_PTR(dog,Animal);

   //将fish和dog放入接口类类型的容器

   moveObjs[0]=SUPER_PTR(fish,IMoveable);

   moveObjs[1]=SUPER_PTR(dog,IMoveable);

   moveObjs[2]=SUPER_PTR(car,IMoveable);

   //打印所有动物的信息

   for(i=0li<2;i++)

   {

      Animal* animal = animals[i];

      animal—>eat(animal);

      animal—>breathe(animal);

      animal—>sayHello(animal);

   }

   }

   //打印所有可移动物体的信息

   for(j=0;j<3;j++)

   {

      IMoveable* moveObj = moveObjs[j];

      moveObj—>move(moveObj);

   }

   lw_oopc_delete(fish);

   lw_oopc_delete(dog);

   lw_oopc_delete(car);

   return 0;

}

   从上边的代码中,我们惊喜地发现,在C语言中,借助LW_OOPC,我们实现了将不同的动物(Fish和Dog对象)装入Animal容器,然后可以用完全相同的方式调用Animal的方法(比如eat和breathe方法),而实际调用的是具体的实现类(Fish和Dog)的对应方法。这正是面向对象中的多态的概念。同样,我们可以将Fish、Dog、Car三个对象均视为可移动物体,均装入IMoveable容器,然后用完全相同的方式调用IMoveable接口的move方法。看到了吗?借助LW_OOPC,在C语言下可以轻松地实现面向对象和面向接口编程!

  

你可能感兴趣的:(技术杂论,语言,编程,框架,c,2010,function)