Wyróżnij alokatory i deallocatory dla każdego typu. Biorąc pod uwagę definicję typu
typedef struct foo
{
int x;
double y;
char *z;
} Foo;
utworzyć funkcję przydzielania
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;
}
funkcję kopiowania
Foo *copyFoo(Foo f)
{
Foo *newFoo = createFoo(f.x, f.y, f.z);
return newFoo;
}
i funkcję dealokator
void destroyFoo(Foo **f)
{
deleteStr(&((*f)->z));
free(*f);
*f = NULL;
}
pamiętać, że createFoo()
z kolei nazywa copyStr()
funkcja odpowiedzialna za przydzielanie pamięci i kopiowanie zawartości ciągu. Zauważ również, że jeśli copyStr()
zawiedzie i zwróci wartość NULL, to newFoo
nie będzie próbował przydzielić pamięci i zwróci wartość NULL. Podobnie, destroyFoo()
wywoła funkcję do usunięcia pamięci dla z przed zwolnieniem reszty struktury. Wreszcie, destroyFoo()
ustawia wartość f na NULL.
Kluczem jest to, że alokator i deallocator przekazują odpowiedzialność innym funkcjom, jeśli elementy członkowskie również wymagają zarządzania pamięcią. Tak, jak twoje typy uzyskać bardziej skomplikowane, można ponownie użyć tych podzielników tak:
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;
}
Oczywiście przykład ten zakłada, że członkowie nie mają życia poza ich pojemników. Nie zawsze tak jest i musisz odpowiednio zaprojektować interfejs. Jednak powinno to dać ci poczucie, co należy zrobić.
Dzięki temu można przydzielić i zwolnić pamięć dla obiektów w spójnej, ściśle określonej kolejności, co stanowi 80% bitwy w zarządzaniu pamięcią. Pozostałe 20% upewnia się, że każde wywołanie przydziału jest zrównoważone przez dealera, który jest naprawdę trudny.
edit
Zmieniono wywołań funkcji delete*
tak że ja przechodząc odpowiednie typy.
Ten długi czas kodera C przełączono na C++, a problem zniknął. –
@Neil Butterworth: Albo to ((częściej) problem stał się znacznie gorszy. – sharptooth
@sharptooth: Nie jestem fanem C++, ale zajmuje się całą klasą problemów związanych z zarządzaniem pamięcią. Oczywiście pozostawiając naprawdę trudne. :-) – JesperE