大家都找到对象了,C语言的对象呢?

98 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

C++的面向对象

面向对象,都很熟悉
张口就出的, 封装、继承、多态

封装

即把数据和相应数据对应的操作给他们封存起来
使其形成一个类型

class Base {
protected:
  int data;

public:
  void print() { printf("[base]%d\n", data); }
  void read(int data) { this->data = data; }
};

继承

可以把众多相似的类型中共同的部分给他抽象出来。
一个类只要继承了这个类,就拥有了这个类的东西

class Base {
protected:
  int data;

public:
  void print() { printf("[base]%d\n", data); }
  void read(int data) { this->data = data; }
};

class Foo : public Base {
public:
  void add() {}
  void del() {}
};

多态

由子类去继承自父类,且对于相同的行为,表现出不同的特征 体现在子类重写父类的方法

class Base {
protected:
  int data;
public:
  virtual void print() { printf("[base]%d\n", data); }
  void read(int data) { this->data = data; }
};

class Foo : public Base {
public:
  void print() { printf("[Foo]%d\n", data); }
};
class Bar : public Base {
public:
  void print() { printf("[Bar]%d\n", data); }
};

其实面向对象是一种思想,那么我C语言是不是也可以有对象呢?
那么,其实前人早已在用面向对象的思想干活了
没有这些语法糖,我们也可以活得很滋润

C语言的对象

封装

把相关数据封装到 struct 里面
用相关函数来进行操作

typedef struct {
  int data;
} Base;

void Base_init(Base *const self, int x) { self->data = x; }
void Base_print(Base *const self) { printf("[base]%d\n", self->data); }
void Base_read(Base *const self, int x) { self->data = x; }

什么? 这样很麻烦? 可以,有解决办法的

struct Base_func;
typedef struct Base {
  int data;
  struct Base_func *func;
} Base;

void Base_print(Base *const self) { printf("[base]%d\n", self->data); }
void Base_read(Base *const self, int x) { self->data = x; }
struct Base_func {
  void (*print)(Base *const);
  void (*read)(Base *const, int);
};
void Base_init(Base *const self, int x) {
  static struct Base_func baseFunc = { .print = Base_print, .read = Base_read };
  self->data = x;
  self->func = &baseFunc;
}
int main() {
  Base base;
  Base_init(&base, 1);
  base.func->print(&base);

  return 0;
}

这样,你就可以把函数封装起来然后调用,不过这一种有点不如第一种形式

继承

继承的本意是什么?把父类的东西全部拿过来用 那我们就可以写成这样

typedef struct {
  int data;
} Base;

typedef struct {
  Base base;
  int foo;
} Foo;

void Base_init(Base *const self, int data) { self->data = data; }
void Base_print(Base *const self) { printf("[base]%d\n", self->data); }
void Base_read(Base *const self, int data) { self->data = data; }

void Foo_init(Foo *const self, int foo, int data) {
  Base_init(&self->base, data);
  self->foo = foo;
}
void Foo_print(Foo *const self) { Base_print(&self->base); }
void Foo_read(Foo *const self, int data) { Base_read(&self->base, data); }

可以看到,我的处理方式是调用foo类的init的时候,同时去初始化base的成员。
这就是谁的成员交给谁的构造
子类继承的父类的方法,这个时候就是直接调用父类的方法即可 然后子类再根据自身的特点,即可以实现子类的特化

多态

如何表现出动态多态的特性?
比如c++的用虚函数实现的动态绑定的实现?
在c语言中一个如何实现它呢?
这个时候就要用到前面提到的那种函数指针了

已经尽量少写代码了,但还是比较多
基类是Base
两个派生类是Foo 和 Bar

typedef struct {
  struct Base_func *func;
  int data;
} Base;
struct Base_func {
  void (*print)(void *const);
};

void Base_print(void *const self) {
  Base *This = (Base *)self;
  printf("[base]%d\n", This->data);
}
void Base_read(Base *const self, int data) { self->data = data; }
void Base_init(Base *const self, int data) {
  self->data = data;
  self->func = (struct Base_func *)malloc(sizeof(struct Base_func));
  *(self->func) = (struct Base_func){.print = Base_print};
}

typedef struct {
  Base base;
  int foo;
} Foo;
void Foo_print(void *const self) {
  Foo *This = (Foo *)self;
  printf("[foo]%d\n", This->foo);
}
void Foo_read(Foo *const self, int data) { Base_read(&self->base, data); }
void Foo_init(Foo *const self, int foo, int data) {
  Base_init(&self->base, data);
  self->foo = foo;
  self->base.func->print = Foo_print;
}

typedef struct {
  Base base;
  double bar;
} Bar;
void Bar_print(void *const self) {
  Bar *This = (Bar *)self;
  printf("[bar]%lf\n", This->bar);
}
void Bar_read(Bar *const self, int data) { Base_read(&self->base, data); }
void Bar_init(Bar *const self, double bar, int data) {
  Base_init(&self->base, data);
  self->bar = bar;
  self->base.func->print = Bar_print;
}
#define New(classname) (classname *)malloc(sizeof(classname))
void print(Base *base) { base->func->print(base); }
int main() {
  Base *base = New(Base);
  Base_init(base, 1);
  print(base);

  base = (Base *)New(Foo);
  Foo_init((Foo *)base, 1, 2);
  print(base);

  base = (Base *)New(Bar);
  Bar_init((Bar *)base, 1, 2);
  print(base);

  return 0;
}

总结

面向对象可以不需要语言支持,也大概可以磕磕碰碰的写出来
可是,我们可以发现,就是简单是一点点代码,c语言也会写很多出来,且容易出现不安全和未定义的情况
在用c语言时,语言假设我们知道自己在干什么,事实上可能是高看了我们
(还是C++、Java这种有面向对象语言支持的写了快,且不容易错误)