C语言实现封装、继承和多态

1、概述

 C语言是一种面向过程的程序设计语言,而C++是在C语言基础上衍生来了的面向对象的语言,实际上,很多C++实现的底层是用C语言实现的,如在Visual C++中的Interface其实就是struct,查找Interface的定义,你可以发现有这样的宏定义:
  • #ifndef Interface
  • #define Interface struct
  • #endif
C++在语言级别上添加了很多新机制(继承,多态等),而在C语言中,我们也可以使用这样的机制,前提是我们不得不自己实现。 本文介绍了用C语言实现封装,继承和多态的方法。

2、 基本知识

在正式介绍C语言实现封装,继承和多态事前,先介绍一下C语言中的几个概念和语法。

(1) 结构体

在C语言中,常把一个对象用结构体进行封装,这样便于对对象进行操作,比如:
  • strcut Point{ 
  •   int x; 
  •   int y; 
  • };
结构体可以嵌套。因而可以把一个结构体当成另一个结构体的成员,如:
  • struct Circle { 
  •     struct Point point_; 
  •     int radius; 
  • };
该结构体与以下定义完全一样(包括内存布置都一样):
  • struct Circle { 
  •     int x; 
  •     int y; 
  •     int radius; 
  • };

(2)函数指针

函数指针是指针的一种,它指向函数的首地址(函数的函数名即为函数的首地址),可以通过函数指针来调用函数。 如函数:
  • int func(int a[], int n);
可以这样声明函数指针:
  • int (*pFunc)(int a[], int n);
这样使用:
  • pFunc = func;
  • (*pFunc)(a, n);//或者PFunc(a, n);
可以用typedef定义一个函数指针类型,如:
  • typdef int (*FUNC)(int a[], int n)
可以这样使用:
  • int cal_a(FUNC fptr, int a[], int n)
  • {
  • //实现体
  • }

(3)extern与static

extern和static是C语言中的两个修饰符,extern可用于修饰函数或者变量,表示该变量或者函数在其他文件中进行了定义;static也可用于修饰函数或者变量,表示该函数或者变量只能在该文件中使用。可利用它们对数据或者函数进行隐藏或者限制访问权限。

3、  封装

在C语言中,可以用结构+函数指针来模拟类的实现,而用这种结构定义的变量就是对象。 封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。有两种方法实现封装: (1) 利用C语言语法。在头文件中声明,在C文件中真正定义它。 这样可以隐藏内部信息,因为外部不知道对象所占内存的大小,所以不能静态的创建该类的对象,只能调用类提供的创建函数才能创建。这种方法的缺陷是不支持继承,因为子类中得不到任何关于父类的信息。如:
  • //头文件:point.h 
  • #ifndef POINT_H 
  • #define POINT_H 

  • struct Point; 
  • typedef struct Point point; 

  • point * new_point(); //newer a point object 
  • void free_point(point *point_);// free the allocated space 

  • #endif
  • //C文件:point.c 
  • #include”point.h” 

  • strcut Point 
  •     int x; 
  •     int y; 
  • }; 

  • point * new_point() 
  •     point * new_point_ = (point *) malloc(sizeof(point)); 
  •     return new_point_; 

  • void free_point(point *point_) 
  •     if(point_ == NULL) 
  •         return; 
  •     free(point_); 
  • }
(2)    把私有数据信息放在一个不透明的priv变量或者结构体中。只有类的实现代码才知道priv或者结构体的真正定义。如:
  • //头文件:point.h 
  • #ifndef POINT_H 
  • #define POINT_H 

  • typedef struct Point point; 
  • typedef struct pointPrivate pointPrivate; 

  • strcut Point 
  •     Struct pointPrivate *pp; 
  • }; 

  • int get_x(point *point_); 
  • int get_y(point *point_); 

  • point * new_point(); //newer a point object 
  • void free_point(point *point_);// free the allocated space 

  • #endif
  • //C文件:point.c 
  • #include”point.h” 

  • struct pointPrivate 
  •     int x; 
  •     int y; 

  • int get_x(point *point_) 
  •     return point_->pp->x; 

  • int get_y(point *point_) 
  •     return point_->pp->y; 

  • //others…..

4、  继承

