赞
踩
面向对象编程(OOP)四大特征:抽象、封装、继承、多态。主流的面向对象编程语言(如C++、Java、C#等)都有完善的面向对象实现机制。C语言是面向过程编程语言,但可以通过结构体和指针实现类似的面向对象语言功能。所以,我更倾向于将其理解为一种编程思想,而不是面向对象编程语言仅有的特性。
本文基于一个实例,演示如何在C语言中实现多态。
//基类函数成员
typedef int (*fptrSet)(void *, int);
typedef int (*fptrGet)(void *);
typedef void (*fptrDisplay)(void *);
//派生类新增函数成员
typedef double (*fptrGetArea)(void *);
(1)定义“基类”,包含数据成员和函数成员,其中函数成员用函数指针定义。
(2)实现函数成员,定义为独立的函数,参数为结构体指针。
(3)实现“构造函数”,以参数形式将数据成员传递进来,直接用(2)中定义的函数给函数成员赋值。
//定义基类Shape 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; }
(1)基于C++及Java等面向对象程序设计语言中派生类的特性来实现该派生类。
(2)测试以下几点:派生类覆盖基类函数成员display;派生类新增数据成员width、height;派生类新增函数成员func1。
//定义派生类 typedef struct _rectangle { //维护一个基类成员变量,必须为变量,不能用指针 Shape base; //派生类额外的函数成员 fptrGetArea func1; //派生类额外的数据成员 int width; int height; } Rectangle; //覆盖基类的dispalay方法 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会访问实际函数地址,如果该函数已被派生类覆盖,则调用派生类的版本。
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])); } //输出shapes[1]中display函数的地址,两者相等,是该多态访问成功的基础 printf("%p\n", ((Rectangle *)shapes[1])->base.display); printf("%p\n", shapes[1]->display); //输出shapes[1]的面积,对于派生类特有的函数,应该转变为派生类指针后再访问 printf("area=%lf\n", ((Rectangle *)shapes[1])->func1(shapes[1])); return 0; }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。