欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

用C实现面向对象

程序员文章站 2023-09-29 17:30:29
很多开发工程师都是从学习c语言的hello world!开始的,都知道c语言中的指针是一把利剑,一不留意就伤了自个。但其c语言绝对是一个宗师级语言,这是不可否认的。 由于我们开发的需要在多个平台上运...

很多开发工程师都是从学习c语言的hello world!开始的,都知道c语言中的指针是一把利剑,一不留意就伤了自个。但其c语言绝对是一个宗师级语言,这是不可否认的。
由于我们开发的需要在多个平台上运行且需要面向对象的一些特性、所以特写此文章。权当抛砖引玉。

一、概述
c语言是一种面向过程的程序设计语言、而c++在语言级别上添加了很多新机制(继承,多态等)
因此这里说说使用c语言实现封装,继承和多态的方法。


二、基本知识


1、结构体


在c语言中,常把一个对象用结构体进行封装,这样便于对对象进行操作,比如:1
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也可用于修饰函数或者变量,表示该函数或者变量只能在该文件中使用。可利用它们对数据或者函数进行隐藏或者限制访问权限。


三、封装


在c语言中,可以用结构+函数指针来模拟类的实现,而用这种结构定义的变量就是对象。


封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。


有两种方法实现封装:


1、利用c语言语法。在头文件中声明,在c文件中真正定义它。


这样可以隐藏内部信息,因为外部不知道对象所占内存的大小,所以不能静态的创建该类的对象,只能调用类提供的创建函数才能创建。这种方法的缺陷是不支持继承,因为子类中得不到任何关于父类的信息。如:


/**
* point的头文件point.h(对外提供接口)
*/
#ifndef point_h
#define point_h


extern const void * point; /* new(point, x, y); */


void move(void * point, int dx, int dy);


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


#define point_x(p)(((const struct point *)(p)) -> x)
#define point_y(p)(((const struct point *)(p)) -> y)


#endif



//point的源文件point.c
#include
#include point.h
#include cnew.h
#include base.h


/**********point类自己的构造函数***********/
static void * point_new(void * _self, va_list * app) {
struct point * self = _self;
self->x = va_arg(*app, int);
self->y = va_arg(*app, int);
printf(point_new self = %p , self);
return self;
}


/**********point类自己的析构函数***********/
static void* point_delete(void * _self) {
printf(call point_delete self =%p , _self);
printf(@@@@@@@@@@@@@@@@@@@@@@@@@@@@ );
return null;
}


/**********point类自己的绘图函数***********/
static void point_draw(const void * _self) {
const struct point * self = _self;
printf(point_draw at %d,%d , self->x, self->y);
}


void move(void * _self, int dx, int dy) {
struct point * self = _self;
printf(call move self =%p , _self);
self->x += dx;
self->y += dy;
}


static const struct base _point = { sizeof(struct point), point_new, point_delete, point_draw };
const void * point = &_point;


四、继承


在c语言中,可以利用“结构在内存中的布局与结构的声明具有一致的顺序”这一事实实现继承。


比如我们要设计一个作图工具,其中可能涉及到的对象有point(点),circle(圆),由于圆是由点组成的,所有可以看成circle继承自point。另外,point和circle都需要空间申请,空间释放等操作,所有他们有共同的基类base。


#ifndef c_new_h
#define c_new_h


/**
* 内存管理类头文件cnew.h(对外提供接口)
*/


void * cnew(const void * base, ...);


void cdelete(void * item);


void draw(const void * self);


#endif /* c_new_h */



/**
* 内存管理类的源文件:cnew.c
*/
#include
#include
#include
#include
#include cnew.h
#include base.h


void * cnew(const void * _class, ...) {
const struct base * base = _class;
void * p = calloc(1, base->size);
assert(p);
*(const struct base **) p = base;
if (base->constructor) {
va_list ap;
va_start(ap, _class);
p = base->constructor(p, &ap);
va_end(ap);
}
return p;
}


void cdelete(void * self) {
const struct base ** cp = self;
if (self && *cp && (*cp)->destroy)
self = (*cp)->destroy(self);
free(self);
}


void draw(const void * self) {
const struct base * const * cp = self;
assert(self && *cp && (*cp)->draw);
(*cp)->draw(self);
}

