对结构进行操作的函数的约定

标签 c oop design-patterns

假设我在 foo.h 中有以下内容:

typedef struct {
    char* my_buf;
    int my_int;
} MyFoo;

/* Creates a foo, allocating memory for my_buf and initializing my_int */
void create_foo(MyFoo *foo);
/* Destroys a foo, freeing the memory for my_buf */
void free_foo(MyFoo *foo);

现在在另一个模块中,bar.h 假设我有以下内容:

#include "foo.h"

/* Do something with a foo */
void do_something(MyFoo *foo, SomeType_t otherType, ...);

如所写,尚不清楚提供给do_somethingMyFoo 是否应该使用create_foo 进行初始化。因为它不是const,我认为应该传入一个foo(即为foo分配的空间)是相当明确的,它会以某种方式进行修改,然后用户可以使用修改后的foo .但是我如何最有效地表明 foo 是否应该被“创建”?一个常见的模式是做类似的事情

MyFoo stackFoo;
do_something(&stackFoo, ...);

但是,如果 do_something 创建了 foo,用户必须知道他们有责任释放 foo。另一方面,没有办法强制创建 foo;我只是要求一个指针(我承认这是一个弱的反驳论点,因为它适用于 C 中的很多事情)。

另一种选择是让 MyFoo 包含一个“已创建”标志,以便该函数可以拒绝任何尚未创建的 foo,但这似乎是我过度设计的东西.

我只是想确保我没有遗漏处理此问题的通用约定或标准方法。也许我太强制 OOP 了。想法和评论表示赞赏!

编辑:有人建议,我同意,create_foo 的更好名称是 init_foo

最佳答案

是的,您强制 OOP,这没有错,但 C 中的 OOP 是一个有序性问题。不管怎样,您不能确保 foo 没有在堆栈上创建。

对于:

void do_something(MyFoo *foo, SomeType_t otherType, ...);

很明显,现有的 foo 将被传递,因为参数是按值传递的。因此,语义很明确:必须传递现有的 foo

对于:

void create_foo(MyFoo *foo);

有些地方不对劲,因为参数是按值传递的,或者这不应该称为 create_foo,而是 init_foo,或者您应该将参数作为指针的地址传递。对于类似 OOP 的编程,您应该区分分配和初始化。

void init_foo(MyFoo *);

MyFoo foo; // simple creation
init_foo(&foo); // initialization

void init_foo(MyFoo *);
MyFoo *alloc_foo();

MyFoo *pfoo = alloc_foo(); // allocation factory
init_foo(pfoo); // initialization
// or with an idiom like
MyFoo *pfoo;
init_foo(pfoo=alloc_foo());

或:

MyFoo *init_foo(MyFoo *);
MyFoo *alloc_foo();

MyFoo *pfoo = init_foo(alloc_foo());

现在您还可以调用一个方便的函数:

MyFoo *create_foo(); // alloc+initialization factory
MyFoo *pfoo = create_foo();

或使用传递指针成语:

void create_foo(MyFoo **f);
MyFoo *pfoo;
create_foo(&pfoo);

如果你愿意,你可以使用模块化的不透明类型,以更好地控制你的分配,但最后它不能确保用户没有作弊:

// Module foo.c
struct internal_foo {
  int m;
};
void *create_foo() {
  struct internal_foo *pfoo = malloc(...);
  // init structure
  return pfoo;
}
void delete_foo(void *pf) {
  free(pf);
}
// convenient functions for members
void set_m_foo(void *pf,int v) { ((struct internal_foo *)pf)->m = v; }
int get_m_foo(void *pf) { return ((struct internal_foo *)pf)->m; }

因此从外部点,用户只能看到一个通用指针。如果你想确保更好的输入,那么你也可以将通用指针封装到一个虚拟结构中:struct MyFoo { void *opaque_foo;

关于对结构进行操作的函数的约定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34542942/

相关文章:

c - 使用 fgets 读取文本文件时出现奇怪的符号

c++ - 实例变量值无法正确打印

java - 如何在Android中使用ormlite实现单例模式

javascript - 在 Backbone.js 的模型和 View 上设置属性的模式是什么?

数组可以称为 const 指针吗?

c - C中的递归。使非递归函数成为递归函数

c++ - 重负载下 C/C++ 中的内存泄漏检查

javascript - 基于原型(prototype)与基于类的继承

c++ - 如何成功创建一个创建对象并返回指向该对象的指针的函数?

node.js - 避免 Node.js Web 应用程序中的竞争条件