Programowanie C++ Sprytne wskazniki Automatyczne niszczenie obiektów utworzonych na stercie w C++ Programista używając C++ musi dbać o zwalnianie obiektów dynamicznych (utworzonych na stercie). Zadanie to można automatyzować, wykorzystując obiekty pośredniczące, tak zwane sprytne wskazniki. Narzuty czasowe i pamięciowe tego rozwiązania są pomijalne w większości zastosowań. sie kompilacji, tworząc odpowiednie klasy, nie Dowiesz się: Powinieneś wiedzieć: dodając żadnych narzutów pamięciowych i cza- " Jak zarządzać obiektami utworzonymi na " Jak pisać proste programy w C++; sowych (kod generowany nie różni się od kodu stercie; " Co to są obiekty dynamiczne (utworzone na tworzonego ręcznie), jedyną niedogodnością jest " Co to są sprytne wskazniki; stercie). dłuższy czas kompilacji. " Jak automatycznie zwalniać obiekty, gdy Sprytnym wskaznikiem jest klasa boost:: występują cykliczne zależności. scoped_ptr przedstawiona na Listingu 1. Kon- struktor kopiujący oraz operator przypisania jest zabroniony (prywatny), ponieważ utwo- sprytny wskaznik usuwa zarządzany obiekt rzenie więcej niż jednego wskaznika tego ty- w destruktorze, technika ta jest nazywana pu przechowującego ten sam obiekt prowadzi Poziom zdobywanie zasobów jest inicjowaniem. Jeże- do błędu, polegającego na próbie powtórnego trudności li obiekt jest wskazywany przez wiele spryt- zwolnienia obiektu dynamicznego. nych wskazników, to powinien być usunięty Sprytny wskaznik, oprócz usuwana obiek- w destruktorze ostatniego z nich. tów po wyjściu z bloku, ułatwia zarządzanie obiektami dynamicznymi, które są składowy- biekty utworzone na stercie (za po- Sprytne wskazniki będące wyłącz- mi klasy, ponieważ nie trzeba ich jawnie zwal- mocą operatora new) powinny być nymi posiadaczami obiektów niać w destruktorze (patrz Listing 2). Dodat- Ozwalniane przez programistę wte- Biblioteka standardowa C++ oraz biblioteki kową zaletą takiego rozwiązania jest poprawne dy, kiedy nie są już używane (operator delete). boost dostarczają różnych rodzajów sprytnych zwalnianie zasobów, gdy są zgłaszane wyjątki. Brak restrykcyjnej kontroli dotyczącej czasu ży- wskazników. Są to szablony, ponieważ potrze- Jeżeli konstruktor klasy Audio (Listing 2) zgło- cia tych obiektów powoduje wycieki pamię- bujemy wskazników zarządzających różnymi si wyjątek, to sprytny wskaznik image_ sprawi, ci, co oznacza, że aplikacja zajmuje coraz wię- typami obiektów. Szablony generują kod w cza- że obiekt Image zostanie skasowany. cej pamięci operacyjnej, kończąc działanie błę- dem, kiedy pamięć ta zostanie wyczerpana. Po- licznik sprytny wskaznik niższy tekst pokazuje wykorzystanie mechani- licznik sprytny wskaznik zmów związanych z wołaniem konstruktorów i destruktorów do automatycznego zwalniania obiekt obiektów tworzonych na stercie, co zwalnia pro- obiekt gramistę z tego zadania. Dostęp do obiektów utworzonych na ster- cie jest realizowany przez wskaznik, jeżeli taki obiekt nie jest wskazywany, to nie mo- Rysunek 1. Sprytne wskazniki ze zliczaniem odniesień współdzielą wskaznik do obiektu oraz licznik. Obiekt sprytnego wskaznika może zawierać jeden lub dwa wskazniki że być używany, więc można go zwolnić. Sprytne wskazniki pośredniczą przy dostę- pie do obiektów dynamicznych (wskaznik Szybki start jest przekazywany w konstruktorze) bada- Aby uruchomić przedstawione przykłady, należy mieć dostęp do dowolnego kompilatora jąc, czy obiekt jest wskazywany i jeżeli nie, C++ oraz edytora tekstu. Większość przykładów korzysta z udogodnień dostarczanych przez biblioteki boost, warunkiem ich uruchomienia jest instalacja tych bibliotek (w wersji 1.36 lub usuwając go. Zakładając, że obiekt dyna- nowszej) oraz wykorzystywanie kompilatora oficjalnie przez nie wspieranego, którymi są: miczny może być wskazywany tylko przez je- msvc 7.1 lub nowszy, gcc g++ 3.4 lub nowszy, Intell C++ 8.1 lub nowszy, Sun Studio 12 lub den sprytny wskaznik, to obiekt ten możemy Darvin/GNU C++ 4.x. Na wydrukach pominięto dołączanie odpowiednich nagłówków oraz usunąć w momencie niszczenia sprytnego udostępnianie przestrzeni nazw, pełne zródła dołączono jako materiały pomocnicze. wskaznika. W tym najprostszym przypadku 40 11/2009 Sprytne wskazniki Biblioteka standardowa C++ zawiera sza- Listing 1. Wybrane metody szablonu scoped_ptr blon std::auto_ptr, który jest innym ty- pem sprytnego wskaznika będącego wyłącz- template class scoped_ptr { nym posiadaczem obiektu. Ma on nietypo- public: wą implementację konstruktora kopiującego explicit scoped_ptr(T* p = 0) : p_(p) {} i operatora przypisania, operacje te są przeka- ~scoped_ptr(){ delete p_; } //usuwa wskazywany obiekt zywaniem własności, a nie tworzeniem kopii T& operator*() { return *p_; } //dostęp do zarządzanego obiektu (patrz Listing 3). T* operator->() { return p_; } //dostęp do zarządzanego obiektu Wskaznik ten możemy wykorzystać //także inne metody, np. porównywanie wskazników (oprócz automatycznego kasowania obiek- private: tu przy usuwaniu wskaznika) do zwracania T* p_; //Wskaznik, którym zarządza obiektu utworzonego na stercie, unikając scoped_ptr(scoped_ptr const &); //zabroniony konstruktor kopiujący wycieków pamięci. Listing 4 zawiera funk- scoped_ptr & operator=(scoped_ptr const &); //zabronione przypisanie cję createFoo, która ilustruje tę technikę. Je- }; żeli będziemy ignorowali wartość zwracaną, Listing 2. Składowe przechowywane jako sprytne wskazniki upraszczają kod to destruktor obiektu tymczasowego zwolni obiekt, jeżeli wartość zwracana zostanie przy- class Book { //przykładowa klasa książki, która agreguje obrazek oraz nagranie pisana do innego obiektu, to obiekt tymcza- public: sowy będzie pusty. Book(const string& name, const string& image_name, const string& audio_name) Nietypowa implementacja konstruktora : name_(name), kopiującego i operatora przypisania (przeka- image_(new Image(image_name) ), zywanie własności, a nie tworzenie kopii) za- audio_(new Audio(audio_name) ) pobiega tworzeniu więcej niż jednego wskaz- {} nika auto_ptr zarządzającego tym samym ~Book() {} //destruktor może być pusty obiektem. Brak możliwości tworzenia kopii private: ogranicza możliwość stosowania tych wskaz- string name_; ników, na przykład obiekty auto_ptr nie scoped_ptr image_; powinny być przechowywane w kolekcjach scoped_ptr