2009 11 Sprytne wskaźniki [Programowanie C C ]


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