/**
* 基类base的内部头文件base.r,对外隐藏
*/
#ifndef base_r
#define base_r


#include


struct base {
size_t size;
void * (*constructor)(void * self, va_list * app); //构造函数
void * (*destroy)(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); */


void move(void * point, int dx, int dy);


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


#define point_x(p)(((const struct point *)(p)) -> x)
#define point_y(p)(((const struct point *)(p)) -> y)


#endif


//point的源文件point.c
#include
#include point.h
#include cnew.h
#include base.h


/**********point类自己的构造函数***********/
static void * point_new(void * _self, va_list * app) {
struct point * self = _self;
self->x = va_arg(*app, int);
self->y = va_arg(*app, int);
printf(point_new self = %p , self);
return self;
}


/**********point类自己的析构函数***********/
static void* point_delete(void * _self) {
printf(call point_delete self =%p , _self);
printf(@@@@@@@@@@@@@@@@@@@@@@@@@@@@ );
return null;
}


/**********point类自己的绘图函数***********/
static void point_draw(const void * _self) {
const struct point * self = _self;
printf(point_draw at %d,%d , self->x, self->y);
}


void move(void * _self, int dx, int dy) {
struct point * self = _self;
printf(call move self =%p , _self);
self->x += dx;
self->y += dy;
}


static const struct base _point = { sizeof(struct point), point_new, point_delete, point_draw };
const void * point = &_point;


/**
* circle的头文件circle.h(对外提供接口)
*/


#ifndef circle_h
#define circle_h


#include point.h


extern const void * circle; /* new(circle, x, y, rad) */


struct circle {
const struct point pbase; //继承point类,需放在第一位
int radius;
int (*area)(void *self);// 面积,扩展方法
};


#define circle_area(p) (((const struct circle *)(p)) -> area(p))


#endif




/**
* circle的源文件circle.c
*/
#include
#include circle.h
#include cnew.h
#include base.h


/**********circle类自己的扩展函数***********/
static int circle_area(void * _self) {
const struct circle * self = _self;
printf(call circle_area self =%p , _self);
return self->radius * self->radius;
}


/**********circle类自己的构造函数***********/
static void * circle_new(void * _self, va_list * app) {
struct circle * self = ((const struct base *) point)->constructor(_self, app);
self->radius = va_arg(*app, int);
self->area = circle_area;
printf(call circle_new self =%p , _self);
return self;
}


/**********circle类自己的构造函数***********/
static void* circle_delete(void * _self) {
printf(call circle_delete self =%p , _self);
printf(@@@@@@@@@@@@@@@@@@@@@@@@@@@@ );
return null;
}


/**********circle类自己的绘图函数***********/
static void circle_draw(const void * _self) {
const struct circle * self = _self;
int x = point_x(self);
int y = point_y(self);
printf(circle_draw at %d,%d rad %d , x, y, self->radius);
}


static const struct base _circle = { sizeof(struct circle), circle_new, circle_delete, circle_draw };
const void * circle = &_circle;






/**
* 测试函数
*/


#include circle.h
#include cnew.h


int oo_main(int argc, char ** argv) {
void * p;
int i;
for (i = 0; i < 2; i++) {
if (i == 0) {
p = cnew(circle, 1, 2, 3);
circle_area(p);
} else {
p = cnew(point, 1, 2);
}
draw(p);
move(p, 10, 20);
draw(p);
cdelete(p);
}
return 0;
}


/***********************************
* 测试结果:
*
* point_new self = 0x50a1d8
* call circle_new self =0x50a1d8
* circle_draw at 1,2 rad 3
* call move self =0x50a1d8
* circle_draw at 11,22 rad 3
* call circle_delete self =0x50a1d8
*
* point_new self = 0x5096a0
* point_draw at 1,2
* call move self =0x5096a0
* point_draw at 11,22
* call point_delete self =0x5096a0
*
************************************/


五、多态


可以是用c语言中的万能指针void* 实现多态,接上面的例子:


//测试main.c

int oo_main(int argc, char ** argv) {
void * p;
int i;
for (i = 0; i < 2; i++) {
if (i == 0) {
p = cnew(circle, 1, 2, 3);
circle_area(p);
} else {
p = cnew(point, 1, 2);
}
draw(p);
move(p, 10, 20);
draw(p);
cdelete(p);
}
return 0;
}


六、总结


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