现在的位置: 首页 > 综合 > 正文

如何用C实现一个类以及些许设计模式

2018年03月31日 ⁄ 综合 ⁄ 共 2631字 ⁄ 字号 评论关闭

在一些开源项目中,结构体中有成员变量,成员函数指针,还有结构体,归属上是子结构体,行为上却是父结构体,这就算是没有访问控制的继承和封装。关于多态,可以通过注册函数来覆盖父结构体的方法。构造和析构也可以通过初始化和去初始化函数来实现,注册和反注册也在此进行。

实例程序

parent.h

#ifndef PARENT_H

#include <stdio.h>

struct parent
{
    int a;          //成员变量
    int (*test)(void);          //成员函数
    void (*fly)(void);
    void (*display)(const struct parent par_d);
    /*......*/
};

void par_init(struct parent *);   //初始化函数

static int par_num;

#endif // PARENT_H

parent.c

#ifndef PARENT_C

#include "parent.h"

static int test(void)
{
    return 0;
}

static void fly(void)
{
    printf("I can fly\n");
}

static void display(const struct parent par_d)
{
    printf("the par->a is %d\nthe par->test is 0x%x\nthe par->fly is 0x%x\n", par_d.a, par_d.test, par_d.fly);
}

void par_init(struct parent *par)   //初始化函数
{
    par->a = 1;
    par->display = display;     //不必去初始化,方便测试
    par->test = test;
    par->fly = fly;
}

void par_deinit(struct parent *par)   //去初始化函数
{
    par->a = 0;
    par->test = NULL;
    par->fly = NULL;
}

#endif // PARENT_C

child.h

#ifndef CHILD_H

#include "parent.h"

struct child
{
    struct parent parentA;
    void (*display)(const struct child cli_d);
};

void A_init(struct child *);

void A_deinit(struct child *);

#endif // CHILD_H

child.c

#ifndef CHILD_C

#include "child.h"

static int CoverMethod[32] = {0};

enum MethodName
{
    TEST = 0,
    /*......*/
};

#define PRINTNULL(x){ \
            if(x) printf("the %s is not NULL\n", #x);      \
            else if(!x) printf("the %s is NULL\n", #x);    \
            }

static int test(void)
{
    return 1;
}

static void display(const struct child cli_d)   //模仿操作法"<<"重载
{
    printf("the child is 0x%x\n", &cli_d);
    const struct parent par_d = cli_d.parentA;
    printf("the parent is 0x%x\n", &par_d);
    cli_d.parentA.display(par_d);
}

void A_init(struct child *cli)      //初始化,并注册函数指针
{
    par_num++;
    par_init(&(cli->parentA));
    cli->display = display;       //操作符重载模拟不必去初始化,方便测试
    if(NULL != (void *)test)
    {
        CoverMethod[TEST] = (int)cli->parentA.test;
        cli->parentA.test = test;       //覆盖父结构体的方法
    }
    /*......其他需要覆盖的方法*/
}

void A_deinit(struct child *cli)        //去初始化,反注册
{
    if(NULL != (void *)test)
    {
        cli->parentA.test = CoverMethod[TEST];       //还原父结构体的方法
    }
    /*......其他需要还原的方法*/

    par_num--;
    if(!par_num) par_deinit(&(cli->parentA));
}

void main()
{
    struct child childA;
    A_init(&childA);

    const struct child cli_d = childA;
    childA.display(cli_d);

    printf("the a is %d, the result is %d\n", childA.parentA.a, childA.parentA.test());
    childA.parentA.fly();

    par_num = 2;        //测试用
    A_deinit(&childA);
    printf("the a is %d, the result is %d\n", childA.parentA.a, childA.parentA.test());
    childA.parentA.fly();

    PRINTNULL(childA.parentA.test);
    PRINTNULL(childA.parentA.fly);

    par_num = 1;        //测试用
    A_deinit(&childA);

    childA.display(cli_d);

    PRINTNULL(childA.parentA.test);
    PRINTNULL(childA.parentA.fly);
}

#endif // CHILD_C

以上代码的健壮性很差,只是抛砖引玉,其实函数指针的使用还是比较危险的,需要良好的控制方法

访问控制不太好实现,对于面向过程的C,一般得靠人力进行访问控制的检查;当需要增加统一的功能时,可以直接在父结构体就行修改。注意是在子结构体中包含父结构体

有了类的基础,就可以实现一些设计模式

比如策略模式,可以用两个只有函数指针的父结构体,分别代表独立的不同方法,子结构体初始化的时候可以指向其中一个。

比如单件模式,可以通过精心设计的静态变量和静态方法来实现

当然,对于大部分能使用OO语言的场合,还是使用OO语言。但是在某些特殊的场合,比如Linux内核以及嵌入式编程中,适当引入OO思想对于扩展性还是很有帮助的



抱歉!评论已关闭.