c印记(二):lw_oopc简介

来源:互联网 发布:js混淆加密压缩 编辑:程序博客网 时间:2024/05/16 01:55

目录

    • 目录
    • 1 lw_oopc简介
    • 2 lw_oopc 宏定义介绍
      • 2_1 基础宏部分
        • 2_1_1 CLASS
        • 2_1_2 CTOREND_CTOR
        • 2_1_3 DTOREND_DTOR
        • 2_1_4 FUNCTION_SETTING
        • 2_1_5 基础宏应用实例
      • 2_2 ABS_CLASS扩展
        • 2_2_1 概述
        • 2_2_2 例子
      • 2_3 INTERFACE扩展
        • 2_3_1 概述
        • 2_3_2 例子
    • 3 总结

1、 lw_oopc简介

在上一篇中简单的提了一下面向对象以及c语言实现面向对象,写得一塌糊涂,这里就不管了,本篇主要介绍lw_oopc这个东东。

lw_oopc是由高焕堂及其MISOO团队创作的(在《UML+OOPC嵌入式C语言开发精讲》一书中有相关介绍),本文说的lw_oopc并非《UML+OOPC嵌入式C语言开发精讲》中原版的lw_oopc,而是source forge上下 载的lw_oopc(在原版的基础上有经过优化和功能增加,如增加了ABS_CLASS(虚基类),且是以LGPL协议开源),其作者是 金永华,在其readme文档中有对两者(原版和这个1.2版本)的对比:

原有高焕堂先生及其MISOO团队创作的宏(总共6个宏),清单如下:

高焕堂及MISOO创作的宏 是否存在问题? 是否修改? INTERFACE 没有问题 否 CLASS 无法支持继承 是 CTOR 对申请不到内存的情况未保护 是 END_CTOR 没有问题 否 FUNCTION_SETTING 没有问题 否 IMPLEMENTS 没有问题 否

为了更好的支持面向对象以及面向接口编程,我(金永华)增加了14个宏:

金永华创作的宏 创作目的(为了解决什么问题?) DTOR 为了支持析构函数的概念 END_DTOR ABS_CLASS 为了支持抽象类的概念 ABS_CTOR 为了支持可继承的抽象类的构造函数 END_ABS_CTOR EXTENDS 为了让熟悉Java的人容易理解(与IMPLEMENTS宏等同) SUPER_CTOR 为了支持子类调用父类的构造函数 SUPER_PTR 为了支持向上转型 SUPER_PTR_2 SUPER_PTR_3 SUB_PTR 为了支持向下转型 SUB_PTR_2 SUB_PTR_3 INHERIT_FROM 为了支持访问直接父类的数据成员

2、 lw_oopc 宏定义介绍

由于篇幅所限,并一定会每个宏都做介绍,因为这些宏都比较简单,所以并不会过多的介绍宏本身,更多的是以使用角度来讲。

2_1 基础宏部分

2_1_1 CLASS

首先咱就来说说这个最基本的关键字(CLASS),在lw_oopc中CLASS这个宏定义如下:

#define CLASS(type)        /*其中的type为所要定义的类名*/                      \typedef struct type type;        /*使用typedef定义一个名为‘type’的类名*/       \type* type##_new(lw_oopc_file_line_params); /*创建对象的函数,类似c++中的new*/ \void type##_ctor(type* t);       /*类的构造函数声明*/                         \int type##_dtor(type* t);        /*类的析构函数声明*/                         \void type##_delete(type* t);     /*释放对象的函数,类似c++中的delete*/         \struct type    /*名为‘type’的结构体定义*/

也以上一篇中Person类为例,定义如下:

CLASS(Person){    void (*Init)(struct Person* pPerson, int age, const char* name);    void (*sayHello)(struct Person* pPerson);    int mAge;    char* mName;};

与上一篇中使用c语言的结构体来模拟类相比,这里就少了一个Uninit方法,因为这个方法的行为将在Person的析构函数中进行。至于ABS_CLASS和INTERFACE宏与CLASS基本类似,只是没有type##_new以及type##_delete方法。

