我特别感兴趣的是要在C语言中使用的对象,而不是构成解释语言核心的对象的实现,例如python。
答案 0 :(得分:9)
我倾向于这样做:
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, ...);
虽然如果你坚持使用“ops”字段的相当短的名称,只需写出原来显示的代码并不是特别冗长。
这种技术完全适用于在C中实现相对简单的基于对象的设计,但它不处理更高级的要求,例如显式表示类和方法继承。对于那些,你可能需要类似GObject(如EFraim所提到的),但我建议确保你真的需要更复杂框架的额外功能。
答案 1 :(得分:7)
你对术语“对象”的使用有点模糊,所以我假设你正在问如何使用C来实现面向对象编程的某些方面(在这个假设下随意纠正我。 )
Method Polymorphism:
方法多态性通常使用函数指针在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;
}
现在,我可以自由使用派生的额外字段,同时获取所有“继承”的字段。此外,如果您有一个只接受结构基础的函数。你可以简单地将struct dervied指针转换为一个没有后果的struct base指针
答案 2 :(得分:4)
GObject等图书馆。
基本上GObject提供了描述不透明值(整数,字符串)和对象的常用方法(通过手动描述接口 - 作为函数指针的结构,基本上与C ++中的VTable相对应) - 可以找到更多关于结构的信息在reference
中您通常也会像"COM in plain C"
那样手工实施vtable答案 3 :(得分:3)
从浏览所有答案可以看出,有图书馆, 函数指针,继承手段,封装等等 可用(C ++最初是C的前端)。
但是,我发现软件的一个非常重要的方面是 可读性。您是否尝试过阅读10年前的代码?作为一个 结果,我倾向于采取最简单的方法来做这样的事情 C.中的对象。
提出以下问题:
我通常会回复到允许我这样的GLIB 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
答案 4 :(得分:0)
查看IJG's实施情况。他们不仅使用setjmp / longjmp进行异常处理,还拥有vtable和一切。这是一个写得很好,足够小的库,可以让你得到一个很好的例子。
答案 5 :(得分:0)
与Dale的方法类似,但是更多的一个步枪是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内部。developer FAQ有更多信息。)