W poprzednim wpisie został wyjaśniony mechanizm wychodzenia z funkcji (Leave) oraz sposoby jego przechwytywania. Teraz należy zwrócić uwagę co się dzieje ze wszystkimi wskaźnikami, które zostały zainicjowane przed takim wyjściem – otóż jeżeli zostanie on usunięty, natomiast miejsce w pamięci nadal jest zajęte – tracimy bezpowrotnie taki obszar – następuje wyciek pamięci.

Co w takim wypadku możemy zrobić? Możemy, a raczej powinniśmy zastosować CleanupStack.

CleanupStack

CleanupStack przydaje się w sytuacjach, gdy po zaalokowaniu pamięci na dany obiekt, funkcja może wywołać Leave nie dochodząc do momentu w którym uprzednio stworzony obiekt jest poprawnie usuwany.

void Klasa::FunkcjaL()
{
TInt * i = new (ELeave) TInt;
delete i;
}

Klasa * w = new (ELeave) Klasa;
w->FunkcjaL();
delete w;

W powyższym przykładzie, funkcja FunkcjaL() alokuje pamięć, ale może także wyjść (wywołać Leave). Warto zauważyć, że ten kawałek kodu może się posypać w dwóch miejscach – po pierwsze przy alokacji obiektu klasy Klasa może coś pójść nie tak, ale w takim przypadku nic złego dla pamięci się nie dzieje. Po drugie w przypadku alokacji obiektu TInt w funkcji FunkcjaL() – jeśli funkcja wyjdzie to wskaźnik na obiekt Klasa może nigdy nie zostać usunięty z pamięci – następuje wyciek.

Bardzo prostym rozwiązaniem jest użycie w tym przypadku CleanupStack. Wszystkie wskaźniki obiektów, które chcemy zabezpieczyć muszą wylądować na tym stosie.

Klasa * w = new (ELeave) Klasa;
CleanupStack::PushL(w);
w->FunkcjaL();
CleanupStack:PopAndDestroy(w);

W takim przypadku, od razu po alokacji pamięci dla obiektu w następuje zachowanie tego wskaźnika na CleanupStack. W momencie, gdy FunkcjaL() zakończy swe działanie nie powodując żadnego błędu, funkcja CleanupStack::PopAndDestroy(w) zdejmuje ten wskaźnik od razu go niszcząc. Natomiast jeżeli FunkcjaL() wyjdzie, wtedy jako część procesu czyszczenia wszystkie obiekty znajdujące się na CleanupStack są poprawnie usuwane.

Istnieje kilka wariacji funkcji zdejmujących z CleanupStack:

  • CleanupStack:Pop() – zdejmuje ze stosu ostatnio położony wskaźnik
  • CleanupStack:Pop(2) – zdejmuje ze stosu ostatnie dwa położone wskaźniki
  • CleanupStack:Pop(wsk) – zdejmuje ze stosu i sprawdza czy jest to wsk

Istnieją także funkcje specjalnie dla klas R: CleanupClosePushL() – przy czyszczeniu wywołuje Close() dla obiektu, CleanupReleasePushL()Release(), CleanupDeletePushL()Delete().

Kilka ważnych uwag: nie należy odkładać zmiennych składowych na CleanupStack (zmienne rozpoczynające się od i – łatwo stwierdzić); funkcje kończące się literką C odkładają na stos. Nie należy nadużywać tego mechanizmu i kłaść na CleanupStack tylko te wskaźniki, w których pomiędzy stworzeniem a usunięciem może wystąpić Leave.

Również jak i w poprzedniej części odsyłam do postu na polskim forum Symbiana, który tłumaczy ten mechanizm: CleanupStack – czyli sprzątamy po sobie.