用C语言实现面向对象的开发

C语言的对象化模型
面向对象的特征主要包括:
.封装,隐藏内部实现
.继承,复用现有代码
.多态,改写对象行为
采用C语言实现的关键是如何运用C语言本身的特性来实现上述面向对象的特征。

1.1  封装
封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的
方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象
能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能
力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必
知晓行为实现的细节,只须用设计者提供的消息来访问该对象。
在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个semaphore,会命名
成take_semaphore,重点在take这个动作上。面向对象编程中刚好相反,命名为rt_sem_take,即名词+动词的形式,重点在名词上,体现了一个对象的方法。另外对于某些方法,仅局限在对象内部使用,它们将采用static修辞把作用范围局限在一个文件的内部。通过这样的方式,把一些不想让用户知道的信息屏蔽在封装里,用户只看到了外层的接口,从而形成了面向对象中的最基本的对象封装实现。

一般属于某个类的对象会有一个统一的创建,析构过程。
.对象内存数据块已经存在,需要对它进行初始化 – rt_sem_init;
.对象内存数据块还未分配,需要创建并初始化 – rt_sem_create。
可以这么认为,对象的创建(create)是以对象的初始化(init)为基础的,创建动作相比较而言多了个
内存分配的动作。
相对应的两类析构方式:
.由rt_sem_init初始化的semaphore对象 – rt_sem_detach;
.由rt_sem_create创建的semaphore对象 – rt_sem_delete.

1.2  继承
继承性是子类自动共享父类之间数据和方法的机制。它由类的派生功能体现。一个类直接继承其
它类的全部描述,同时可修改和扩充。继承具有传递性。继承分为单继承(一个子类只有一父类)
和多重继承(一个类有多个父类,当前RT-Thread的对象系统不能支持)。类的对象是各自封闭的,
如果没继承性机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而
且还促进系统的可扩充性。

类似的实现代码如下程序清单:
/*  父类  */
struct  parent_class
{
  int  a,  b;
  char  *str;
};

/*  继承于父类的子类  */
struct  child_class
{
  struct  parent class  p;
  int  a,  b;
};

/*  操作示例函数*/
void  func()
{
  struct  child_class  obj,  *obj_ptr;  /*  子类对象及指针  */
  struct  parent_class  *parent_ptr;  /*  父类指针  */
  obj_ptr  =  &obj;

  /*  取父指针  */
  parent_ptr  =  (struct  parent*)  &obj;

  /*  可通过转换过类型的父类指针访问相应的属性  */
  parent ptr->a  =  1;
  parent ptr->b  =  5;

  /*  子类属性的操作  */
  obj ptr->a  =  10;
  obj ptr->b  =  100;
}

在上面代码中,注意child_class结构中第一个成员p,这种声明方式代表child_class类型的数据中
开始的位置包含一个parent_class类型的变量。在函数func中obj是一个child_class对象,正像这个
结构类型指示的,它前面的数据应该包含一个parent_class类型的数据。在第21行的强制类型赋值
中parent_ptr指向了obj变量的首地址,也就是obj变量中的p对象。好了,现在parent_ptr指向的是
一个真真实实的parent类型的结构,那么可以按照parent的方式访问其中的成员,当然也包括可以
使用和parent结构相关的函数来处理内部数据,因为一个正常的,正确的代码,它是不会越界访
问parent结构体以外的数据。
经过这基本的结构体层层相套包含,对象简单的继存关系就体现出来了:父对象放于数据块的最
前方,代码中可以通过强制类型转换获得父对象指针。

1.3  多态
    对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种
现象称为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息
的对象自行决定,如是,同一消息即可调用不同的方法。例如:抽象设备具备接口统一的读写接口。
串口是设备的一种,也应支持设备的读写。但串口的读写操作是串口所特有的,不应和其他设备
操作完全相同,例如操作串口的操作不应应用于SD卡设备中。
    多态性的实现受到继承性的支持,利用类继承的层次关系,把具有通用功能的协议存放在类层次
中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的
对象就能给通用消息以不同的响应。

对象模型采用结构封装中使用指针的形式达到面向对象中多态的效果,例如:
/*  抽象父类  */
struct  parent_class
{
  int  a;

  /*  反映不同类别属性的方法  */
  void  (*vfunc)(int  a);
}

/*  抽象类的方法调用  */
void  parent_class_vfunc(struct  parent_class  *self,  int  a)
{
  assert(self  !=  NULL);
  assert(slef->vfunc  !=  NULL);

  /*  调用对象本身的虚拟函数  */
  self->vfunc(a);
}

/*  继承自parent class的子类  */
struct  child_class
{
  struct  parent_class  parent;
  int  b;
};

/*  子类的构造函数  */
void  child_class_init(struct  child_class*  self)
{
  struct  parent_class*  parent;

  /*  强制类型转换获得父类指针  */
  parent  =  (struct  base_class*)  self;
  assert(parent  !=  NULL);
  
  /*  设置子类的虚拟函数  */
  parent->vfunc  =  child_class_vfunc;
}

