对结构

时间:2015-12-31 07:40:18

标签: 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, ...);

如上所述,目前尚不清楚提供给MyFoo的{​​{1}}是否应该使用do_something进行初始化。由于它不是create_foo,我认为应该传入foo(即为foo分配的空间),它将以某种方式进行修改,然后用户可以使用修改后的foo。但是,我如何最有效地表明foo是否应该“创建”?一种常见的模式是做类似

的事情
const

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

另一个选择是让do_something包含一个“created”标志,以便函数可以拒绝任何尚未创建的foos,但这似乎是我过度设计的东西。

我只是想确定我没有错过处理此问题的常用惯例或标准方法。也许我太强迫OOP了。赞赏的想法和评论!

修改:有人建议,我同意,MyFoo更好的名称是create_foo

1 个答案:

答案 0 :(得分:2)

是的,你强制 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);

如果您希望可以使用模块化的opaque类型,以便更好地控制您的分配,但最终它不会确保用户不会作弊:

// 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; }