C 语⾔实现⾯向对象编程
C 语⾔实现⾯向对象编程
1、引⾔
⾯向对象编程(OOP)并不是⼀种特定的语⾔或者⼯具,它只是⼀种设计⽅法、设计思想。它表现出来的三个最基本的特性就是封装、继承与多态。很多⾯向对象的编程语⾔已经包含这三个特性了,例如 Smalltalk、C++、Java。但是你也可以⽤⼏乎所有的编程语⾔来实现⾯向对象编程,例如 ANSI-C。要记住,⾯向对象是⼀种思想,⼀种⽅法,不要太拘泥于编程语⾔。
2、封装
封装就是把数据和⽅法打包到⼀个类⾥⾯。其实C语⾔编程者应该都已经接触过了,C 标准库
中的 fopen(), fclose(), fread(), fwrite()等函数的操作对象就是 FILE。数据内容就是 FILE,数据的读写操作就是 fread()、fwrite(),fopen() 类⽐于构造函数,fclose() 就是析构函数。这个看起来似乎很好理解,那下⾯我们实现⼀下基本的封装特性。这是 Shape 类的声明,⾮常简单,很好理解。⼀般会把声明放到头⽂件⾥⾯ “Shape.h”。
来看下 Shape 类相关的定义,当然是在 “Shape.c” ⾥⾯。#ifndef SHAPE_H #define SHAPE_H #include <stdint.h>// Shape 的属性typedef struct  {    int16_t x;    int16_t y; } Shape;// 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);#endif /* SHAPE_H */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
再看下 main.c
编译之后,看看执⾏结果:
整个例⼦,⾮常简单,⾮常好理解。以后写代码时候,要多去想想标准库的⽂件IO操作,这样也有意识的去培养⾯向对象编程的思维。
3、继承
继承就是基于现有的⼀个类去定义⼀个新类,这样有助于重⽤代码,更好的组织代码。在 C 语⾔⾥⾯,去实现单继承也⾮常简单,只要把基类放到继承类的第⼀个数据成员的位置就⾏了。#include "shape.h"// 构造函数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#include "shape.h"  /* Shape class interface */#include <stdio.h>  /* for printf() */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_m
oveBy(&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;}
12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Shape s1(x=0,y=1)Shape s2(x=-1,y=2)Shape s1(x=2,y=-3)Shape s2(x=0,y=0)
1
2
3
4c语言如何去学
例如,我们现在要创建⼀个 Rectangle 类,我们只要继承 Shape 类已经存在的属性和操作,再添加不同于 Shape 的属性和操作到Rectangle 中。
下⾯是 Rectangle 的声明与定义:
我们来看⼀下 Rectangle 的继承关系和内存布局#ifndef RECT_H #define RECT_H #include "shape.h" // 基类接⼝// 矩形的属性typedef struct  {    Shape super; // 继承 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
1011
12
13
14
15
16
17
18
19#include "rect.h"// 构造函数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 */    me->width = width;    me->height = height;}
1
2
3
4
5
6
7
8
9
10
11
12
13
因为有这样的内存布局,所以你可以很安全的传⼀个指向 Rectangle 对象的指针到⼀个期望传⼊ Shape 对象的指针的函数中,就是⼀个函数的参数是 “Shape *”,你可以传⼊ “Rectangle *”,并且这是⾮常安全的。这样的话,基类的所有属性和⽅法都可以被继承类继承!
输出结果:#include "rect.h"  #include <stdio.h> int main() {    Rectangle r1, r2;    // 实例化对象    Rect
angle_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
31
4、多态
C++ 语⾔实现多态就是使⽤虚函数。在 C 语⾔⾥⾯,也可以实现多态。
现在,我们⼜要增加⼀个圆形,并且在 Shape 要扩展功能,我们要增加 area() 和 draw() 函数。但是 Shape 相当于抽象类,不知道怎么去计算⾃⼰的⾯积,更不知道怎么去画出来⾃⼰。⽽且,矩形和圆形的⾯积计算⽅式和⼏何图像也是不⼀样的。
下⾯让我们重新声明⼀下 Shape 类Rect r1(x=0,y=2,width=10,height=15)Rect r2(x=-1,y=3,width=5,height=8)Rect r1(x=-2,y=5,width=10,height=15)Rect r2(x=1,y=2,width=5,height=8)
1
2
3
4#ifndef SHAPE_H #define SHAPE_H #include <stdint.h>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);}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

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。