源码:https://github.com/baitxaps/CPointer
typedef void (*fptrSet)(void *,int);
typedef int (*fptrGet)(void *);
typedef void(*fptrDisPlay)();
/*
1.C++多态是建立在基类及派生类之间继承关系的基础上的,C不支持继承,所以得模拟结构体之间的继承
定议和使用两个结构体来说明多态行为:Shape结构体表示基“类”,而Rectangle结构表示从基类Shape派生的类
结构体的变量分配顺序对这个技术的工作原理影响很大,当创建一个派生类/结构体的实例时,会先分配基类/结构体的
变量,也需要考虑打算覆盖的函数
2.Shape结构体持有函数指针,接着是x和y的坐标整数
3.vFunctions结构体:由一系列函数指针组成.fptrSet和fptrGet函数指针为整数类型数据定义了典型的getter和setter
函数,用来获取和设置Shape和Rectangle的x和y值,fptrDisplay函数指针定义了一个参数为空,返回值为空的函数
会用这个打印函数解释多态行为
4.当一个类/结构体执行函数时,其行为取决它作用的对象是什么,如对Shpae调用打印函数
就会显示一个Shape,对Rectangle调用打印函数就会显示Rectangle.在面向对象编程中这通常通过虚表(VTable)
实现,vFunctions结构就是用来实现这种功能
*/
typedef struct _functions{
fptrSet setX;
fptrGet getX;
fptrSet setY;
fptrGet getY;
fptrDisPlay display;
}vFunctions;
typedef struct _shape{
vFunctions functions;
int x;
int y;
}Shape;
/*
看起来实现Shape结构体这么做有点大费周章,但是从Shape派生出一个Rectangle结构体,就会看到这么做
强大的能力
*/
typedef struct _rectangle{
Shape base;
int width;
int height;
}Rectangle;
void shapeDisplay(Shape *shape){
printf("Shape\n");
}
void shapeSetX(Shape *shape,int x){
shape->x = x;
}
int shapeGetX(Shape *shape){
return shape->x;
}
void shapeSetY(Shape *shape,int y){
shape->y= y;
}
int shapeGetY(Shape *shape){
return shape->y;
}
void rectangleSetX(Rectangle *rectangle,int x){
rectangle->base.x= x;
}
void rectangleSetY(Rectangle *rectangle,int y){
rectangle->base.y= y;
}
int rectangleGetX(Rectangle *rectangle){
return rectangle->base.x;
}
int rectangleGetY(Rectangle *rectangle){
return rectangle->base.y;
}
void rectangleDisplay(){
printf("Rectangle\n");
}
/*
为对象分配内存,然后为其设置函数
*/
Shape *getShapeInstance(){
Shape *shape = (Shape *)malloc(sizeof(Shape));
shape->functions.display =shapeDisplay;
shape->functions.setX = (void *)shapeSetX;
shape->functions.getX = (void *)shapeGetX;
shape->functions.setY = (void *)shapeSetY;
shape->functions.getY = (void *)shapeGetY;
shape->x = 100;
shape->y = 100;
return shape;
}
Rectangle *getRectangleInstance(){
Rectangle *rectangle = (Rectangle *)malloc(sizeof(Rectangle));
rectangle->base.functions.display =rectangleDisplay;
rectangle->base.functions.setX = (void *)rectangleSetX;
rectangle->base.functions.getX = (void *)rectangleGetX;
rectangle->base.functions.setY = (void *)rectangleSetY;
rectangle->base.functions.getY = (void *)rectangleGetY;
rectangle->base.x = 200;
rectangle->base.y = 200;
rectangle->width = 300;
rectangle->height = 500;
return rectangle;
}
int main(int argc,constchar * argv[]) {
// Shape *sptr = getShapeInstance();
// sptr->functions.setX(sptr,35);
// sptr->functions.display();
// printf("%d\n",sptr->functions.getX(sptr));
//
//
// Rectangle *rptr = getRectangleInstance();
// rptr->base.functions.setX(rptr,65);
// rptr->base.functions.display();
// printf("%d\n",rptr->base.functions.getX(rptr));
/*
1.创建一个Shape指针的数组,然后初始化。当把Rectangle赋给shapes[1]时,没有必要非得把它转成(Shape*),
但是不强转会产生警告
2.创建Shape指针的数组过程中,创建一个Rectangle实例并将其赋给数组的第二个元素,当for循环中打印元素时,
它倒用Rectangle的函数行为而不是Shape的,这就是一种多态行为。display函数的行为取决于它所执行的对象
3.我们把它当成Shape来访问,因此不应该试图用shape[i]来访问其宽度和高度,原因是这个元素可能引用一个Rectangle,
也可能不是。如果不这么做,就可能访问shapes的其他内存,那些内存并不代表宽度和高度信息,会导致不可预期的结果
4.也可以再从Shape中派生一个结构,如Circle,把它加入数组,而不需要大量修改代码。我们也需要为这个结构体创建函数
5.如给基结构Shape增加一个函数,如getArea,就可以为每一个类实现一个唯一的getArea函数,在循环中,可以轻易地把所有Shape和Shape派生的结构体的面积累加,而不需要先判断处理的是什么类型的Shape.
6.如果Shape的getArea实现足够了,那么就不需要为其他结构增加函数了。这样很容易维护和扩展一个应用程序
*/
Shape *shape[3];
shape[0] = getShapeInstance();
shape[0]->functions.setX(shape[0],5);
shape[1] =getRectangleInstance();
shape[1]->functions.setX(shape[1],15);
shape[2] = getShapeInstance();
shape[2]->functions.setX(shape[2],25);
for (int i =0; i<3; i++) {
shape[i]->functions.display();
printf("%d\n",shape[i]->functions.getX(shape[i]));
}
return 0;
}
输出结果:
Shape
5
Rectangle
15
Shape
25
Program ended with exit code: 0