C语言实现OOP (ing)

    想用C来实现OOP,关键在于结构体。struct和OOP中的class最大区别为默认的继承访问权限:struct是public的,大家都能看到,class是private的,只有指定的对象看得到。

码农翻身里面有一篇文章讲到过用c语言来实现OOP,今天参照着它撸了一下oop的三大概念:封装、继承、多态。

1 封装:意思就是把信息隐藏起来。

举个例子,先创建一个shape结构体,然后实现create和init等方法,之后将源代码封装成库。这样一来,外部程序只能看见头文件的接口原型,而看不到其内部结构。

  shape.h

//声明结构体
struct Shape;
//api原型
struct Shape *Shape_create(int x,int y);
void Shape_init(struct Shape *self, int x,int y);
void Shape_move(struct Shape *self, int x,int y);
float Shape_area(struct Shape *self);
...

shape.c

struct Shape{
  int x;
  int y;
};

struct Shape *Shape_create(int x, int y){
  struct Shape *s = malloc(sizeof(struct Shape));
  s->x= x;
  s->y= y;
  return s;
}

void Shape_move(struct Shape *self, int x,int y)
{
  self->x= x;
  self->y= y;
}

float Shape_area(struct Shape *self){
  return (*self->vptr->area)(self);  //vptr成员后面会新增进去
}

main.c中调用api

int main(int argc, *argv[]){
  ...
  struct Shape *s = Shape_create(0,0);
  Shape_move(s,1,2);
  ...
}

虽然结构体成员和create、move等方法是分开写的,看起来没class那么融合,可也算是实现了简单的封装。

2 继承

先看下面代码:

//Rectangle子类
struct Rectangle{
  struct Shape base;
  int length;
  int width;
};

struct Rectangle *Rectangle_create(int x,int y,int l, int w){

  struct Rectangle *r = malloc(sizeof(struct Rectangle)); //创建对象

  Shape_init((struct Shape *)r,x,y);  //继承了Shape基类的成员函数:shape_init
  r->length = l;
  r->width = w;

  return r;
}
...

int main(){
  struct Rectangle *r = Rectangle_create(1,2,30,40);
  Shape_move((struct Shape*)r,10,20);
  ...
}

上述代码创建了另一个结构体Rectangle。因其包含了Shape,所以称之为子类。其在内存中是这样的:

继承关系如下:

有两个地方需要注意:

  • 在继承Shape_init这个api时,传进去的对象为 r,此时需要类型转换为Shape基类。
  • 在Rectangle子类中Shape基类的位置需放在最前。下面举个例子说明一下:
struct rectangle{
  int length;
  int width;
  struct shape base; //此处放在最后,会有问题
};
...
struct rectangle *r = malloc(sizeof(struct rectangle));
struct shape *tmp = (struct shape *)r; //强制类型转换
tmp->x=110;

上述代码中 将Shape类放在了最后。本来是将110赋值给tmp 的 x成员,实际却是给了length成员,调试结果如下:

(r和tmp的地址一样,只是里头的数据代表的类型不同)

3 多态

多态最难搞,关键在于理解函数指针的使用。先上代码:

//新建虚函数表
struct shapeVtbl{
  float (*area)(struct shape *self);
  void (*draw)(struct shape *self);
}
//将虚函数表添加到Shape基类中
struct shape{
  struct shapevtbl *vptr;
  int x;
  int y;
};

新结构体shapeVtbl 包含两个函数指针,目的是为了获取对象的面积及画图,我们称其为虚函数表。之后不管创建的子类是Rectangle 还是其它的,只要是继承自Shape基类,都会有个虚函数表。

下面是Rectangle 和Square对象。前者的函数指针area指向的是自家的 Rectangle_area();而Square对象的函数指针area指向的是Square_area()。

开头部分的shape.h中有个api为Shape_area(...),在子类继承之后,调用它就可以直接指向自家的api地址。如下:

float Area;
...
struct Rectangle *r = Rectangle_create(1,2,30,40);
Area = Shape_area((struct Shape*)r);

注意:上图中的虚函数表需要你手动将自家的函数入口地址填进去,比如 &Rectangle_area 和 &Rectangle_draw,而C++则可以自动完成。

参考 https://mp.weixin.qq.com/s/2ivQ9hcRvZnhk89jzAppSg

发布了23 篇原创文章 · 获赞 16 · 访问量 5654

猜你喜欢

转载自blog.csdn.net/xiong3110/article/details/87257283
ing