/*  子类的虚拟函数实现  */
static  void   child class vfunc(struct  child class*self,  int  a)
{
  self->b  =  a  +  10;
}

阅读(4398)| 评论(5)
    
喜欢推荐转载
 MIDI数据完成实现标准的DTMF信号 海量数据处理方法
最近读者
登录后,您可以在此留下足迹。 liu_jing07
liu_jing
  zhansh21cn
zhansh21
  zougang_215
zougang_
  liuxueuestc
liuxueue
  shuangyu226@126
shuangyu
  dmlgt123
dmlgt123
 张俊勇
张俊勇
  xiaoying1277
xiaoying
热度
 1037140300
10371403
  yzfcer
yzfcer
 
关闭
玩LOFTER,免费冲印20张照片,人人有奖!     我要抢>
评论
点击登录|昵称:

 
江苏 泰州
03-19 13:56
songyajun0107
太精彩了!人才啊!受益匪浅啊
回复
广东 佛山
2011-03-27 09:30
双木
我水平还没到,咋办
回复

2010-12-30 12:40
woshizhaozigeng
我仿照您的思路写了一个简单的多态例子,不过实现的不好看, 基类里的虚函数参数void  (*vfunc)(struct parent_class *self, int  a);   /包含一个基类类型指针.
下面每个派生类自己的虚函数实现void  (*vfunc)(struct parent_class *self, int  a);   也要包含一个基类的指针.这样写我感觉很难看..
回复

2010-12-30 12:40
woshizhaozigeng
struct  parent_class   
{
 int  a; 
 void  (*vfunc)(struct parent_class *self, int  a);   //虚函数
};
struct child_class
{
 struct parent_class base;
 void (vfunc_my_implemention)(struct parent_class *self, int a);
};

void child_vfunc(struct parent_class *self, int info)
{
 printf("这是子类实现的虚方法........\n");
}
void init_child_class(struct child_class *self)
{
 ((struct parent_class *)self)->vfunc = child_vfunc;
}
int main()
{
 struct parent_class *ptrparent_class = NULL;
 struct child_class c_object;
 init_child_class(&c_object);
 ptrparent_class = (struct parent_class *)&c_object;
 ptrparent_class->vfunc(ptrparent_class, 2);                 //多态
 return 0;
}
回复

2010-12-30 11:54
woshizhaozigeng
你好,您的关于c语言实现实现多态的部分代码实现有问题。。
当用c实现封装里,由于没有编译器的支持,所以每个函数参数要添加一个指向自己的指针struct  child class*self,相当于c++里面的pthis,所以您说的抽象父类的虚函数里面也要添加一个pthis指针,而且要参数要是void*类型的。您说对吗?
/*  抽象父类  */
struct  parent_class
{
  int  a;

  /*  反映不同类别属性的方法  */
  void  (*vfunc)(int  a);         //改正一下. void(*vfunc)(void *self, int a);
}
     Redy的开发语言是C,但在源码中,有很多地方都使用到了面向对象编程的方法,例如:在基本数据类型这一个模块,所有的数据类型都继承robject;在抽象语法树模块,所有的节点都继承astobjct。在linux内核中,也有很多是使用的面向对象方法,在虚拟文件系统,驱动模型中都可以看到。c语言是一种结构化编程语言,以模块工能和处理过程设计为主,实现数据与代码分隔化。面向对象方法论中,核心是类,类是用于创造对象的模板,其三要素为:封装,继承,多态。C语言本身对面向对象的支持很弱,但可以通过一些技巧来实现。下面通过一个具体的实例来说明实现这此技巧。

实例介简:

       在几何中,所有的几何类型都继承父类“形状(shape)”,父类“形状”有两处属性s_type和s_name。其中s_type用于表示该形状所属的类型,s_name用于表于该形状态的名称。而且父类shape还有两个虚接口,一个为shape_area用于返回该形状的面积,一个为shape_perimeter用于返回该形状的周长。所子继承“形状”的子类都必须实现这两个接口。
struct shape;
struct shape_ops
{
    /*返回几何体的面积*/
    float (*so_area)(struct shape*); 
    /*返回几何体的周长*/
    int (*so_perimeter)(struct shape*);
};
struct shape
{
    int* s_type;
    char* s_name;
    struct shape_ops* s_ops; /*虚接口,所有子类必须实现*/
};

float shape_area(struct shape* s)  /*求形状面积*/
{
    return s->s_ops->so_area(s);  
}
int shape_perimeter(struct shape* s) /*求周长*/
{
    return s->s_ops->so_perimeter(s);
}
        几何体“三角形(triangle)”继承父类“形状”,并且实现了父类的两个虚接口。“三角形”有三条边,分别用t_side_a,t_side_b,t_side_c来表于三条边的长度。
/*三角形*/
struct triangle
{
    struct shape t_base;
    int t_side_a;
    int t_side_b;
    int t_side_c;
};

