using w C#W C++ nie ma mechanizmu typu garbage collector, więc jedyne automatyczne zwalnianie obiektów, jakie w tym języku występuje, dotyczy tych lokalnych - tworzonych na stosie. Dlatego wszelkiego typu pomocnicze obiekty (np. uchwyty do zewnętrznych zasobów, jak pliki) deklaruje się tu zwykle jako właśnie zmienne lokalne.
W innych językach z kolei - dokładniej: w tych, w których GC występuje - praktycznie wszystkie obiekty są tworzone na stercie i zarządzane przez odśmiecacz pamięci. Nie musimy więc martwić się o to, jak i kiedy zostaną one zwolnione.
Ta zaleta staje się jednak wadą w sytuacji, gdy chcielibyśmy jednak móc swoje obiekty niszczyć samodzielnie. Jeśli na przykład mamy do czynienia ze wspominanym już uchwytem do zewnętrznego zasobu, to pewnie życzylibyśmy sobie, by został on zamknięty jednak nieco wcześniej niż na końcu działania programu (w niezbyt dużych aplikacjach zazwyczaj dopiero wtedy włącza się garbage collector). Inaczej będziemy niepotrzebnie zjadać zasoby systemowe.
W C# najlepszym sposobem na ograniczenie czasu życia obiektu jest instrukcja using (w tym kontekście to słowo kluczowe nie znaczy wcale użycia przestrzeni nazw!). Podajemy jej po prostu obiekt, którego chcemy użyć wewnątrz bloku; w zamian mamy zapewnione, że związane z nim zasoby zostaną zwolnione po wyjściu z tego bloku. Prosty przykład wygląda choćby tak:
Czemu jednak samodzielnie nie wywołać tego Close czy innej podobnej metody, która służy do zwolnienia zasobu?... Ano choćby dlatego, że istnieje coś takiego jak wyjątki. O ile powoływanie się na ten fakt w C++ bywa zwykle nadmiarem ostrożności, o tyle w .NET wyjątki latają praktycznie stale i mogą być rzucane przez właściwie każdą instrukcję. Nie można więc pomijać możliwości ich wystąpienia i liczyć na to, że mimo niedbałego kodowania wyciek zasobów jakimś cudem nigdy nam się nie trafi.
Może więc lepiej użyć zwykłego bloku try-finally? Zasadniczo using jest mu równoważny, a ponadto ma jeszcze dodatkowe zalety: automatycznie sprawdza istnienie obiektu przez jego zwolnieniem i ogranicza zasięg zmiennej przechowującej do niego referencję (jeśli deklarujemy ją tak, jak powyżej). Ponadto pozwala też nie wnikać w to, jaką metodę - Close, Disconnect, Release, End, ... - trzeba by wywołać na koniec w bloku finally. Jako że wymagane jest, by obiekt w using implementował interfejs IDisposable, będzie to zawsze metoda Dispose, która zawsze posprząta i pozamyka wszystko co trzeba.
Sprzątanie śmieciPamięcią operacyjną można w programowaniu zarządzać na dwa sposoby. Pierwszy to ręczne tworzenie obiektów i niszczenie ich, gdy nie są już potrzebne. Daje to kontrolę nad czasem ich życia, ale dopuszcza też możliwość powstawania błędów, jak wycieki pamięci czy próby podwójnego jej zwalniania. Aby im zapobiec, każdy obiekt musi mieć ściśle określonego właściciela, odpowiedzialnego za jego zniszczenie.
Drugi sposób to użycie mechanizmu odśmiecania pamięci (garbage collecting), które powinien sam wykrywać "porzucone" obiekty i je zwalniać, kiedy zachodzi ku temu potrzeba. Pozwala to oczywiście przestać martwić się o ich niszczenie. Zwykle nie oznacza to jednak, że wszystkie wyciekające fragmenty pamięci zostaną zwolnione natychmiast. Tracimy więc kontrolę nad czasem życia obiektów.
Nie da się jednak ukryć, że od kiedy komputery mają dość mocy obliczeniowej, aby wyświetlać miękkie cienie pod okienkami, mogą też z powodzeniem zajmować się automatycznym porządkowaniem sterty w swoim wolnym czasie. Dlatego zdecydowana większość nowych języków programowania jest wyposażona w odśmiecacze, które na dodatek są zawsze włączone i zwykle nie da się z nich zrezygnować. Najlepiej byłoby naturalnie mieć tutaj wybór, lecz rzadko jest on nam dany.
Nie inaczej jest w C++, tyle że tutaj mamy chyba jednak tę gorszą opcję - czyli konieczność ręcznego zarządzania alokacją i zwalnianiem. Można aczkolwiek to zmienić, lecz nie odbędzie się to w sposób przezroczysty dla programisty.
Odśmiecanie można przeprowadzić dwiema podstawowymi metodami, które mają naturalnie wiele wariantów. Są to:
W swoim pierwszym (działającym :)) ośmiecaczu dla C++ zastosowałem drugą metodę - oczywiście ze względu na jej prostotę. Jak wiadomo jednak nie jest ona doskonała, gdyż jej piętą achillesową są odwołania cykliczne. Można jej zaradzić na przykład poprzez tak zwane słabe referencje... Ale na szczęście póki co nie potrzebuję jeszcze takich "zakręconych" (dosłownie i przenośni) relacji między obiektami ;P