2_1_2 CTOR/END_CTOR

这组宏用于定义类的构造函数,具体宏定义如下:

#define CTOR(type)                                      \    type* type##_new() { /*实现创建对象的函数*/           \    struct type *cthis;                                 \    cthis = (struct type*)malloc(sizeof(struct type)); /*为对象(即struct)分配内存*/ \    if(!cthis)                                          \    {                                                   \        return 0;                                       \    }                                                   \    type##_ctor(cthis);/*调用类的构造函数*/               \    return cthis;                                       \}                                                       \                                                        \void type##_ctor(type* cthis) {/*定义类的构造函数*/#define END_CTOR    } /*类构造函数的结束大括号*/

CTOR宏中的malloc可以使用其他的接口函数代替,以便记录一些debug信息,在lw_oopc中在memory leak检测的时 候就会使用:
lw_oopc_malloc(sizeof(struct type), #type, file, line);
代替malloc,他会记录类的名字,源码的文件名,以及在文件中的行号。

2_1_3 DTOR/END_DTOR

这组宏是在金永华创作的lw_oopc当中新增的,定义类的析构函数,与CTOR相对应:

#define DTOR(type)                                        \void type##_delete(type* cthis) /*实现释放对象的函数*/      \{                                                         \    if(type##_dtor(cthis)) /*调用类的析构函数*/             \    {                                                     \            lw_oopc_free(cthis);/*释放对象的内存*/          \    }                                                     \}                                                         \int type##_dtor(type* cthis) /*定义类的析构函数*/           \{#define END_DTOR } /*类析构函数结束大括号*/

2_1_4 FUNCTION_SETTING

这个宏用于注册成员函数,它的定义也比较简单,基本上就是一个赋值语句(当然,如果有需要的话也可以丰富这个宏的内容,让其功能更强大):

#define FUNCTION_SETTING(f1, f2) cthis->f1=f2;/*将成员函数赋值给struct中的函数指针*/

2_1_5 基础宏应用实例

以上几个定义和注册函数的使用方式如下:

/* 声明Person类 */CLASS(Person){    void (*Init)(struct Person* pPerson, int age, const char* name);    void (*sayHello)(struct Person* pPerson);    int mAge;    char* mName;};/*定义成员函数,为了封装性,建议在.c档案中定义static类型的函数*/    static void init(struct Person* pPerson, int age, const char* name){     pPerson->mAge = age;     pPerson->mName = name;/*为了简单表示,这里不去关心字符串内存问题*/}static void sayHello(struct Person* pPerson){   printf("hello, my name is:%s and i'm %d years old\n", pPerson->mName, pPerson->mAge);}/*定义构造函数*/CTOR(Person)cthis->mAge = 0;cthis->mName = NULL; /*初始化成员函数*/FUNCTION_SETTING(Init, init);FUNCTION_SETTING(sayHello, sayHello); /*注册成员函数*/END_CTOR/*定义析构函数*/DTOR(Person)//todo release member variablereturn lw_oopc_true; //如果需要释放对象就分会true,反之返回false(这样可以实现引用计数之类的应用)END_DTOR/*具体使用*/int main(int argc, char* argv[]){    Person* pPerson = Person_new(); /*创建Person的对象实例*/    pPerson->Init(pPerson, 12, "LiLei");/* 初始化对象 */    pPerson->sayHello(pPerson);    Person_delete(pPerson);    return 0;}

2_2 ABS_CLASS扩展

2_2_1 概述

ABS_CLASS宏对应于c++中的虚基类(因为只是c语言的宏,所以无法从语法规则上去约束其只是虚基类,也就是说具体使用的时候也可能将其作为普通的基类(即所有成员函数都有实现))。这个宏的作用就是可以让c语言实现继承以及多态。当然这里也有不少缺陷和约束,比如因为不是语法规则上的约束,所以无法在编译阶段就知道是不是所有成员函数都有对应的实现。

其宏定义和CLASS差不多,只是没有type##_new以及type##_delete方,所以这里就不在将其列出来了,下面就以一个例子来说明其使用方式。

2_2_2 例子

这里以lw_oopc的demo中关于动物的例子(会有适当的修改,详情请到lw_oopc的源码包中去查看),动物是一个基类,而狗,鱼等都属于动物,是动物的子类(is-a关系):

#include "lw_oopc.h"#include <stdio.h>ABS_CLASS(Animal)/*声明动物 基类*/{    char name[128];     // 动物的昵称(假设小于128个字符)    int age;            // 动物的年龄    void (*setName)(Animal* t, const char* name);   // 设置动物的昵称    void (*setAge)(Animal* t, int age);             // 设置动物的年龄     void (*sayHello)(Animal* t);                    // 动物打招呼    void (*init)(Animal* t, const char* name, int age); // 初始化昵称和年龄};CLASS(Fish) /*声明 鱼 类*/{    EXTENDS(Animal);        // 继承Animal抽象类    void (*init)(Fish* t, const char* name, int age);};CLASS(Dog) /*声明 狗 类*/{    EXTENDS(Animal);        // 继承Animal抽象类    void(*init)(Dog* t, const char* name, int age);};///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////* 设置动物的昵称 */void Animal_setName(Animal* t, const char* name){    // 这里假定name小于128个字符,为简化示例代码,不做保护(产品代码中不要这样写)    strcpy(t->name, name);}/* 设置动物的年龄 */void Animal_setAge(Animal* t, int age){    t->age = age;}/* 动物和我们打招呼 */void Animal_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//////////////////////////////////////////////////////////////////* 初始化鱼的昵称和年龄 */void Fish_init(Fish* t, const char* name, int age){    Animal* animal = SUPER_PTR(t, Animal);    animal->setName(animal, name);    animal->setAge(animal, age);}CTOR(Fish)/*定义 鱼 类的构造函数,并注册成员函数*/SUPER_CTOR(Animal);FUNCTION_SETTING(init, Fish_init);END_CTOR/////////////////////////////////////////////////////////////////* 初始化狗的昵称和年龄 */void Dog_init(Dog* t, const char* name, int age){    Animal* animal = SUPER_PTR(t, Animal);    animal->setName(animal, name);    animal->setAge(animal, age);}CTOR(Dog)/*定义 狗 类的构造函数,并注册成员函数*/SUPER_CTOR(Animal);FUNCTION_SETTING(init, Dog_init);END_CTOR//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////int main(int argc, char* argv[]){        Fish* fish = Fish_new();    // 创建鱼对象    Dog* dog = Dog_new();       // 创建狗对象    Animal* animal = NULL;    // 初始化鱼对象的昵称为:小鲤鱼,年龄为:1岁    fish->init(fish, "小鲤鱼", 1);              // 初始化狗对象的昵称为:牧羊犬,年龄为:2岁    dog->init(dog, "牧羊犬", 2);     // 将fish指针转型为Animal类型指针    animal = SUPER_PTR(fish, Animal);           animal->sayHello();    // 将dog指针转型为Animal类型指针    animal = SUPER_PTR(dog, Animal);    animal->sayHello();     lw_oopc_delete(fish);    lw_oopc_delete(dog);    return 0;}

从上面的例子可以看出,c模拟面向对象毕竟不是原生支持,必然会有很多缺陷和约束,比如这个狗类中继承动物类的sayHello函数,就无法使用dog对象去调用,必须将其转换为animal对象方能调用。

这里的继承其实可以用在c的模块开发当中,ABS_CLASS定义模块的接口,CLASS再定义各种子模块,比如设计一个stream模块(各种流操作),就可以使用ABS_CLASS定义出stream模块的对外接口,以及一些stream模块内部各个子模块可共用的接口,然后在stream模块就可以使用CLASS去定义一些具体的子模块,如本地文件CLASS(FileStream),网络文件CLASS(HttpStream)等等。

2_3 INTERFACE扩展

2_3_1 概述

这个概念应该源自java中的interface,以interface 关键字定义一套接口方法(这里只有方法的声明,没有实现 ),然后再在具体的类中去实现这些接口方法。

那么问题就来了,既然有了ABS_CLASS,那还要INTERFACE干嘛呢? 就以lw_oopc的demo中的例子来说,动物,除了有呼吸,吃东西等行为之外,还有一个共同的特征就是“移动”。如 果以ABS_CLASS的方式实现的话,就会在Animal类中定义一个move方法。

那是不是只有动物才会“移动”呢? 显然不是,最常见的比如汽车,也是会移动的物体,当它却不是动物,如果以 ABS_CLASS方式实现,那么就需要在Animal和Car类中都声明move方法,为何要如此呢? 这是因为动物和汽车是不 同的类别。

当然这里还有另一种实现方式——INTERFACE,move是一种抽象的行为,谁有这种行为,就去实现这个INTERFACE,汽车能够移动,那么汽车类就去实现汽车的移动方式,动物能够移动,那动物类就去实现动物的移动方式。

2_3_2 例子

这里还是以lw_oopc中的例子来说明 INTERFACE的具体应用:

#include "lw_oopc.h"#include <stdio.h>INTERFACE(IMoveable){    void (*move)(IMoveable* t);     // Move行为};/*这里为了简单,就不用Animal基类了*/CLASS(Fish){    IMPLEMENTS(IMoveable);  // 实现IMoveable接口    void (*sayHello)(Fish* t);                    // 动物打招呼    void (*init)(Fish* t, const char* name, int age);    char name[128];     // 动物的昵称(假设小于128个字符)    int age;            // 动物的年龄};///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////* 鱼的移动行为 */void Fish_move(IMoveable* t){    printf("鱼在水里游!\n");}/* 初始化鱼的昵称和年龄 */void Fish_init(Fish* t, const char* name, int age){     // 这里假定name小于128个字符,为简化示例代码,不做保护(产品代码中不要这样写)    strcpy(t->name, name);    t->age = age;}/* 动物和我们打招呼 */void Fish_sayHello(Fish* t){    printf("Hello! 我是%s,今年%d岁了!\n", t->name, t->age);}CTOR(Fish)/*定义 鱼 类的构造函数,并注册成员函数*/FUNCTION_SETTING(IMoveable.move, Fish_move);FUNCTION_SETTING(init, Fish_init);FUNCTION_SETTING(sayHello, Fish_sayHello);END_CTOR//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////int main(int argc, char* argv[]){        Fish* fish = Fish_new();    // 创建鱼对象    IMoveable* moveObj = NULL;    // 初始化鱼对象的昵称为:小鲤鱼,年龄为:1岁    fish->init(fish, "小鲤鱼", 1);              //鱼打招呼    fish->sayHello();    // 将fish指针转型为IMoveable接口类型指针    moveObj = SUPER_PTR(fish, IMoveable);    //鱼移动    moveobj->move();    lw_oopc_delete(fish);    return 0;}

3、 总结

从以上简介可以看出,虽然其可以模拟面向对象编程,但显然其中有不少缺陷和约束。而且这些宏的功能虽然基本的都有了,用于简单的学习是完全足够,但如果要用于实际的开发项目,可能就还需要更多的功能扩展和优化(这个下一章会有相关说明),现在我们来总结一下lw_oopc宏的一些优缺点(都是寡人的门户之见,并不一定正确,如有不同看法,欢迎拍砖):
- 优点
1. 比较简洁,基本上一看就懂,学习曲线比较平缓;
2. 能在一定程度上实现面向对象编程;
3. 有利于功能模块以及子模块的划分和隔离。
- 缺点:
1. 功能还不完善,需要进一步增强和优化才能更好的用于实际项目;
2. 由于是使用的宏定义,会使用预处理机制,所以在这些宏定义中就无法在编译时进行类型检测;
3. 由于是模拟的面向对象编程,所以没有原生支持的编程语言使用起来方便;
4. 无法在编译时就检测成员函数是否已经实现,如未实现,就会在运行时产生segmentation fault错误。
5. 阅读不方便,如source insight这样的工具,无法判断以CLASS等宏定义的结构体,在阅读以 及代码跳转查看的时候不太方便

0 0