C 就地与分配API
我需要在C中创建一个库,我想知道如何管理对象:返回已分配的(例如:fopen、opendir)或就地初始化(例如:gnuhcreate\r) 我知道大部分情况下是这样的,我倾向于选择分配API,因为在进行延迟初始化时很方便(通过测试对象指针是否为NULL) 然而,在阅读之后,我想知道这种设计是否会导致引用的局部性问题,特别是当我从其他对象合成对象时:C 就地与分配API,c,api,C,Api,我需要在C中创建一个库,我想知道如何管理对象:返回已分配的(例如:fopen、opendir)或就地初始化(例如:gnuhcreate\r) 我知道大部分情况下是这样的,我倾向于选择分配API,因为在进行延迟初始化时很方便(通过测试对象指针是否为NULL) 然而,在阅读之后,我想知道这种设计是否会导致引用的局部性问题,特别是当我从其他对象合成对象时: struct opaque_composite { struct objectx *member1; struct objecty
struct opaque_composite {
struct objectx *member1;
struct objecty *member2;
struct objectz *member2;
/* ... */
};
此类对象的分配将产生其他子分配的级联。这在实践中是一个问题吗?还有其他问题我应该知道吗?
要考虑的是函数构造的对象的类型是否是不透明的。不透明类型仅在头文件中进行正向声明,您可以使用它做的唯一事情是使用指向它的指针并将该指针传递给单独编译的API函数<标准库中的代码>文件就是这样一种不透明类型。对于不透明类型,您没有选择,但必须提供分配和解除分配函数,因为用户没有其他方法获得对该类型对象的引用
如果类型不是不透明的–也就是说,结构的定义在头文件中–更通用的方法是使用一个只进行初始化的函数,如果需要的话,使用另一个进行终结的函数,但不进行分配和释放。原因是,使用此界面,用户可以决定是否将对象放在堆栈上
struct widget w;
widget_init(&w, 42, "lorem ipsum");
// use widget…
widget_fini(&w);
…或者在堆上
struct widget * wp = malloc(sizeof(struct widget));
if (wp == NULL)
exit(1); // or do whatever
widget_init(wp, 42, "lorem ipsum");
// use widget…
widget_fini(wp);
free(wp);
如果你认为这太多了,你——或者你的用户自己——可以很容易地提供方便的功能
inline struct widget *
new_widget(const int n, const char *const s)
{
struct widget wp = malloc(sizeof(struct widget));
if (wp != NULL)
widget_init(wp, n, s);
return wp;
}
inline void
del_widget(struct widget * wp)
{
widget_fini(wp);
free(wp);
}
反过来是不可能的
接口应始终提供组成更高级别抽象的基本构建块,但不能因为过于严格而使合法使用变得不可能
当然,这给我们留下了一个问题:什么时候使类型不透明。一个很好的经验法则——我第一次在Linux内核中看到这一点——可能是只有在没有用户可以有意义地访问的数据成员时,才使类型不透明。我认为应该对这个规则进行一些改进,以考虑到非不透明类型允许在头文件中以inline
版本提供“member”函数,这从性能角度来看可能是可取的。另一方面,不透明类型提供了更好的封装(特别是因为C无法限制对结构
成员的访问)。如果使不透明类型不不透明会迫使我将
#头文件包含到库的头文件中,因为它们提供了用作类型中成员的类型的定义,那么我也会更容易倾向于不透明类型。(对于uint32\u t
,我可以使用\include
。\include
包含这样的大标题,我不太容易。
包含来自第三方库的标题,例如
)在“子分配级联”中如果您保持对象不透明,以便使其处于一致状态,则不会出现问题。创建和销毁例程在创建过程中会增加一些处理分配失败的复杂性,但不会太繁重
除了选择静态/堆栈分配副本(我通常不喜欢),在我看来,以下方案的主要优点是:
x = initThang(thangPtr);
是返回各种更具体的错误代码的容易程度