在C语言中,可以利用‘’结构在内存中的布局与结构的声明具有一致的顺序‘’这一事实实现继承。 比如我们要设计一个作图工具,其中可能涉及到的对象有Point(点),Circle(圆),由于圆是由点组成的,所有可以看成Circle继承自Point。另外,Point和Circle都需要空间申请,空间释放等操作,所有他们有共同的基类Base。
  • //内存管理类new.h 
  • #ifndef NEW_H 
  • #define NEW_H 

  • void * new (const void * class, ...); 
  • void delete (void * item); 
  • void draw (const void * self); 

  • #endif
  • //内存管理类的C文件:new.c 
  • #include “new.h” 
  • #include “base.h” 

  • void * new (const void * _base, ...) 
  •     const struct Base * base = _base; 
  •     void * p = calloc(1, base->size); 
  •     assert(p); 
  •     *(const struct Base **) p = base; 

  •     if (base ->ctor) 
  •     { 
  •         va_list ap; 
  •         va_start(ap, _base); 
  •         p = base->ctor(p, &ap); 
  •         va_end(ap); 
  •     } 
  •     return p; 

  • void delete (void * self) 
  •     const struct Base ** cp = self; 
  •     if (self && * cp && (* cp) —> dtor) 
  •         self = (* cp)—>dtor(self); 
  •     free(self); 

  • void draw (const void * self) 
  •     const struct Base * const * cp = self; 
  •     assert(self &&* cp && (* cp)->draw); 
  •     (*cp)->draw(self); 
  • }
  • //基类:base.h 
  • #ifndef BASE_H 
  • #define BASE_H 

  • struct Base 
  •     size_t size; //类所占空间 
  •     void * (* ctor) (void * self, va_list * app); //构造函数 
  •     void * (* dtor) (void * self); //析构函数 
  •     void (* draw) (const void * self); //作图 
  • }; 

  • #endif
  • //Point头文件(对外提供的接口):point.h 
  • #ifndef   POINT_H 
  • #define  POINT_H 

  • extern const void * Point;                /* 使用方法:new (Point, x, y); */

  • #endif
  • //Point内部头文件(外面看不到):point.r 
  • #ifndef POINT_R 
  • #define POINT_R 

  • struct Point 
  •     const void * base; //继承,基类指针,放在第一个位置,const是防止修改 
  •     int x, y;        //坐标 
  • }; 

  • #endif
  • //Point的C文件:point.c 
  • #include “point.h” 
  • #include “new.h” 
  • #include “point.h” 
  • #include “point.r” 

  • static void * Point_ctor (void * _self, va_list * app) 
  •     struct Point * self = _self; 
  •     self ->x = va_arg(* app, int); 
  •     self ->y = va_arg(* app, int); 

  •     return self; 

  • static void Point_draw (const void * _self) 
  •     const struct Point * self = _self; 
  •     printf(“draw (%d,%d)”, self -> x, self -> y); 

  • static const struct Base _Point = {
  •   sizeof(struct Point), Point_ctor, 0, Point_draw 
  • }; 
  • const void * Point = & _Point;
  • //测试程序:main.c 
  • #include “point.h” 
  • #include “new.h” 

  • int main (int argc, char ** argv) 
  •     void * p = new(Point, 1, 2); 
  •     draw(p); 
  •     delete(p); 
  • }
同样,Circle要继承Point,则可以这样:
  • struct Circle 
  •     const struct Point point; //放在第一位,可表继承 
  •     int radius; 
  • };

5、  多态

可以是用C语言中的万能指针void* 实现多态,接上面的例子:
  • //测试main.c 
  • void * p = new(Point, 1, 2); 
  • void * pp = new(Circle, 1, 2); 

  • draw(p); //draw函数实现了多态 
  • draw(pp); 

  • delete(p); 
  • delete(pp);

6、  总结

C语言能够模拟实现面向对象语言具有的特性,包括:多态,继承,封装等,现在很多开源软件都了用C语言实现了这几个特性,包括大型开源数据库系统postgreSQL,可移植的C语言面向对象框架GObject,无线二进制运行环境BREW。采用C语言实现多态,继承,封装,能够让软件有更好的可读性,可扩展性。