1、引语
编程语言发展到今天,已经有相当多种了,有些甚至都没有听说过和接触过。对于一个程序员来说,C语言都是基础,C语言是一个面向过程的程序设计语言,那么如何使用C语言来写出面向对象的程序呢?
面向对象编程的三个基本特征是封装、继承和多态。
先使用一个C++的例子,然后把例子转成C语言。
////////////////////////////Object.h/////////////////////////
//基类
class Object
{
public:
Object(double h);
~Object(){}
virtual double calcuArae() = 0; //计算表面积
virtual double calcuVolume() = 0;//计算体积
//有一个共同的属性
double height;
};
//圆柱体
class Cylinder :public Object
{
public:
Cylinder(double h,double r);
~Cylinder(){}
virtual double calcuArae();
virtual double calcuVolume();
double radius;
};
//长方体
class Cuboid :public Object
{
public:
Cuboid(double h,double w,double l);
~Cuboid(){}
virtual double calcuArae();
virtual double calcuVolume();
double weith;
double length;
};
实现:
///////////////////////////Object.cpp///////////////////////
//基类构造函数
Object::Object(double h)
:height(h)
{
}
/////////////////////////////////////////////////////////
Cylinder::Cylinder(double h, double r)
:Object(h)
,radius(r)
{
}
double Cylinder::calcuArae()
{
return 2*PI*radius*radius + 2*PI*radius*height;
}
double Cylinder::calcuVolume()
{
return (PI*(radius*radius)/4)*height;
}
/////////////////////////////////////////////////////////
Cuboid::Cuboid(double h, double w, double l)
:Object(h)
,weith(w)
,length(l)
{
}
double Cuboid::calcuArae()
{
return length*weith*height;
}
double Cuboid::calcuVolume()
{
return 2*(length*weith+weith*height+length*height);
}
多态:
QVector<Object*> vec;
vec<< new Cylinder(2,3);
vec<< new Cuboid(2,3,4);
for(int i = 0 ; i < vec.size();i++)
{
qDebug()<<vec.at(i)->calcuArae();
qDebug()<<vec.at(i)->calcuVolume();
}
C++的多态是依靠虚函数来实现的。
2、封装
在C++中使用class来封装数据与方法的 在C语言中使用的struct来封装数据与方法的,在标准的C运行库里一组函数fopen(), fclose(),fread(), and fwrite() ,就是在操作FILE结构的,每个函数都会传入一个FILE的指针。
封装比较好办
//抽象对象
typedef struct
{
int16_t x;
int16_t y;
} Shape;
//构造函数
Shape *Shape_new(int16_t x, int16_t y)
{
Shape * obj = (Shape*)malloc(sizeof(Shape));
obj->x = x;
obj->y = y;
return obj;
}
//方法
void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy)
{
me->x += dx;
me->y += dy;
}
3、继承
C语言里的继承有点像C++的组合,子类包函父类的对象。
//具体对象
typedef struct
{
Shape super; //这里一定要放在最前面
uint16_t width;
uint16_t height;
} Rectangle;
//构造函数
Rectangle*Rectangle_new(int16_t x, int16_t y,uint16_t width, uint16_t height)
{
Rectangle* r = (Rectangle*)malloc(sizeof(Rectangle));
r->super.x = x;
r->super.y = y;
r->width = width;
r->height = height;
return r;
}
//方法,使用基类的方法
应用:
Rectangle* r1 = Rectangle_new(1,1,2,3);
Shape_moveBy((Shape *)r1, -2, 3);//向上类型转换,upcasting在C++里是安全的
4、多态
- 多态的实现就是建立一张虚表,然后实现后绑定。对于C语言来说就是使用函数指针的方法。
先封装:
//基类
typedef struct
{
struct ObjectVtbl const *vptr;//虚表,这个也得放在最前面
double height;
}Object;
//虚表类
struct ObjectVtbl
{
double (*area)(Object * const o);
double (*volume)(Object * const o);
};
//Cylinder子类
typedef struct
{
Object super;
double radius;
}Cylinder;
//Cuboid子类
typedef struct
{
Object super;
double width;
double length;
}Cuboid;
再把所有的方法实现了:
这些方法只在模块内使用不expouse给用户
static double cylinder_area(Object * const o)
{
Cylinder * const me = (Cylinder *)o;
double r = (uint)me->radius;
double h = (uint)o->height;
return 2*PI*r*r + 2*PI*r*h;
}
static double cylinder_volume(Object * const o)
{
Cylinder * const me = (Cylinder *)o;
double r = (uint)me->radius;
double h = (uint)o->height;
return (PI*(r*r)/4)*h;
}
/////////////////////////////////////////////////////////////
static double cuboid_volume(Object * const o)
{
Cuboid * const me = (Cuboid *)o;
double w = (uint)me->width;
double h = (uint)o->height;
double l = (uint)me->length;
return l*w*h;
}
static double cuboid_area(Object * const o)
{
Cuboid * const me = (Cuboid *)o;
double w = (uint)me->width;
double h = (uint)o->height;
double l = (uint)me->length;
return 2*(l*w+w*h+l*h);
}
再把构造函数给实现了:
Cuboid * new_Cuboid(double l,double w,double h)
{
//这里要使用static、const,因为所有的实例都使用同一套函数
//而且这里应该要在rom上分配空间
static struct ObjectVtbl const vtbl = { &cuboid_area,
&cuboid_volume,
};
Cuboid * c = (Cuboid *)malloc(sizeof(Cuboid));
c->super.vptr = &vtbl;
c->super.height = h;
c->length = l;
c->width = w;
return c;
}
Cylinder * new_Cylinder(double r,double h)
{
static struct ObjectVtbl const vtbl = { &cylinder_area,
&cylinder_volume,
};
Cylinder * c = (Cylinder *)malloc(sizeof(Cylinder));
c->super.vptr = &vtbl;
c->super.height = h;
c->radius = r;
return c;
}
最后把要后绑定的方法实现了:
static double calcu_area(Object * const o)
{
return (*o->vptr->area)(o);
}
static double calcu_volume(Object * const o)
{
return (*o->vptr->volume)(o);
}
使用:
Cylinder *cylinder = new_Cylinder(2,6);
Cuboid *cuboid = new_Cuboid(2,6,5);
Object *table[2];
table[0] = (Object *)cylinder;
table[1] = (Object *)cuboid;
for(int i = 0 ; i < 2 ;i++)
{
qDebug()<<calcu_area(table[i]);
qDebug()<<calcu_volume(table[i]);
}
5、总结
- Object Oriented Programming 是一种设计方法而非一种语言
- 封装与继承,C语言可以轻松的handle,但是多态的话相对复杂,而且没有带来任何性能上的提升。
- 如果是写框架之类的可以考虑使用多态来隐藏细节,一般的应用不要使用。
6、参考
https://www.cs.rit.edu/~ats/books/ooc.pdf
https://www.state-machine.com/doc/AN_OOP_in_C.pdf
https://www.youtube.com/watch?v=d5k_HBPFm0M
https://stackoverflow.com/questions/415452/object-orientation-in-c
http://ldeniau.web.cern.ch/ldeniau/html/oopc.html
https://sourceforge.net/projects/lwoopc/