在一些开源项目中,结构体中有成员变量,成员函数指针,还有结构体,归属上是子结构体,行为上却是父结构体,这就算是没有访问控制的继承和封装。关于多态,可以通过注册函数来覆盖父结构体的方法。构造和析构也可以通过初始化和去初始化函数来实现,注册和反注册也在此进行。
实例程序
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_Hchild.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思想对于扩展性还是很有帮助的