C中vtable的独立结构

C中vtable的独立结构,c,oop,struct,interface,C,Oop,Struct,Interface,在C中执行OOP的自然方法是执行以下操作: #include <stddef.h> /* stream operations */ struct _src { /** open stream. Return true on success */ bool (*open)(struct _src *src, const char *fspec); /** close stream. Return true on success */ bool (*close)(stru

在C中执行OOP的自然方法是执行以下操作:

#include <stddef.h>
/* stream operations */
struct _src {
  /** open stream. Return true on success */
  bool (*open)(struct _src *src, const char *fspec);
  /** close stream. Return true on success */
  bool (*close)(struct _src *src);
  /** Returns the actual size of buffer read (<=len), or 0 upon error */
  size_t (*read)(struct _src *src, void *buf, size_t len);
  /* hold the actual FILE* in the case of file stream */
  void *data;
};
#include <stddef.h>
struct _src {
  /* vtable */
  const struct _src_ops *ops;
  /* hold the actual FILE* in the case of file stream */
  void *data;
};

/* stream operations */
struct _src_ops {
  /** open stream. Return true on success */
  bool (*open)(struct _src *src, const char *fspec);
  /** close stream. Return true on success */
  bool (*close)(struct _src *src);
  /** Returns the actual size of buffer read (<=len), or 0 upon error */
  size_t (*read)(struct _src *src, void *buf, size_t len);
};
#包括
/*流操作*/
结构{
/**打开流。成功返回true*/
bool(*open)(struct _src*src,const char*fspec);
/**关闭流。成功时返回true*/
bool(*close)(struct _src*src);

/**返回缓冲区读取的实际大小(似乎有两个好处:

  • const
    限定符阻止用户有意或无意地重新定向函数指针。这将使函数指针本身成为只读的
  • 拥有指针允许函数成为单独的“单例”实例,因为这些函数对于每个结构对象都是相同的,只需要分配一次

然而,使用函数指针执行OOP是相当粗糙的,并且大多数情况下在您绝对确定需要多态性的情况下才有意义。没有
这个
指针,因此您最终会遇到笨拙的语法,例如
foo.func(&foo,…)
。值得注意的是,可以编写“对象点成员”是一种语言语法,与OO设计本身无关

总的来说,花费大量精力来实现继承/多态性这一罕见的、专门的OO概念,同时却忽略了私有封装这一更重要的OO概念,这是很奇怪的


根据我的经验,最好只使用普通函数,但实现不透明类型并将实例作为参数传递给结构。在实际需要时处理继承。

似乎有两个好处:

  • const
    限定符阻止用户有意或无意地重新定向函数指针。这将使函数指针本身成为只读的
  • 拥有指针允许函数成为单独的“单例”实例,因为这些函数对于每个结构对象都是相同的,只需要分配一次

然而,使用函数指针执行OOP是相当粗糙的,并且大多数情况下在您绝对确定需要多态性的情况下才有意义。没有
这个
指针,因此您最终会遇到笨拙的语法,例如
foo.func(&foo,…)
。值得注意的是,可以编写“对象点成员”是一种语言语法,与OO设计本身无关

总的来说,花费大量精力来实现继承/多态性这一罕见的、专门的OO概念,同时却忽略了私有封装这一更重要的OO概念,这是很奇怪的


根据我的经验,最好只使用普通函数,但实现不透明类型并将实例作为参数传递给结构。在实际需要时处理继承。

您可能需要考虑vtable的两种“样式”,即基于原型的继承与基于类的继承

第一种方法类似于基于JavaScript原型的继承。通过修改实例vtable,可以更轻松地更改单个实例的行为。更改某个“类”的所有对象的行为并不简单

第二种方法类似于传统的C++(和java)继承。它使改变某类的所有对象的行为变得更容易(通过修改共享VTABLE),但是它使单个实例的行为稍微复杂一些:在这种情况下,需要创建一个新的VTHT表。(从原始vtable复制)、修改并指定给特定实例

根据您的应用程序,您可以选择正确的实现

假设没有必要将行为分配给特定实例,并且不需要动态修改类行为(在初始创建之后),考虑以下因素来做出决定:

  • 共享vtable的每次调用成本略高(因为需要访问vtable而不是指针)
  • 共享vtable将降低内存利用率(不需要每个实例vtable)
  • 共享vtable将导致更快的对象创建(无需为每个实例初始化vtable)

更多信息请参见:

您可能想从基于原型的继承与基于类的继承的角度来考虑vtable的两种“样式”

第一种方法类似于基于JavaScript原型的继承。通过修改实例vtable,可以更轻松地更改单个实例的行为。更改某个“类”的所有对象的行为并不简单

第二种方法类似于传统的C++(和java)继承。它使改变某类的所有对象的行为变得更容易(通过修改共享VTABLE),但是它使单个实例的行为稍微复杂一些:在这种情况下,需要创建一个新的VTHT表。(从原始vtable复制)、修改并指定给特定实例

根据您的应用程序,您可以选择正确的实现

假设没有必要将行为分配给特定实例,并且不需要动态修改类行为(在初始创建之后),考虑以下因素来做出决定:

  • 共享vtable的每次调用成本略高(因为需要访问vtable而不是指针)
  • 共享vtable将降低内存利用率(不需要每个实例vtable)
  • 共享vtable将导致更快的对象创建(无需为每个实例初始化vtable)

更多信息请参见:

可能还有其他“对象”支持相同类型的操作(它们具有相同或相似的语义)?然后它们可以共享operations-structure。您只需要一个
\u src\u ops
对象来处理所有
\u src