float triangle_area(struct shape* s)  /*三角形面积,用海伦公式*/
{
    struct triangle* t=(struct triangle*)s;
    int a=t->t_side_a;
    int b=t->t_side_b;
    int c=t->t_side_c;
    float p=(a+b+c)/2;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
int triangle_perimeter(struct shape* s)  /*三角形周长*/
{
    struct triangle* t=(struct triangle*)s;
    int a=t->t_side_a;
    int b=t->t_side_b;
    int c=t->t_side_c;
    return a+b+c;
}
struct shape_ops triangle_ops=    /*对父类虚接口的实现*/
{
    triangle_area,
    triangle_perimeter,
};
struct triangle* triangle_create(int a,int b,int c)  /*创建三角形*/
{
    struct triangle* ret=(struct triangle*)malloc(sizeof (*ret));
    ret->t_base.s_name="triangle";
    ret->t_base.s_ops=&triangle_ops;
    ret->t_side_a=a;
    ret->t_side_b=b;
    ret->t_side_c=c;
    return ret;
}
        几何体“矩形(rectangle)”继承父类“形状”,同样也实现的父类的两个虚接口。有两个属性r_width和r_height,分别表示矩形的长和宽。
/*矩形*/
struct rectangle
{
    struct shape r_base;
    int r_width;
    int r_height;
};

float rectangle_area(struct shape* s)  /*矩形面积*/
{
    struct rectangle* r=(struct rectangle*)s;
    return r->r_width*r->r_height;
}
int rectangle_perimeter(struct shape* s)/*矩形周长*/
{
    struct rectangle* r=(struct rectangle*)s;
    return (r->r_width+r->r_height)*2;
}
struct shape_ops rectangle_ops=      /*对父类虚接口的实现*/
{
    rectangle_area,
    rectangle_perimeter,
};

struct rectangle* rectangle_create(int width, int height)  /*创建矩形*/
{
    struct rectangle* ret=(struct rectangle*)malloc(sizeof(*ret));
    ret->r_base.s_name="rectangle";
    ret->r_base.s_ops=&rectangle_ops;
    ret->r_height=height;
    ret->r_width=width;
    return ret;
}
测试代码:
int main()
{
    struct shape* s[4];
    s[0]=triangle_create(5,5,4);
    s[1]=triangle_create(3,4,5);
    s[2]=rectangle_create(10,12);
    s[3]=rectangle_create(5,8);

    int i=0;
    for(i=0;i<4;i++)
    {
        float area=shape_area(s[i]);    
        int perimeter=shape_perimeter(s[i]);
        char* name=s[i]->s_name;

        printf("name:%s ,area:%.2f ,perimeter:%d\n",name,area,perimeter);
    }
    return 0;
}

运行结果:
name:triangle ,area:9.17 ,perimeter:14
name:triangle ,area:6.00 ,perimeter:12
name:rectangle ,area:120.00 ,perimeter:44
name:rectangle ,area:40.00 ,perimeter:26
我谈C语言和面向对象 by 招

之前看完骏豪写的《C语言实现面向对象编程》,同时发现最近blog没有高质量的编程系文章orz,于是就一直想写篇东西谈谈语言和设计,,也算是对自己做程序和OOAD的经验教训小结
打好草稿之后发现又长又臭,索性换一种短而快的方式——分节,一来好写,二来免得大家懒得看=_,=
PS:貌似我还欠着n篇游戏特效制作的文章。。。囧

首先更正骏豪的几个错误观点:P
1.面向对象和基于对象是两个不完全相同的东西。
基于对象就是类最基本的特征,将数据和方法绑定到一起。
在C这样没有类的语言中可以用一个结构表示一个类的数据,然后在所有与之相关的函数(方法)中传入这个结构(或指针),也能达到同样的效果,典型例子如C标准库的FILE
另外Win32API中采取的是句柄,即用一个数值作为对象的ID,去代替你操作的对象,同样能达到封装数据的目的。
但是这并未达到面向对象的程度

面向对象是在基于对象的基础上做到继承和多态,尤其是多态,这是与基于对象的本质区别,也是面向对象与结构化编程最不同的地方
C可以实现多态,方法是定义一个struct,包含若干函数指针,然后由函数指针指向具体的函数以实现多态
然而要真正实现多态,那么需要衍生出很多“周边产品”——运行时类型检查(RTTI,例如C#的is关键字)、反射等等。这点要在C语言实现只能用取巧的办法,例如自动分配类的id然后查id来匹配,或者用宏等等。
具体可以参考GTK,它的编写风格是面向对象,也支持RTTI。

所以Windows至少从API的设计看来并不是面向对象。

2.C++ is better than C? OO is better than structral programming?
没有面向对象,甚至没有基于对象,我们真的写不出一个大型的程序吗?
我想大部分初学C++的人都会接受这么一种观点:C++是更好的C语言(很不幸连张锋也这么跟我们说。。囧),至少你爱用OO的特性就用,不爱就当它是C来用。
类似地,面向对象(OO)才是王道,结构化编程纯属老土——这样的观点也充斥于很多C++新手甚至是有相当经验的人士当中。
我写这段并非想争论C和C++孰优孰劣。理解如何通过C语言进行高质量编程对程序设计有非常大的帮助,包括面向对象的能力。

我先把问题放这:C语言+结构化编程能写高质量的大型程序。问题是怎么写?

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。