• c语言实现面向对象编程(const * ,* const)


    参考了onlyshi的博客代码,orz传送门
    参考了嵌入式实践一些代码,这里就不加了。

    面向对象思想

    面向对象编程(OOP)并不是一种特定的语言或者工具,它只是一种设计方法、设计思想。它表现出来的三个最基本的特性就是封装、继承与多态。
    如果用C语言来模拟这样的编程方式,需要解决3个问题:
    数据的封装
    继承
    多态

    封装

    对于c的实现就是写一个结构体,如果封装就是放进结构体里面,那么并没有实现封装要达到的效果,因为对于c++的封装,存在成员属性的区别。public和private。
    所以对于封装,还是有每个公司自己的约定。比如以下划线开头的变量,默认为私有成员。

    继承

    继承就是基于现有的一个类去定义一个新类,这样有助于重用代码,更好的组织代码。
    对于c语言的实现就是将结构体进行一个嵌套,将基类放到继承类的第一个数据成员的位置。

    多态

    c++实现多态是使用虚函数。在C++中,只有通过基类引用或者指针,去调用虚函数的时候才发生多态,也就是说多态是发生在运行期间的,C++内部通过一个虚表来实现多态。
    虚表(Virtual Table)是这个类所有虚函数的函数指针的集合。

    虚指针(Virtual Pointer)是一个指向虚表的指针。这个虚指针必须存在于每个对象实例中,会被所有子类继承

    代码实现

    这个代码还有一些小难看(明白),可以看知乎的一个代码实现传送门,很简单易懂。

    shape.h 里面是shape类的声明

    #ifndef SHAPE_H
    #define SHAPE_H
    
    #include 
    
    // Shape 的属性
    typedef struct {
        int16_t x; 
        int16_t y; 
    } Shape;
    //int_t为一个结构的标注,通过typedef定义,利用预编译和typedef可以最有效的维护代码。
    //int16_t    : typedef signed short ;
    // Shape 的操作函数,接口函数
    void Shape_ctor(Shape * const me, int16_t x, int16_t y);
    void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy);
    int16_t Shape_getX(Shape const * const me);
    int16_t Shape_getY(Shape const * const me);
    //shape const * a,相当于const 修饰 *a,*a是shape类型,所以地址a指向的那块空间不可变
    //shape *const a相当于const修饰a,a是shape *类型,所以a本身不可变
    #endif /* SHAPE_H */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    shape.c相关函数定义

    #include "shape.h"  /* Shape class interface */
    #include   /* for printf() */
    // 构造函数
    void Shape_ctor(Shape * const me, int16_t x, int16_t y)
    {
        me->x = x;
        me->y = y;
    }
    
    void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy) 
    {
        me->x += dx;
        me->y += dy;
    }
    
    // 获取属性值函数
    int16_t Shape_getX(Shape const * const me) 
    {
        return me->x;
    }
    int16_t Shape_getY(Shape const * const me) 
    {
        return me->y;
    }
    
    
    • 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
    #include "shape.h"  /* Shape class interface */
    #include  
    int main() 
    {
        Shape s1, s2; /* multiple instances of Shape */
    
        Shape_ctor(&s1, 0, 1);
        Shape_ctor(&s2, -1, 2);
    
        printf("Shape s1(x=%d,y=%d)\n", Shape_getX(&s1), Shape_getY(&s1));
        printf("Shape s2(x=%d,y=%d)\n", Shape_getX(&s2), Shape_getY(&s2));
    
        Shape_moveBy(&s1, 2, -4);
        Shape_moveBy(&s2, 1, -2);
    
        printf("Shape s1(x=%d,y=%d)\n", Shape_getX(&s1), Shape_getY(&s1));
        printf("Shape s2(x=%d,y=%d)\n", Shape_getX(&s2), Shape_getY(&s2));
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这就实现了一个基类,shape,非常标准。
    下面开始一个继承类rectangle的编写。
    rect.h

    #ifndef RECT_H
    #define RECT_H
    
    #include "shape.h" // 基类接口
    
    // 矩形的属性
    typedef struct {
        Shape super; // 继承 Shape
        //开始写一个新的rectangle结构体,将shape类作为第一个成员。
        // 自己的属性
        uint16_t width;
        uint16_t height;
    } Rectangle;
    
    // 构造函数
    void Rectangle_ctor(Rectangle * const me, int16_t x, int16_t y,
                        uint16_t width, uint16_t height);
    
    #endif /* RECT_H */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    rect.c

    #include "rect.h"
    #include  
    // 构造函数
    void Rectangle_ctor(Rectangle * const me, int16_t x, int16_t y,
                        uint16_t width, uint16_t height)
    {
        /* first call superclass’ ctor */
        Shape_ctor(&me->super, x, y);
    
        /* next, you initialize the attributes added by this subclass... */
        me->width = width;
        me->height = height;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    main_rect.c

    #include "rect.h"
    #include  
    int main() 
    {
        Rectangle r1, r2;
    
        // 实例化对象
        Rectangle_ctor(&r1, 0, 2, 10, 15);
        Rectangle_ctor(&r2, -1, 3, 5, 8);
    
        printf("Rect r1(x=%d,y=%d,width=%d,height=%d)\n",
               Shape_getX(&r1.super), Shape_getY(&r1.super),
               r1.width, r1.height);
        printf("Rect r2(x=%d,y=%d,width=%d,height=%d)\n",
               Shape_getX(&r2.super), Shape_getY(&r2.super),
               r2.width, r2.height);
    
        // 注意,这里有两种方式,一是强转类型,二是直接使用成员地址
        Shape_moveBy((Shape *)&r1, -2, 3);
        Shape_moveBy(&r2.super, 2, -1);
    
        printf("Rect r1(x=%d,y=%d,width=%d,height=%d)\n",
               Shape_getX(&r1.super), Shape_getY(&r1.super),
               r1.width, r1.height);
        printf("Rect r2(x=%d,y=%d,width=%d,height=%d)\n",
               Shape_getX(&r2.super), Shape_getY(&r2.super),
               r2.width, r2.height);
    
        return 0;
    }
    
    • 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

    在这里插入图片描述
    在这里插入图片描述
    继承就在上面两个类实现了,下面实现多态,多态需要重新声明一下shape类,因为要加入虚表、虚指针等内容。
    shape.h
    函数指针在上一篇博客刚记录了,对于这个虚表能更好的理解。

    #ifndef SHAPE_H
    #define SHAPE_H
    
    #include 
    
    struct ShapeVtbl;
    // Shape 的属性
    typedef struct {
        struct ShapeVtbl const *vptr;
        int16_t x; 
        int16_t y; 
    } Shape;
    
    // Shape 的虚表
    struct ShapeVtbl {
        uint32_t (*area)(Shape const * const me);
        void (*draw)(Shape const * const me);
    };
    
    // Shape 的操作函数,接口函数
    void Shape_ctor(Shape * const me, int16_t x, int16_t y);
    void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy);
    int16_t Shape_getX(Shape const * const me);
    int16_t Shape_getY(Shape const * const me);
    
    static inline uint32_t Shape_area(Shape const * const me) 
    {
        return (*me->vptr->area)(me);
    }
    
    static inline void Shape_draw(Shape const * const me)
    {
        (*me->vptr->draw)(me);
    }
    
    static inline uint32_t Shape_area(Shape const * const me) 
    {
        return (*me->vptr->area)(me);
    }
    Shape const *largestShape(Shape const *shapes[], uint32_t nShapes);
    void drawAllShapes(Shape const *shapes[], uint32_t nShapes);
    
    #endif /* SHAPE_H */
    
    • 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
    #include "shape.h"
    #include 
    
    // Shape 的虚函数
    static uint32_t Shape_area_(Shape const * const me);
    static void Shape_draw_(Shape const * const me);
    
    // 构造函数
    void Shape_ctor(Shape * const me, int16_t x, int16_t y) 
    {
        // Shape 类的虚表
        static struct ShapeVtbl const vtbl = 
        { 
           &Shape_area_,
           &Shape_draw_
        };
        me->vptr = &vtbl; 
        me->x = x;
        me->y = y;
    }
    
    
    void Shape_moveBy(Shape * const me, int16_t dx, int16_t dy)
    {
        me->x += dx;
        me->y += dy;
    }
    
    
    int16_t Shape_getX(Shape const * const me) 
    {
        return me->x;
    }
    int16_t Shape_getY(Shape const * const me) 
    {
        return me->y;
    }
    
    // Shape 类的虚函数实现
    static uint32_t Shape_area_(Shape const * const me) 
    {
        assert(0); // 类似纯虚函数
        return 0U; // 避免警告
    }
    
    static void Shape_draw_(Shape const * const me) 
    {
        assert(0); // 纯虚函数不能被调用
    }
    
    
    Shape const *largestShape(Shape const *shapes[], uint32_t nShapes) 
    {
        Shape const *s = (Shape *)0;
        uint32_t max = 0U;
        uint32_t i;
        for (i = 0U; i < nShapes; ++i) 
        {
            uint32_t area = Shape_area(shapes[i]);// 虚函数调用
            if (area > max) 
            {
                max = area;
                s = shapes[i];
            }
        }
        return s;
    }
    
    
    void drawAllShapes(Shape const *shapes[], uint32_t nShapes) 
    {
        uint32_t i;
        for (i = 0U; i < nShapes; ++i) 
        {
            Shape_draw(shapes[i]); // 虚函数调用
        }
    }
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    #include "rect.h"  
    #include  
    
    // Rectangle 虚函数
    static uint32_t Rectangle_area_(Shape const * const me);
    static void Rectangle_draw_(Shape const * const me);
    
    // 构造函数
    void Rectangle_ctor(Rectangle * const me, int16_t x, int16_t y,
                        uint16_t width, uint16_t height)
    {
        static struct ShapeVtbl const vtbl = 
        {
            &Rectangle_area_,
            &Rectangle_draw_
        };
        Shape_ctor(&me->super, x, y); // 调用基类的构造函数
        me->super.vptr = &vtbl;           // 重载 vptr
        me->width = width;
        me->height = height;
    }
    
    // Rectangle's 虚函数实现
    static uint32_t Rectangle_area_(Shape const * const me) 
    {
        Rectangle const * const me_ = (Rectangle const *)me; //显示的转换
        return (uint32_t)me_->width * (uint32_t)me_->height;
    }
    
    static void Rectangle_draw_(Shape const * const me) 
    {
        Rectangle const * const me_ = (Rectangle const *)me; //显示的转换
        printf("Rectangle_draw_(x=%d,y=%d,width=%d,height=%d)\n",
               Shape_getX(me), Shape_getY(me), me_->width, me_->height);
    }
    
    
    • 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

    main.c

    #include "rect.h"  
    #include "circle.h" 
    #include  
    
    int main() 
    {
        Rectangle r1, r2; 
        Circle    c1, c2; 
        Shape const *shapes[] = 
        { 
            &c1.super,
            &r2.super,
            &c2.super,
            &r1.super
        };
        Shape const *s;
    
        // 实例化矩形对象
        Rectangle_ctor(&r1, 0, 2, 10, 15);
        Rectangle_ctor(&r2, -1, 3, 5, 8);
    
        // 实例化圆形对象
        Circle_ctor(&c1, 1, -2, 12);
        Circle_ctor(&c2, 1, -3, 6);
    
        s = largestShape(shapes, sizeof(shapes)/sizeof(shapes[0]));
        printf("largetsShape s(x=%d,y=%d)\n", Shape_getX(s), Shape_getY(s));
    
        drawAllShapes(shapes, sizeof(shapes)/sizeof(shapes[0]));
    
        return 0;
    }
    
    • 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

    circle类
    circle.h

    #ifndef CIRCLE_H
    #define CIRCLE_H
    
    #include "shape.h" // 基类接口
    
    // 矩形的属性
    typedef struct {
        Shape super; // 继承 Shape
    
        // 自己的属性
        uint16_t radius;
    } Circle;
    
    // 构造函数
    void Circle_ctor(Circle * const me, int16_t x, int16_t y,
                        uint16_t radius);
    
    #endif /* CIRCLE_H */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    circle.c

    #include  
    #include "../include/circle.h"  
    
    // Circle 虚函数
    static uint32_t Circle_area_(Shape const * const me);
    static void Circle_draw_(Shape const * const me);
    
    // 构造函数
    void Circle_ctor(Circle * const me, int16_t x, int16_t y,
                        uint16_t radius)
    {
        static struct ShapeVtbl const vtbl = 
        {
            &Circle_area_,
            &Circle_draw_
        };
        Shape_ctor(&me->super, x, y); // 调用基类的构造函数
        me->super.vptr = &vtbl;           // 重载 vptr
        me->radius = radius;
    }
    
    // Circle's 虚函数实现
    static uint32_t Circle_area_(Shape const * const me) 
    {
        Circle const * const me_ = (Circle const *)me; //显示的转换
        return (uint32_t)me_->radius * (uint32_t)me_->radius * 3;
    }
    
    static void Circle_draw_(Shape const * const me) 
    {
        Circle const * const me_ = (Circle const *)me; //显示的转换
        printf("Circle_draw_(x=%d,y=%d,radius=%d)\n",
               Shape_getX(me), Shape_getY(me), me_->radius);
    }
    
    
    • 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
    函数签名及返回值的约定

    c语言函数签名:函数名、参数(入参、出参)、返回值。
    对于研发的一种约定俗成,对于一个函数,要返回值来表示errcode,并且0表示no error,成功,对于-1表示失败,对于1,表示没有成功,但是返回了一些有意义的东西。

    const

    const * :表示指针指向的值不可变,但是指针可以重新赋新地址。

    重载

    重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
    每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
    最常用的地方就是构造器的重载。
    重载规则:

    • 被重载的方法必须改变参数列表(参数个数或类型不一样);
    • 被重载的方法可以改变返回类型;
    • 被重载的方法可以改变访问修饰符;
    • 被重载的方法可以声明新的或更广的检查异常;
    • 方法能够在同一个类中或者在一个子类中被重载。
    • 无法以返回值类型作为重载函数的区分标准。
  • 相关阅读:
    想做某类型游戏却找不到对应的教程,怎么办?
    【微电网优化】基于matlab遗传算法求解微电网经济优化问题【含Matlab源码 2062期】
    领域驱动设计-领域建模
    LeetCode | 218. 天际线问题
    深入探究MinimalApi是如何在Swagger中展示的
    正则运行报错 unclosed group near index 9 解决
    关于 Bash 脚本中 Shebang 的趣事
    Bug分级处理指南:优先级与严重性的平衡
    微信小程序使用路由传参和传对象的方法
    快速幂(c++,超级详细)
  • 原文地址:https://blog.csdn.net/weixin_43786143/article/details/126877376