C语言之“面向对象编程”

面向对象编程(OOP)四大特征:抽象、封装、继承、多态。主流的面向对象编程语言(如C++、Java、C#等)都有完善的面向对象实现机制。C语言是面向过程编程语言,但可以通过结构体和指针实现类似的面向对象语言功能。所以,我更倾向于将其理解为一种编程思想,而不是面向对象编程语言仅有的特性。
本文基于一个实例,演示如何在C语言中实现多态。

1 定义函数指针

//基类函数成员
typedef int (*fptrSet)(void *, int);
typedef int (*fptrGet)(void *);
typedef void (*fptrDisplay)(void *);

//派生类新增函数成员
typedef double (*fptrGetArea)(void *);

2 定义Shape基类

(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;
}

3 定义Rectangle派生类

(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;
}

4 测试

用基类指针指向派生类对象,调用基类方法时,可选择调用基类或派生类的方法实现,原理在于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;
}

在这里插入图片描述

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值