我是打算从C之内使用的对象特别感兴趣,而不是形成解释语言如Python的核心对象的实现。
Answer 1:
我倾向于做这样的事情:
struct foo_ops {
void (*blah)(struct foo *, ...);
void (*plugh)(struct foo *, ...);
};
struct foo {
struct foo_ops *ops;
/* data fields for foo go here */
};
有了这些结构的定义,实施FOO的代码看起来是这样的:
static void plugh(struct foo *, ...) { ... }
static void blah(struct foo *, ...) { ... }
static struct foo_ops foo_ops = { blah, plugh };
struct foo *new_foo(...) {
struct foo *foop = malloc(sizeof(*foop));
foop->ops = &foo_ops;
/* fill in rest of *foop */
return foop;
}
然后,在代码中使用FOO:
struct foo *foop = new_foo(...);
foop->ops->blah(foop, ...);
foop->ops->plugh(foop, ...);
这个代码可以收拾与宏或内联函数,所以它看起来更类似C
foo_blah(foop, ...);
foo_plugh(foop, ...);
但如果你坚持使用一个相当短的名字为“行动”现场,简单地写出所示的代码本来不是特别详细。
这种技术是完全足够在C实施一个相对简单的基于对象的设计中,但它不处理更高级的需求,例如明确地表示类,方法和继承。 对于这些,你可能需要类似的GObject(如埃夫拉伊姆提到的),但我建议,确保你确实需要更复杂的框架的额外功能。
Answer 2:
您使用的术语“对象”是一个有点模糊,所以我将假设你问如何用C实现面向对象编程的某些方面(随时纠正我的这个假设。)
方法多态性:
方法多态性使用函数指针通常仿真中℃。 例如,如果我有我用来表示image_scaler(一些需要的图像,并调整大小以新的维度)一个结构,我可以做这样的事情:
struct image_scaler {
//member variables
int (*scale)(int, int, int*);
}
然后,我可以做一些图像缩放器这样:
struct image_scaler nn, bilinear;
nn->scale = &nearest_neighbor_scale;
bilinear->scale = &bilinear_scale;
这让我实现多态行为是发生在一个image_scaler并通过简单地传递一个不同的image_scaler使用它的尺度方法的任何功能。
遗产
继承,则通常可获得这样:
struct base{
int x;
int y;
}
struct derived{
struct base;
int z;
}
现在,我可以自由地使用衍生的额外的字段,以让基地的所有的“继承”字段一起。 此外,如果你有一个功能,只需要在一个结构基础。 你可以简单地施展你的结构dervied指针变成没有结果的一个结构基指针
Answer 3:
图书馆等的GObject 。
基本上GObject的提供描述不透明的值(整数,字符串)和对象(通过手动地描述接口-如函数指针的结构,基本上correspoinding到++ V表中C) -常见的方式在结构上的详细信息可以在其被发现参考
你常常还用手实现虚函数表中“在普通的C COM”
Answer 4:
正如你可以浏览所有的答案看到,有图书馆,函数指针,继承,封装等方式,所有可用的(C ++原本是前端为C)。
然而,我发现,软件一个很重要的方面是可读性。 你试过从10年前读码? 因此,我倾向于采取最简单的办法做这样的事情在C.对象时
问以下内容:
- 这是为有期限的客户(如果是的话,考虑OOP)?
- 我可以使用OOP(通常更少的代码,更快的发展,更可读的)?
- 我可以使用库(现有的代码,现有的模板)?
- 我在由存储器或CPU(例如Arduino的)约束?
- 是否有其他技术原因用C?
- 我可以保持我的C非常简单,可读?
- 我真的需要为我的项目是什么OOP的特点?
我通常会恢复到像油嘴API,让我概括了我的代码,并提供了一个非常可读的接口。 如果需要更多,我添加函数指针多态性。
class_A.h:
typedef struct _class_A {...} Class_A;
Class_A* Class_A_new();
void Class_A_empty();
...
#include "class_A.h"
Class_A* my_instance;
my_instance = Class_A_new();
my_instance->Class_A_empty(); // can override using function pointers
Answer 5:
看看IJG的实现。 他们不仅使用了setjmp / longjmp的异常处理,他们有虚函数表和一切。 这是一个良好的书面和足够小的图书馆,你得到一个很好的例子。
Answer 6:
类似戴尔的做法,但一个多footgun位是PostgreSQL的代表如何解析树节点,表达式类型,并在内部等。 有默认的Node
和Expr
结构,沿行
typedef struct {
NodeTag n;
} Node;
其中NodeTag
是unsigned int的的typedef,并有一个与一堆描述所有可能的节点类型的常量的头文件。 节点本身是这样的:
typedef struct {
NodeTag n = FOO_NODE;
/* other members go here */
} FooNode;
和FooNode
可以转换为一个Node
不受惩罚,因为C结构的怪癖:如果两个结构具有相同的第一部件,可以将它们浇铸到彼此。
是的,这意味着一个FooNode
可以转换为一个BarNode
,你可能不希望这样做。 如果你想正确的运行时类型检查,GObject的是要走的路,虽然准备恨的生活,而你得到了它的窍门。
(注:从内存中的例子,我还没有砍死在一段时间Postgres的内部的。 开发人员常见问题有更多的信息。)