Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
人们使用什么技术/策略在C(而不是C+;+;)中构建对象?_C_Inheritance_Object_C99_C89 - Fatal编程技术网

人们使用什么技术/策略在C(而不是C+;+;)中构建对象?

人们使用什么技术/策略在C(而不是C+;+;)中构建对象?,c,inheritance,object,c99,c89,C,Inheritance,Object,C99,C89,我对从C语言中使用的对象特别感兴趣,而不是像python这样的解释语言的核心对象的实现 基本上,GObject提供了描述不透明值(整数、字符串)和对象的常用方法(通过手动描述接口-作为函数指针的结构,基本上对应于C++中的VTable)-有关该结构的更多信息可以在其 您通常还会手动实现vtables,如“查看实现”中所示。它们不仅使用setjmp/longjmp进行异常处理,还拥有vtables和所有内容。这是一个写得很好、足够小的库,您可以得到一个非常好的示例。我倾向于这样做: struct

我对从C语言中使用的对象特别感兴趣,而不是像python这样的解释语言的核心对象的实现

基本上,GObject提供了描述不透明值(整数、字符串)和对象的常用方法(通过手动描述接口-作为函数指针的结构,基本上对应于C++中的VTable)-有关该结构的更多信息可以在其


您通常还会手动实现vtables,如“查看实现”中所示。它们不仅使用setjmp/longjmp进行异常处理,还拥有vtables和所有内容。这是一个写得很好、足够小的库,您可以得到一个非常好的示例。

我倾向于这样做:

struct foo_ops {
    void (*blah)(struct foo *, ...);
    void (*plugh)(struct foo *, ...);
};
struct foo {
    struct foo_ops *ops;
    /* data fields for foo go here */
};
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;
} 
struct image_scaler {
    //member variables
    int (*scale)(int, int, int*);
}
typedef struct {
    NodeTag n = FOO_NODE;
    /* other members go here */
} FooNode;
通过这些结构定义,实现foo的代码如下所示:

struct foo_ops {
    void (*blah)(struct foo *, ...);
    void (*plugh)(struct foo *, ...);
};
struct foo {
    struct foo_ops *ops;
    /* data fields for foo go here */
};
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;
} 
struct image_scaler {
    //member variables
    int (*scale)(int, int, int*);
}
typedef struct {
    NodeTag n = FOO_NODE;
    /* other members go here */
} FooNode;
然后,在使用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提到的),但我建议确保您确实需要更复杂框架的额外功能。

您对术语“对象”的使用有点模糊,因此我假设您正在问如何使用C来实现面向对象编程的某些方面(请随意更正我的假设。)

方法多态性:

方法多态性通常在C中使用函数指针进行模拟。例如,如果我有一个用于表示图像缩放器的结构(可以获取图像并将其调整为新维度),我可以执行以下操作:

struct foo_ops {
    void (*blah)(struct foo *, ...);
    void (*plugh)(struct foo *, ...);
};
struct foo {
    struct foo_ops *ops;
    /* data fields for foo go here */
};
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;
} 
struct image_scaler {
    //member variables
    int (*scale)(int, int, int*);
}
typedef struct {
    NodeTag n = FOO_NODE;
    /* other members go here */
} FooNode;
然后,我可以制作几个图像缩放器:

struct image_scaler nn, bilinear;
nn->scale = &nearest_neighbor_scale;
bilinear->scale = &bilinear_scale;
这让我可以通过简单地传递一个不同的图像缩放器来实现任何接收图像缩放器并使用其缩放方法的函数的多态行为

继承

继承通常是这样实现的:

struct base{
   int x;
   int y;
} 

struct derived{
   struct base;
   int z;
}

现在,我可以自由地使用派生的额外字段,以及获取base的所有“继承”字段。此外,如果您有一个只接受结构基的函数,您可以简单地将结构基指针转换为结构基指针,而不会产生任何后果

,这与Dale的方法类似,但PostgreSQL更像是一个步兵在内部表示解析树节点、表达式类型等。有默认的
Node
Expr
结构

typedef struct {
    NodeTag n;
} Node;
其中,
NodeTag
是unsigned int的typedef,还有一个头文件,其中包含一组描述所有可能节点类型的常量。节点本身如下所示:

struct foo_ops {
    void (*blah)(struct foo *, ...);
    void (*plugh)(struct foo *, ...);
};
struct foo {
    struct foo_ops *ops;
    /* data fields for foo go here */
};
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;
} 
struct image_scaler {
    //member variables
    int (*scale)(int, int, int*);
}
typedef struct {
    NodeTag n = FOO_NODE;
    /* other members go here */
} FooNode;
FooNode
可以不受惩罚地强制转换为
节点,因为C结构有一个怪癖:如果两个结构具有相同的第一个成员,它们可以相互强制转换

是的,这意味着可以将
FooNode
转换为
BarNode
,这可能是您不想做的。如果您想进行适当的运行时类型检查,GObject是一种方法,尽管在掌握诀窍的同时要准备好憎恨生活


(注意:来自内存的示例,我已经有一段时间没有对Postgres内部进行黑客攻击了。有更多信息。)

从浏览所有答案中可以看出,有库, 函数指针、继承方式、封装等,所有 可用(C++最初是C的前端)

然而,我发现软件的一个非常重要的方面是 可读性。你试过阅读10年前的代码吗?作为一个 因此,我倾向于采取最简单的方法来做类似的事情 C中的对象

询问以下问题:

  • 对于有截止日期的客户(如果是,考虑OOP)?
  • 我可以使用OOP(通常代码更少、开发速度更快、可读性更强)吗
  • 我可以使用库(现有代码、现有模板)吗
  • 我是否受到内存或CPU(例如Arduino)的限制
  • 使用C还有其他技术原因吗
  • 我能保持我的C语言非常简单易读吗
  • 我的项目真正需要什么OOP功能
  • 我通常会回到像GlibAPI这样的东西,它允许我 封装我的代码并提供一个非常可读的接口 如果需要,我会为多态性添加函数指针

    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
    

    尽管我一直在使用我想你可以称之为“基于对象”的C编程风格,但要做到这一点(以及其他一切)要容易得多在C++中,我必须问你为什么不想使用它?@尼尔:如果没有理由,就不会有GOBEKIT。C有很多原因:过时的平台、可保护性、二进制ABI兼容性,你可以说出它。@ EFRAIM我很清楚这些原因,但我问的问题是什么原因。@ Niel -不想注入C。++作为一个依赖项应该是足够的理由。此外,有很多聪明的解决方案……我想在这里看到它们的记录/比较。谢谢Falain(我投了赞成票)。你能指出一个这样做的库吗?这样我或其他任何人都可以检查整个实现?EFraim-你能在你的回答中描述GObject实现策略,以便我们可以将其与给出的其他解决方案进行比较吗?你知道他们是否使用了Dale和Falaina建议的实现?或者其他一些le more dynamic?很久以前(大约1988年),我在一个C编译器上工作,其中解析节点是单个节点类型的位并集,在uni之外有一个类型标记