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.