纯C语言实现简单封装继承机制
来源:互联网 发布:mysql 索引 编辑:程序博客网 时间:2024/06/09 22:47
0 继承是OO设计的基础
继承是OO设计中的基本部分,也是实现多态的基础,C++,C#,Objective-C,Java,PHP,JavaScript等为OO而设计的语言,其语言本身对实现继承提供了直接支持。而遵循C/Unix设计哲学的语言,从不限定编程风格,而且提供了实现OO的基本支持。下面我们就来看看如何用C语言实现继承。
1 内存布局层面上继承的含义
如今几乎所有程序员都知道继承的抽象含义,对于被用烂了的猫狗继承动物的例子也耳熟能详。在此,我们抛开抽象世界,深入到继承的具体实现上。当然不同的语言对继承的实现机制并不完全相同,但是了解其中一种典型的实现细节对于理解继承是非常有好处的。这里我们以C++为例进行说明。
class B{ int x; int y; int z;};class C : B{ float f; char s[10];};
上述代码表示子类C继承了父类B,下面是类C的一个实例(对象)的内存布局。
C对象有两部分组成,红色区域是继承自B的部分,蓝色区域是自身特有的。这样一来,红色部分完全可以当成是一个B类对象。
2 利用结构体实现继承的两种方法
2.1 父类对象作为子类的成员
理解了继承的内存布局原理之后,用C来实现继承就非常容易了。最容易想到的方法如下:
struct B{ int x; int y; int z;};struct C{ struct B objB; float f; char s[10];};
上述代码通过在C中包含一个B类型的成员来实现继承,此方法非常直接,但使用起来有一些不太方便。
struct C objC; objC.objB.x = 10; ((struct B*)&objC)->x = 10;
要想访问父类的成员x,有两种方法,一种是objC.objB.x;另一种是((struct B*)&objC)->x = 10。这两种方式都看起来不够直接。而在子类方法中访问父类成员是非常频繁的。
void c_member_method(struct C* pObjC){ pObjC->objB.x = 20; /* 访问父类成员 */ pObjC->f = 0.23f; /* 访问自身成员 */}
第一种方法,感觉更像是OB风格,而不是OO。
第二种方法,必须进行强制类型转换,感觉语法上不够美观。
2.2 子类包含所有的父类成员
struct C{ int x; int y; int z; float f; char s[10];};
把所有的父类成员原样作为子类的成员。这样子类对象访问继承来的成员就非常直接了。
void c_member_method(struct C* pObjC){ pObjC->x = 20; /* 访问父类成员 */ pObjC->f = 0.23f; /* 访问自身成员 */}void main(){ struct C objC; objC.x = 10;}
看起来很好,实际上在工程上会存在一个很大的问题:难以维护!例如,每当创建一个子类,必须原样书写所有的父类成员,当父类定义变动时,子类需要做出同样的修改。一旦父类稍具规模,维护这种继承关系将是一场噩梦!
那么如何解决的?
方法是现成的,那就是利用C语言的预处理宏定义#define. 如下所示:
#define B_STRUCT \ int x; \ int y; \ int zstruct B{ B_STRUCT;};struct C{ B_STRUCT; float f; char s[10];};
当继承层级更深时,例如 C继承B,D继承C,可以照搬此方法。
#define B_STRUCT \ int x; \ int y; \ int zstruct B{ B_STRUCT;};#define C_STRUCT \ B_STRUCT; \ float f; \ char s[10]struct C{ C_STRUCT;};#define D_STRUCT \ C_STRUCT; \ double dstruct D{ D_STRUCT;};
通过宏定义,可以很容易实现和维护这种继承关系。
3 方法(成员函数)的封装与继承
C语言中没有成员函数的概念,语言本身也不支持。使用C语言实现真正的成员函数几乎是不可能的,除非嵌入汇编语言。与其使用汇编语言,还不如直接使用C++呢。所以,我们不追求形式上的成员函数,只实现意义上的成员函数–(对给定类型对象进行操作)的函数,并使用带结构名前缀的函数名加以命名之。我们还是以上面的例子进行说明。
typedef struct B B;static void b_member_function(B* pobjB) /* 类B的成员函数 */{}typedef struct C C;static void c_member_function(C* pobjC) /* 类C的成员函数 */{}
对成员函数的调用,有两种情形:(1)外部代码调用成员函数;(2)子类成员函数中调用父类的成员函数;
static void c_member_function(C* pobjC){ b_member_function((B*)pobjC); /* 子类成员函数内部调用父类成员函数 */}void main(){ C* pObjC = malloc(sizeof(C)); b_member_function((B*)pObjC); /* 外部代码调用成员函数 */ free(pObjC);}
这两种情况都需要对实参进行强制类型转换为父类型。C编译器对类型继承关系一无所知,无法从语法上对继承进行自动支持,所以只能手动强制类型转换了。
有些人喜欢更进一步模拟成员函数,把所有成员函数的地址作为指针类型的成员变量存储到结构体内部。如下:
#define B_STRUCT \ int x; \ int y; \ int z; \ void (*pb_member_function1)(B*); \ void (*pb_member_function2)(B*, int arg)struct B{ B_STRUCT; };/* 初始化B对象的同时初始化 */B* b = malloc(sizeof(B));b->pb_member_function1 = b_member_function1;b->pb_member_function2 = b_member_function2;/* 调用 */b->b_member_function1(b);
这样形式上更加接近“成员函数”,但同时也带来了额外的内存开销和代码量。为了减小内存消耗,有人提出不再在对象中完全存放所有成员函数指针,而是只存放一个指向成员函数地址列表的指针。毕竟,同一类型的所有实例(对象)共享相同的一组成员函数。
/* B类型的成员方法表 */const struct B_MethodTable{ void (*pb_member_function1)(B*); void (*pb_member_function2)(B*, int arg);}b_method_table{ b_member_function1, b_member_function2,};#define B_STRUCT \ int x; \ int y; \ int z; \ struct B_MethodTable * pMethodTable;struct B{ B_STRUCT; };/* 初始化B对象的同时初始化 */B* b = malloc(sizeof(B));b->pMethodTable = &b_method_table;/* 调用 */b->pMethodTable->pb_member_function1(b);
这样在一定程度上减小了内存占用量和代码量,但是队成员函数的调用写法却变得非常繁琐不自然。
4 做到何种程度?
使用C语言做OO开发时,要掌握好一个度。不要过分追求对OO语言C++的模拟,完全模拟C++的话,还不如干脆直接使用C++。
- 有些语言特性无法模拟,如C++中private,protected等访问限定符,成员函数的this指针。更应该注重意义上的模拟,通过一些命名规则和约定来达到OO。
- 永远不要违背C语言的设计哲学:程序员控制一切,直接简明。
这个度的把握需要根据具体的项目规模和需求,是实践中摸索出来的,无法给出理论上的最优值。
- 纯C语言实现简单封装继承机制
- C语言实现封装,继承
- C语言实现简单的日志封装
- C语言实现封装、继承和多态
- C语言实现封装、继承和多态
- C语言实现封装、继承和多态
- C语言实现封装、继承和多态
- C语言实现封装、继承和多态
- 面向对象之继承,封装,多态c语言实现
- c语言实现封装,继承和多态
- c语言实现封装,继承和多态
- c语言实现封装,继承和多态
- C语言实现封装、继承和多态
- C语言实现封装、继承和多态
- c语言实现封装,继承和多态
- C语言实现封装、继承和多态
- C语言实现C++的封装继承和多态
- C语言实现封装、继承和多态
- 获取当前软件路径
- mac系统设置显示和隐藏文件
- GCD的简单使用——图片的下载及展示
- Cisco Anyconnect VPN连接后网络受限的一种解决办法
- SQL笔记
- 纯C语言实现简单封装继承机制
- POJ 3159 Candies(差分约束+SPFA )
- 看到一篇文章留着慢慢消化
- A1219. 采矿(陈许旻) 树链剖分+线段树+DP
- 【转帖】对通用输入输出GPIO的深入理解http://www.9mcu.com/9mcubbs/forum.php?mod=viewthread&tid=954521(出处: 大连创客空间)
- C#委托&事件
- shiro入门实战笔记(9)--会话管理
- react-native组件封装与传值
- 对话DeepMind创始人:建立通用人工智能