面向对象编程(OOP)四大特征:抽象、封装、继承、多态。主流的面向对象编程语言(如C++、Java、C#等)都有完善的面向对象实现机制。
C语言是面向过程编程语言,但可以通过结构体和指针实现类似的面向对象语言功能。所以,我更倾向于将其理解为一种编程思想,而不是面向对象编程语言仅有的特性。
本文基于一个实例,演示如何在C语言中实现多态。
定义函数指针
1 2 3 4 5 6
| typedef int (*fptrSet)(void *, int);typedef int (*fptrGet)(void *); typedef void (*fptrDisplay)(void *);
typedef double (*fptrGetArea)(void *);
|
定义Shape基类
(1)定义“基类”,包含数据成员和函数成员,其中函数成员用函数指针定义。
(2)实现函数成员,定义为独立的函数,参数为结构体指针。
(3)实现“构造函数”,以参数形式将数据成员传递进来,直接用(2)中定义的函数给函数成员赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| typedef struct _shape { struct { fptrSet setX; fptrGet getX; fptrSet setY; fptrGet getY; fptrDisplay display; }; int x; int y; } Shape;
void shapeDisplay(Shape *shape) { printf("Shape\n"); } void shapeSetX(Shape *shape, int x) { shape->x = x; } void shapeSetY(Shape *shape, int y) { shape->y = y; } int shapeGetX(Shape *shape) { return shape->x; } int shapeGetY(Shape *shape) { return shape->y; }
Shape *shapeConstructor(int x, int y) { Shape *shape = (Shape *)malloc(sizeof(Shape)); shape->x = x; shape->y = y; shape->display = shapeDisplay; shape->setX = shapeSetX; shape->setY = shapeSetY; shape->getX = shapeGetX; shape->getY = shapeGetY; return shape; }
|
定义Rectangle派生类
(1)基于C++及Java等面向对象程序设计语言中派生类的特性来实现该派生类。
(2)测试以下几点:派生类覆盖基类函数成员display;派生类新增数据成员width、height;派生类新增函数成员func1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| typedef struct _rectangle { Shape base;
fptrGetArea func1;
int width; int height; } Rectangle;
void rectangleDisplay(Rectangle *rectangle) { printf("Rectangle\n"); }
double getArea(Rectangle *rectangle) { return rectangle->width * rectangle->height; }
Rectangle *rectangleConstructor(int width, int height, int x, int y) { Rectangle *rectangle = (Rectangle *)malloc(sizeof(Rectangle));
rectangle->width = width; rectangle->height = height; rectangle->func1 = getArea;
rectangle->base = (Shape){.x = x, .y = y, .display = rectangleDisplay, .setX = shapeSetX, .setY = shapeSetY, .getX = shapeGetX, .getY = shapeGetY}; return rectangle; }
|
测试
用基类指针指向派生类对象,调用基类方法时,可选择调用基类或派生类的方法实现,原理在于shapes[1]- >display会访问实际函数地址,如果该函数已被派生类覆盖,则调用派生类的版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int main() { Shape *shapes[3]; shapes[0] = shapeConstructor(0, 0);shapes[1] = rectangleConstructor(100, 200, 50, 50); shapes[2] = shapeConstructor(600, 600);
for (int i = 0; i < 3; i++) { shapes[i]->display(shapes[i]);printf("%d\n", shapes[i]->getX(shapes[i])); }
printf("%p\n", ((Rectangle *)shapes[1])->base.display); printf("%p\n", shapes[1]->display);
printf("area=%lf\n", ((Rectangle *)shapes[1])->func1(shapes[1]));
return 0; }
|