C语言下的封装 继承 与多态.docx
《C语言下的封装 继承 与多态.docx》由会员分享,可在线阅读,更多相关《C语言下的封装 继承 与多态.docx(11页珍藏版)》请在冰豆网上搜索。
C语言下的封装继承与多态
C语言下的封装、继承与多态
上次课,钱SIR提到,Liux下面也有很多用C实现的面向对象的结构。
比较感觉兴趣,就在网上查了一些资料,原来C语言模拟实现面向对象语言所具有的特性:
多态,继承,封装,也是一件很简单的事儿。
并且现在很多开源软件都了用C语言实现了这几个特性,包括大型开源数据库系统postgreSQL,可移植的C语言面向对象框架GObject。
在自己机器上实践了下,感叹C语言的灵活与强大!
总结一下,以便交流:
一、基础知识
(1)结构体
结构体可以嵌套,因而可以把一个结构体当成另一个结构体的成员,如:
[cpp]viewplaincopyprint?
1.structPoint{
2.intx;
3.inty;
4.};
[cpp]viewplaincopyprint?
1.structCircle{
2.structPointpoint_;
3.intradius;
4.};
该结构体与以下定义完全一样(包括内存布置都一样
[cpp]viewplaincopyprint?
1.structCircle{
2.intx;
3.inty;
4.intradius;
5.};
(2)void*
指针是整个C语言的精髓所在。
而你也一直敬畏着指针,又爱又恨地使用着它。
许多教材都告诉你,int*叫做指向整型的指针,而char*是指向字符型的指针,等等等等不一而足。
然而这里有一个另类的指针家族成员——void*。
不要按照通常的命名方式叫它做指向void类型的指针,它的正式的名字叫做:
可以指向任意类型的指针。
(3)C中的参数个数可变函数
可变参数函数的原型声明:
[cpp]viewplaincopyprint?
1.typeVAFunction(typearg1,typearg2,…);
参数可以分为两部分:
个数确定的固定参数和个数可变的可选参数。
函数至少需要一个固定参数,固定参数的声明和普通函数一样;可选参数由于个数不确定,声明时用"..."表示。
固定参数和可选参数公同构成一个函数的参数列表。
标准C/C++包含头文件stdarg.h,该头文件中定义了操作不定变量的相关宏:
[cpp]viewplaincopyprint?
1.voidva_start(va_listarg_ptr,prev_param);/*ANSIversion*/
2.typeva_arg(va_listarg_ptr,type);
3.voidva_end(va_listarg_ptr);
在这些宏中,va就是variableargument(可变参数)的意思;
arg_ptr是指向可变参数表的指针;
prev_param指可变参数表的前一个固定参数;
type为可变参数的类型。
va_list也是一个宏,其定义为typedefchar*va_list,实质上是一char型指针。
具体用法可以参考:
二、封装
封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。
在C语言中的实现方法:
把私有数据信息放在一个不透明的priv变量或者结构体中,只有类的实现代码才知道priv或者结构体的真正定义。
例如:
头文件:
[cpp]viewplaincopyprint?
1.//========头文件:
Point.h文件========
2.#ifndefPOINT_H
3.#definePOINT_H
4.typedefstructPointpoint;
5.typedefstructpointPrivatepointPrivate;
6.structPoint
7.
8.{
9.structpointPrivate*pp;};
10.intget_x(point*point_);
11.intget_y(point*point_);
12.point*new_point(intx,inty);
13.}
14.#endif
源文件
[cpp]viewplaincopyprint?
1.//=======C文件:
Point.c文件========
2.#include"Point.h"
3.#include
4.structpointPrivate;
5.intx;
6.inty;
7.};
8.
9.intget_x(point*point_){
10.returnpoint_->pp->x;
11.}
12.
13.intget_y(point*point_){
14.returnpoint_->pp->y;
15.}
16.
17.point*new_point(intx,inty){
18.point*p=(point*)malloc(sizeof(point));
19.p->pp=(pointPrivate*)malloc(sizeof(pointPrivate));
20.p->pp->x=x;
21.p->pp->y=y;
22.returnp;
23.}
测试文件:
[cpp]viewplaincopyprint?
1.intmain()
2.{
3.point*p=new_point(1,2);
4.//printf("x:
%d,y:
%d\n",p->pp->x,p->pp->y);
5.printf("x:
%d,y:
%d\n",get_x(p),get_y(p));
6.}
在测试代码中,注释掉的一部分是编译不过的,因为我们已经把pointPrivate结构体的定义隐藏了。
而且必须使用new_point来创建point结构对象,否则无法初始化point结构体中的pp成员变量。
有意思的是:
这段代码生成的exe文件可能会被360误认为病毒。
三、继承
在C语言中,可以利用“结构在内存中的布局与结构的声明具有一致的顺序”这一事实实现继承。
比如我们要设计一个作图工具,其中可能涉及到的对象有Point(点),Circle(圆),由于圆是由点组成的,所有可以看成Circle继承自Point。
另外,Point和Circle都需要空间申请,空间释放等操作,所有他们有共同的基类Base。
[cpp]viewplaincopyprint?
1.//基类Base的内部头文件Base.r,对外隐藏
2.#ifndefBASE_R
3.#defineBASE_R
4.#include
5.structBase{
6.size_tsize;
7.void*(*ctor)(void*self,va_list*app);//构造函数
8.void*(*dtor)(void*self);//析构函数
9.void(*draw)(constvoid*self);//作图函数
10.};
11.#endif
12.
13.//Point的内部头文件Point.r,对外隐藏
14.#ifndefPOINT_R
15.#definePOINT_R
16.structPoint{
17.constvoid*base;//继承Base类,基类指针,放在第一个位置,const是防止修改
18.intx,y;//坐标
19.};
20.#definex(p)(((conststructPoint*)(p))->x)
21.#definey(p)(((conststructPoint*)(p))->y)
22.#endif
23.
24.//Point的头文件Point.h(对外提供接口)
25.#ifndefPOINT_H
26.#definePOINT_H
27.externconstvoid*Point;/*new(Point,x,y);*/
28.voidmove(void*point,intdx,intdy);
29.#endif
30.
31.//Point的源文件Point.c
32.#include
33.#include"Point.h"
34.#include"Point.r"
35.#include"new.h"
36.#include"Base.r"
37./**********Point类自己的构造函数***********/
38.staticvoid*Point_ctor(void*_self,va_list*app){
39.structPoint*self=_self;
40.self->x=va_arg(*app,int);
41.self->y=va_arg(*app,int);
42.returnself;
43.}
44./**********Point类自己的绘图函数***********/
45.staticvoidPoint_draw(constvoid*_self){
46.conststructPoint*self=_self;
47.printf("Pointat%d,%d\n",self->x,self->y);
48.}
49.staticconststructBase_Point={
50.sizeof(structPoint),Point_ctor,0,Point_draw
51.};
52.constvoid*Point=&_Point;
53.voidmove(void*_self,intdx,intdy){
54.structPoint*self=_self;
55.self->x+=dx,self->y+=dy;
56.}
57.
58.//Circle内部头文件Circle.r,对外隐藏
59.#ifndefCIRCLE_R
60.#defineCIRCLE_R
61.#include"Point.r"
62.structCircle{
63.conststructPoint_;//继承Point类,需放在第一位
64.intrad;
65.};
66.#endif
67.
68.//Circle的头文件Circle.h(对外提供接口)
69.#ifndefCIRCLE_H
70.#defineCIRCLE_H
71.#include"Point.h"
72.externconstvoid*Circle;/*new(Circle,x,y,rad)*/
73.#endif
74.
75.//Circle的源文件Circle.c
76.#include
77.#include"Circle.h"
78.#include"Circle.r"
79.#include"new.h"
80.#include"Base.r"
81./**********Circle类自己的构造函数***********/
82.staticvoid*Circle_ctor(void*_self,va_list*app){
83.structCircle*self=((conststructBase*)Point)->ctor(_self,app);
84.self->rad=va_arg(*app,int);
85.returnself;
86.}
87./**********Circle类自己的绘图函数***********/
88.staticvoidCircle_draw(constvoid*_self){
89.conststructCircle*self=_self;
90.printf("circleat%d,%drad%d\n",x(self),y(self),self->rad);
91.}
92.staticconststructBase_Circle={
93.sizeof(structCircle),Circle_ctor,0,Circle_draw
94.};
95.constvoid*Circle=&_Circle;
96.
97.//内存管理类头文件new.h(对外提供接口)
98.#ifndefNEW_H
99.#defineNEW_H
100.void*new(constvoid*base,...);
101.voiddelete(void*item);
102.voiddraw(constvoid*self);
103.#endif
104.
105.//内存管理类的源文件:
new.c
106.#include
107.#include
108.#include
109.#include"Base.r"
110.void*new(constvoid*_class,...){
111.conststructBase*base=_class;
112.void*p=calloc(1,base->size);
113.assert(p);
114.*(conststructBase**)p=base;
115.if(base->ctor){
116.va_listap;
117.va_start(ap,_class);
118.p=base->ctor(p,&ap);
119.va_end(ap);
120.}
121.returnp;
122.}
123.voiddelete(void*self){
124.conststructBase**cp=self;
125.if(self&&*cp&&(*cp)->dtor)
126.self=(*cp)->dtor(self);
127.free(self);
128.}
129.voiddraw(constvoid*self){
130.conststructBase*const*cp=self;
131.assert(self&&*cp&&(*cp)->draw);
132.(*cp)->draw(self);
133.}
四、多态可以是用C语言中的万能指针void*实现多态,接上面的例子:
[cpp]viewplaincopyprint?
1.#include"Circle.h"
2.#include"new.h"
3.intmain(intargc,char**argv)
4.{
5.void*p;
6.inti;
7.for(i=0;i<2;i++)
8.{
9.if(i==0)
10.p=new(Circle,1,2,3);
11.else
12.p=new(Point,1,2);
13.draw(p);
14.move(p,10,20);
15.draw(p);
16.delete(p);
17.}
18.return0;
19.}
输出结果:
circleat1,2rad3
circleat11,22rad3
Pointat1,2
Pointat11,22
五、总结
面向对象是一种程序设计思想,而C语言则是一种编程语言。
也许它并不是专门为了面向对象编程而设计,但是这绝不意味着它不能实现面向对象的程序设计。
当然以上所展示的这几个操作,如果是用别的编程语言,可能只要寥寥几行就可以完成,但是C语言想告诉我们的是:
也许我不擅长,但是并不意味着我做不到。
注:
有些头文件名被csdn误认为内部命令,而变很很诡异,请以所下载的代码为准。