抽象出每种类型的分配器和释放器。给定类型定义
typedef struct foo
{
int x;
double y;
char *z;
} Foo;
创建分配器函数
Foo *createFoo(int x, double y, char *z)
{
Foo *newFoo = NULL;
char *zcpy = copyStr(z);
if (zcpy)
{
newFoo = malloc(sizeof *newFoo);
if (newFoo)
{
newFoo->x = x;
newFoo->y = y;
newFoo->z = zcpy;
}
}
return newFoo;
}
复制功能
Foo *copyFoo(Foo f)
{
Foo *newFoo = createFoo(f.x, f.y, f.z);
return newFoo;
}
和一个释放函数
void destroyFoo(Foo **f)
{
deleteStr(&((*f)->z));
free(*f);
*f = NULL;
}
请注意,createFoo()
依次调用一个copyStr()
函数,该函数负责为字符串分配内存和复制字符串的内容。另请注意,如果copyStr()
失败并返回 NULL,则newFoo
不会尝试分配内存并返回 NULL。同样,destroyFoo()
在释放结构的其余部分之前,将调用一个函数来删除 z 的内存。最后,destroyFoo()
将 f 的值设置为 NULL。
这里的关键是如果成员元素也需要内存管理,分配器和释放器将责任委托给其他函数。因此,随着您的类型变得越来越复杂,您可以像这样重用这些分配器:
typedef struct bar
{
Foo *f;
Bletch *b;
} Bar;
Bar *createBar(Foo f, Bletch b)
{
Bar *newBar = NULL;
Foo *fcpy = copyFoo(f);
Bletch *bcpy = copyBar(b);
if (fcpy && bcpy)
{
newBar = malloc(sizeof *newBar);
if (newBar)
{
newBar->f = fcpy;
newBar->b = bcpy;
}
}
else
{
free(fcpy);
free(bcpy);
}
return newBar;
}
Bar *copyBar(Bar b)
{
Bar *newBar = createBar(b.f, b.b);
return newBar;
}
void destroyBar(Bar **b)
{
destroyFoo(&((*b)->f));
destroyBletch(&((*b)->b));
free(*b);
*b = NULL;
}
显然,此示例假定成员在其容器之外没有生命周期。情况并非总是如此,您必须相应地设计您的界面。但是,这应该让您了解需要做什么。
这样做可以让您以一致的、明确定义的顺序为对象分配和取消分配内存,这是内存管理战斗的 80%。另外 20% 是确保每个分配器调用都由一个释放器平衡,这是真正困难的部分。
编辑
更改了对delete*
函数的调用,以便我传递正确的类型。