You are on page 1of 48

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Wicej ni C++.
Wprowadzenie do
bibliotek Boost
Autor: Bjrn Karlsson
Tumaczenie: Przemysaw Szeremiota
ISBN: 83-246-0339-5
Tytu oryginau: Beyond the C++ Standard Library:
An Introduction to Boost
Format: B5, stron: 384

Jzyk C++ znajduje coraz wicej zastosowa, w wypadku ktrych biblioteka


standardowa czsto okazuje si zbyt uboga. Projekt Boost powsta w celu wypenienia
luk i wyeliminowania niedoskonaoci biblioteki STL. Dzi biblioteki Boost zyskuj
coraz wiksz popularno, czego dowodem jest wczenie dziesiciu z nich do
przygotowywanej biblioteki standardowej jzyka C++0x. Twrcy kolejnej specyfikacji
C++ zdecydowali si nawet na kilka modyfikacji jzyka w celu uatwienia korzystania
z bibliotek Boost.
Ksika Wicej ni C++. Wprowadzenie do bibliotek Boost to przegld 58 bibliotek
projektu. Dwanacie z nich omwiono szczegowo i zilustrowano przykadami.
Analizujc zaprezentowane projekty, przekonasz si, jak bardzo biblioteki Boost
uatwiaj prac i pozwalaj ulepszy aplikacje. Nauczysz si korzysta z inteligentnych
wskanikw, obiektw funkcyjnych, wyrae regularnych i wielu innych funkcji
oferowanych przez biblioteki Boost.
Bezpieczna konwersja typw
Stosowanie elastycznych bibliotek kontenerw
Wyraenia regularne
Wywoania zwrotne
Zarzdzanie sygnaami i slotami
Wykorzystaj ju teraz elementy bibliotek Boost, a nowa biblioteka standardowa nie
bdzie miaa przed Tob adnych tajemnic.

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Sowo wstpne ................................................................................. 9


Od autora ....................................................................................... 11
Podzikowania ................................................................................ 13
O autorze ....................................................................................... 15
Organizacja materiau ..................................................................... 17
Przegld bibliotek Boost ................................................................. 19
Przetwarzanie tekstw i cigw znakw ..........................................................................19
Struktury danych, kontenery, iteratory i algorytmy .........................................................21
Obiekty funkcyjne i programowanie wyszego rzdu .....................................................24
Programowanie uoglnione i metaprogramowanie z uyciem szablonw ......................26
Liczby i obliczenia ...........................................................................................................29
Wejcie-wyjcie ...............................................................................................................31
Rne ................................................................................................................................32

Cz I

Biblioteki oglnego przeznaczenia ..................................37

Rozdzia 1. Biblioteka Smart_ptr ....................................................................... 39


Jak ulepszy wasne programy z uyciem biblioteki Smart_ptr? ....................................39
Po co nam inteligentne wskaniki? ..................................................................................40
Jak ma si biblioteka Smart_ptr do biblioteki standardowej C++? ..................................41
scoped_ptr ........................................................................................................................42
scoped_array .....................................................................................................................50
shared_ptr .........................................................................................................................51
shared_array .....................................................................................................................63
intrusive_ptr .....................................................................................................................63
weak_ptr ...........................................................................................................................74
Smart_ptr podsumowanie ............................................................................................82

Rozdzia 2. Biblioteka Conversion ..................................................................... 83


Jak ulepszy wasne programy z uyciem biblioteki Conversion? ..................................83
polymorphic_cast .............................................................................................................84
polymorphic_downcast ....................................................................................................90
numeric_cast .....................................................................................................................93
lexical_cast .....................................................................................................................100
Conversion podsumowanie .......................................................................................105

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07


druk\!!Spis.doc
5

Spis treci

Rozdzia 3. Biblioteka Utility ........................................................................... 107


Jak ulepszy wasne programy z uyciem biblioteki Utility? ........................................107
BOOST_STATIC_ASSERT ..........................................................................................108
checked_delete ...............................................................................................................110
noncopyable ...................................................................................................................114
addressof .........................................................................................................................119
enable_if .........................................................................................................................121
Utility podsumowanie ...............................................................................................129

Rozdzia 4. Biblioteka Operators ..................................................................... 131


Jak ulepszy wasne programy z uyciem biblioteki Operators? ...................................131
Jak ma si biblioteka Operators do biblioteki standardowej C++? ................................132
Operators ........................................................................................................................132
Stosowanie .....................................................................................................................137
Operators podsumowanie ..........................................................................................156

Rozdzia 5. Biblioteka Regex .......................................................................... 157


Jak ulepszy wasne programy z uyciem biblioteki Regex? ........................................157
Jak ma si biblioteka Regex do biblioteki standardowej C++? .....................................158
Regex ..............................................................................................................................158
Stosowanie ..................................................................................................................... 160
Regex podsumowanie ...............................................................................................174

Cz II

Kontenery i struktury danych .......................................175

Rozdzia 6. Biblioteka Any .............................................................................. 177


Jak ulepszy wasne programy z uyciem biblioteki Any? ............................................177
Jak ma si biblioteka Any do biblioteki standardowej C++? .........................................178
Any .................................................................................................................................178
Stosowanie .....................................................................................................................181
Any podsumowanie ...................................................................................................203

Rozdzia 7. Biblioteka Variant ......................................................................... 205


Jak ulepszy wasne programy z uyciem biblioteki Variant? ......................................205
Jak ma si biblioteka Variant do biblioteki standardowej C++? ....................................206
Variant ............................................................................................................................206
Stosowanie .....................................................................................................................209
Variant podsumowanie ..............................................................................................219

Rozdzia 8. Biblioteka Tuple ........................................................................... 221


Jak ulepszy wasne programy z uyciem biblioteki Tuple? .........................................221
Jak ma si biblioteka Tuple do biblioteki standardowej C++? ......................................222
Tuple ...............................................................................................................................222
Stosowanie .....................................................................................................................227
Tuple podsumowanie ................................................................................................243

Cz III Obiekty funkcyjne i programowanie wyszego rzdu .....245


Rozdzia 9. Biblioteka Bind ............................................................................. 247
Jak ulepszy wasne programy z uyciem biblioteki Bind? ...........................................247
Jak ma si biblioteka Bind do biblioteki standardowej C++? ........................................248
Bind ................................................................................................................................248
Stosowanie .....................................................................................................................249
Bind podsumowanie ..................................................................................................273

6 D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\!!Spis.doc

Spis treci

Rozdzia 10. Biblioteka Lambda ........................................................................ 275


Jak ulepszy wasne programy z uyciem biblioteki Lambda? .....................................275
Jak ma si biblioteka Lambda do biblioteki standardowej jzyka C++? .......................276
Lambda ...........................................................................................................................277
Stosowanie .....................................................................................................................278
Lambda podsumowanie .............................................................................................312

Rozdzia 11. Biblioteka Function ....................................................................... 313


Jak ulepszy wasne programy z uyciem biblioteki Function? ....................................313
Jak ma si biblioteka Function do biblioteki standardowej jzyka C++? ......................313
Function ..........................................................................................................................314
Stosowanie .....................................................................................................................317
Function podsumowanie ...........................................................................................337

Rozdzia 12. Biblioteka Signals ......................................................................... 339


Jak ulepszy wasne programy z uyciem biblioteki Signals? .......................................339
Jak ma si biblioteka Signals do biblioteki standardowej jzyka C++? ........................340
Signals ............................................................................................................................340
Stosowanie .....................................................................................................................343
Signals podsumowanie ..............................................................................................365

Bibliografia ................................................................................... 367


Skorowidz ..................................................................................... 371

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07


druk\!!Spis.doc
7

Rozdzia 1.

Jak ulepszy wasne programy


z uyciem biblioteki Smart_ptr?
t Poprzez automatyczne zarzdzanie czasem ycia obiektw za pomoc
szablonu shared_ptr, bezpiecznie i efektywnie zarzdzajcego wsplnymi

zasobami.
t Poprzez bezpieczne podgldanie zasobw wsplnych za pomoc szablonu
weak_ptr, co eliminuje ryzyko charakterystyczne dla wiszcych wskanikw.
t Poprzez osadzanie zasobw w zasigach programu za pomoc szablonw
scoped_ptr i scoped_array, uatwiajcych konserwacj kodu i pomocnych

przy zabezpieczaniu przed zgubnym wpywem wyjtkw.


Wskaniki inteligentne, implementowane w bibliotece Smart_ptr, rozwizuj odwieczny problem zarzdzania czasem ycia zasobw (chodzi zwykle o zasoby przydzielane
dynamicznie1). Inteligentne wskaniki dostpne s w wielu odmianach. Wszystkie
jednak maj jedn cech wspln: automatyzacj zarzdzania zasobami. w automatyzm manifestuje si rozmaicie, na przykad poprzez kontrol czasu ycia obiektw przydzielanych dynamicznie czy te poprzez kontrol nad akwizycj i zwalnianiem zasobw (plikw, pocze sieciowych itp.). Wskaniki inteligentne z biblioteki
Boost implementuj pierwsze z tych zastosowa, to jest przechowuj wskaniki do
dynamicznie przydzielanych obiektw, dbajc o ich zwalnianie w odpowiednich momentach. Mona si zastanawia, czy to nie zbytnie ograniczenie ich zada. Czy nie
mona by zaimplementowa rwnie pozostaych aspektw zarzdzania zasobami?
C, mona by, ale nie za darmo. Rozwizania oglne wymagaj czsto wikszej zoonoci, a przy inteligentnych wskanikach biblioteki Boost gwny nacisk pooono
nawet nie tyle na elastyczno, co na wydajno. Ale dziki moliwoci implementowania
1

Czyli wszelkie zasoby, do ktrych mona si odwoywa za porednictwem typu wskanikowego


rwnie inteligentnego przyp. aut.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
37

Cz I Biblioteki oglnego przeznaczenia

38

wasnych mechanizmw zwalniania najbardziej inteligentne ze wskanikw z rodziny


Boost (boost::shared_ptr) mog obsugiwa zasoby wymagajce przy zwalnianiu
bardziej wyrafinowanych operacji ni proste wywoanie delete. Pi implementacji
wskanikw inteligentnych w bibliotece Boost.Smart_ptr odzwierciedla za szereg
kategorii potrzeb pojawiajcych si w programowaniu.

Po co nam inteligentne wskaniki?


Po co nam inteligentne wskaniki?

Wskaniki inteligentne stosuje si przy:


t manipulowaniu zasobami pozostajcymi w posiadaniu wielu obiektw,
t pisaniu kodu odpornego na wyjtki,
t unikaniu typowych bdw w postaci wyciekw zasobw.

Wspdzielenie wasnoci zachodzi, kiedy dany obiekt jest uytkowany przez pewn
liczb innych obiektw. Jak (albo raczej: kiedy) naley zwolni w uywany obiekt?
Aby rozpozna odpowiedni moment zwolnienia wspuytkowanego obiektu, naleaoby wyposay kady z obiektw uytkujcych w informacje o wspwacicielach.
Tego rodzaju wizanie obiektw nie jest podane z punktu widzenia poprawnoci
projektowej, a take z uwagi na atwo konserwacji kodu. Lepiej byoby, aby obiekty-wspwaciciele zoyy odpowiedzialno za zarzdzanie czasem ycia wspuytkowanego obiektu na inteligentny wskanik. Ten, po wykryciu, e nie ma ju adnego
waciciela, moe bezpiecznie zwolni obiekt uytkowany.
Odporno na wyjtki to w najprostszym ujciu zabezpieczenie przed wyciekami zasobw, jak rwnie zabezpieczenie trwaoci niezmiennikw programu w obliczu
wyjtkw. Obiekt przydzielony dynamicznie moe w obliczu wyjtku nie zosta zwolniony. W ramach procedury zwijania stosu przy zrzucaniu wyjtku i porzucaniu biecego zasigu dojdzie do utracenia wskanikw obiektw dynamicznych, co uniemoliwi zwolnienie obiektu a do momentu zakoczenia programu (a i w fazie kocowej
programu jzyk nie daje gwarancji zwolnienia zasobw). Program niezabezpieczony
przed takim wpywem wyjtkw moe nie tylko doprowadzi do deficytu pamici
operacyjnej, ale i znale si w niestabilnym stanie; zastosowanie wskanikw inteligentnych automatyzuje zwalnianie zasobw nawet w obliczu wyjtkw.
Co do unikania typowych bdw, to najbardziej typowym jest chyba pominicie (wynikajce z przeoczenia) wywoania delete. Tymczasem typowy inteligentny wskanik
nie ledzi bynajmniej cieek przebiegu wykonania programu jego jedyn trosk
jest zwolnienie wskazywanego obiektu wywoaniem delete w ramach wasnej destrukcji. Stosowanie inteligentnych wskanikw zwalnia wic programist od koniecznoci ledzenia podanych momentw zwalniania obiektw. Do tego inteligentne

wskaniki mog ukrywa szczegy dealokacji, dziki czemu klienci nie musz wie-

38D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

39

dzie, kiedy wywoywa delete, kiedy specjaln funkcj zwalniajc, a kiedy w ogle
powstrzyma si od zwalniania zasobu.
Bezpieczne i efektywne wskaniki inteligentne to wana bro w arsenale programisty.
Cho biblioteka standardowa jzyka C++ udostpnia szablon std::auto_ptr, jego implementacja nie spenia wymienionych postulatw funkcjonalnoci inteligentnego
wskanika. Wskaniki auto_ptr nie mog na przykad wystpowa w roli elementw
kontenerw biblioteki STL. Luk w standardzie wypeniaj z powodzeniem klasy inteligentnych wskanikw z biblioteki Boost.
W niniejszym rozdziale skupimy si na klasach scoped_ptr, shared_ptr, intrusive_ptr
i weak_ptr. Uzupeniajce ten zestaw klasy scoped_array i shared_array, cho te
przydatne, nie s tak czsto potrzebne; do tego ich podobiestwo do pozostaych implementacji wskanikw uzasadnia mniejszy poziom szczegowoci omwienia.

Jak ma si biblioteka Smart_ptr


do biblioteki standardowej C++?
Biblioteka Smart_ptr zostaa zaproponowana do wcielenia do biblioteki standardowej.
Propozycja jest uzasadniona trojako:
t Biblioteka standardowa jzyka C++ oferuje obecnie jedynie klas auto_ptr,

pokrywajc zaledwie wski wycinek spektrum zastosowa inteligentnych


wskanikw. Zwaszcza w porwnaniu do klasy shared_ptr, udostpniajcej
odmienne, a jake wane udogodnienia.
t Wskaniki inteligentne biblioteki Boost zostay zaprojektowane jako

uzupenienie i naturalne rozszerzenie biblioteki standardowej. Na przykad


przed zaproponowaniem shared_ptr nie istniay standardowe wskaniki
inteligentne nadajce si do uycia w roli elementw standardowych
kontenerw.
t Programici ustanowili inteligentne wskaniki biblioteki Boost standardem

de facto, powszechnie i z powodzeniem wdraajc je we wasnych


programach.
Wymienione wzgldy sprawiaj, e biblioteka Smart_ptr stanowi bardzo podany
dodatek do biblioteki standardowej jzyka C++. Klasy shared_ptr (i szablon pomocniczy enable_shared_from_this) i weak_ptr z Boost.Smart_ptr zostay wic zaakceptowane do najbliszego raportu technicznego biblioteki standardowej, czyli dokumentu
zbierajcego propozycje dla najbliszego wydania standardu opisujcego bibliotek
jzyka C++.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
39

Cz I Biblioteki oglnego przeznaczenia

40

scoped_ptr
Nagwek:

"boost/scoped_ptr.hpp"

Szablon boost::scoped_ptr suy do zapewniania waciwego usuwania przydzielanego dynamicznie obiektu. Cechy klasy scoped_ptr upodobniaj j do std::auto_ptr,
z t istotn rnic, e w scoped_ptr nie zachodzi transfer prawa wasnoci, charakterystyczny dla auto_ptr. W rzeczy samej, wskanik scoped_ptr nie moe by kopiowany ani przypisywany! Wskanik scoped_ptr gwarantuje zachowanie wycznego
posiadania obiektu wskazywanego, uniemoliwiajc przypadkowe ustpienie wasnoci.
Ta wasno scoped_ptr pozwala na wyraziste rozgraniczenie tej rnicy w kodzie
poprzez stosowanie raz scoped_ptr, a raz auto_ptr, zalenie od potrzeb.
Wybierajc pomidzy std::auto_ptr a boost::scoped_ptr, naley rozway wanie
to, czy podan cech tworzonego inteligentnego wskanika ma by transfer prawa
wasnoci obiektu wskazywanego. Jeli nie, najlepiej zastosowa scoped_ptr. Jego
implementacja jest na tyle odchudzona, e jego wybr nie spowoduje ani rozrostu, ani
spowolnienia programu wpynie za to korzystnie na bezpieczestwo kodu i jego
zdatno do konserwacji.
Pora na przegld skadni scoped_ptr, uzupeniony krtkim opisem poszczeglnych
skadowych.
namespace boost {
template<typename T> class scoped_ptr : noncopyable {
public:
explicit scoped_ptr(T* p = 0);
~scoped_ptr();
void reset(T* p = 0);
T& operator*() const;
T* operator->() const;
T* get() const;
};

void swap(scoped_ptr& b);

template<typename T>
void swap(scoped_ptr<T> & a, scoped_ptr<T> & b);

Metody
explicit scoped_ptr(T* p = 0);

Konstruktor przechowujcy kopi p. Uwaga: p musi by przydzielone za porednictwem


wywoania operatora new albo mie warto pust (ang. null). T nie musi by w czasie

40D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

41

konstrukcji wskanika kompletnym typem. To przydatne, kiedy wskanik p jest wynikiem wywoania pewnej funkcji przydziau, a nie bezporedniego wywoania new:
skoro typ nie musi by kompletny, wystarcza deklaracja zapowiadajca T. Konstruktor nie zrzuca wyjtkw.
~scoped_ptr();

Usuwa obiekt wskazywany. Typ T musi by kompletny przy usuwaniu. Jeli wskanik
scoped_ptr nie przechowuje w czasie destrukcji adnego zasobu, destruktor nie wykonuje adnych operacji dealokacji. Destruktor nie zrzuca wyjtkw.
void reset(T* p = 0);

Wyzerowanie (ang. reset) wskanika scoped_ptr oznacza zwolnienie pozostajcego


w jego pieczy wskanika (o ile taki istnieje), a nastpnie przyjcie na wasno wskanika p. Zazwyczaj scoped_ptr przejmuje cakowicie zarzdzanie czasem ycia obiektu,
ale w rzadkich sytuacjach, kiedy trzeba zwolni zasb jeszcze przed zwolnieniem obiektu
wskanika scoped_ptr albo trzeba przekaza pod jego opiek zasb inny ni pierwotny.
Jak wida, metoda reset moe si przyda, ale naley j stosowa wstrzemiliwie
(zbyt czste stosowanie znamionuje niekiedy uomnoci projektu). Metoda nie zrzuca
wyjtkw.
T& operator*() const;

Zwraca referencj obiektu wskazywanego przez wskanik przechowywany w obiekcie scoped_ptr. Poniewa nie istnieje co takiego jak referencje puste, wyuskiwanie
za porednictwem tego operatora obiektu scoped_ptr zawierajcego wskanik pusty
prowokuje niezdefiniowane zachowanie. Jeli wic zachodz wtpliwoci co do wartoci przechowywanego wskanika, naley skorzysta z metody get. Operator nie zrzuca wyjtkw.
T* operator->() const;

Zwraca przechowywany wskanik. Wywoanie tej metody na rzecz obiektu scoped_ptr zawierajcego wskanik pusty prowokuje niezdefiniowane zachowanie. Jeli
nie ma pewnoci, czy wskanik jest pusty, czy nie, naley zastosowa metod get.
Operator nie zrzuca wyjtkw.
T* get() const;

Zwraca przechowywany wskanik. Metod get naley stosowa z zachowaniem


ostronoci, a to z racji ryzyka zwizanego z manipulowaniem goym wskanikiem.
Metoda get przydaje si jednak choby do jawnego sprawdzenia, czy przechowywany wskanik jest pusty. Metoda nie zrzuca wyjtkw. Wywouje si j typowo celem
zaspokojenia wymogw, np. wywoania funkcji wymagajcej przekazania argumentu
w postaci zwykego wskanika.
operator nieokrelony-typ-logiczny() const

Okrela, czy scoped_ptr jest niepusty. Typ wartoci zwracanej (bliej nieokrelony) powinien nadawa si do stosowania w kontekcie wymagajcym wartoci logicznych.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
41

Cz I Biblioteki oglnego przeznaczenia

42

T funkcj konwersji mona stosowa zamiast metody get w instrukcjach warunkowych do testowania stanu wskanika scoped_ptr.
void swap(scoped_ptr& b);

Wymienia zawarto dwch obiektw klasy scoped_ptr. Nie zrzuca wyjtkw.

Funkcje zewntrzne
template<typename T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b);

Szablon funkcji realizujcej preferowan metod wymiany zawartoci dwch obiektw


klasy scoped_ptr. To preferowana metoda podmiany, bo wywoanie swap(scoped1,
scoped2) moe by stosowane w sposb uoglniony (w kodzie szablonowym) dla wielu
typw wskanikowych, w tym goych wskanikw i inteligentnych wskanikw w implementacjach zewntrznych2. Tymczasem alternatywne wywoanie scoped1.swap(scoped2)
zadziaa tylko dla odpowiednio wyposaonych wskanikw inteligentnych, ale ju nie
dla wskanikw zwykych.

Stosowanie
Klas scoped_ptr stosuje si jak zwyky typ wskanikowy, z paroma zaledwie (za to
istotnymi) rnicami; najwaniejsza objawia si w tym, e nie trzeba pamita o wywoywaniu delete dla takiego wskanika i e nie mona uywa go w operacjach
kopiowania. Typowe operatory wyuskania dla typw wskanikowych (operator*
i operator->) s przecione dla klasy scoped_ptr, tak aby skadniowo stosowanie
wskanikw inteligentnych nie rnio si od stosowania wskanikw zwykych. Odwoania za porednictwem wskanikw scoped_ptr s rwnie szybkie, jak za porednictwem wskanikw zwykych, nie ma tu te adnych dodatkowych narzutw co do
rozmiaru mona wic ich uywa powszechnie. Stosowanie klasy boost::scoped_ptr
wymaga wczenia do kodu pliku nagwkowego "boost/scoped_ptr.hpp". Przy deklarowaniu wskanika scoped_ptr szablon konkretyzuje si typem obiektu wskazywanego.
Oto przykadowy scoped_ptr, kryjcy wskanik obiektu klasy std::string:
boost::scoped_ptr<std::string> p(new std::string("Ahoj"));

Przy usuwaniu obiektu scoped_ptr jego destruktor sam wywouje operator delete dla
przechowywanego wskanika.

Brak koniecznoci rcznego usuwania obiektu


Spjrzmy na program, ktry wykorzystuje obiekt klasy scoped_ptr do zarzdzania
wskanikiem obiektu klasy std::string. Zauwamy brak jawnego wywoania delete;
obiekt scoped_ptr jest zmienn automatyczn i jako taka podlega usuwaniu przy wychodzeniu z zasigu.
2

Dla takich zewntrznych implementacji wskanikw inteligentnych, ktre nie udostpniaj swojej
wersji swap, mona napisa wasn funkcj wymiany przyp. aut.

42D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

43

#include "boost/scoped_ptr.hpp"
#include <string>
#include <iostream>
int main()
{
boost::scoped_ptr<std::string>
p(new std::string("Zawsze uywaj scoped_ptr!"));
// Wypisanie cigu na wyjciu programu
if (p)
std::cout << *p << '\n';
// Okrelenie dugoci cigu
size_t i=p->size();
// Pzypisanie nowej wartoci cigu
*p="Jak zwyky wskanik";
}

} // Tu usunicie p i zwolnienie (delete) wskazywanego obiektu std::string

W powyszym kodzie wypadaoby zwrci uwag na kilka elementw. Przede wszystkim scoped_ptr mona testowa jak zwyke wskaniki, bo udostpnia funkcj niejawnej konwersji na typ zdatny do stosowania w wyraeniach logicznych. Po drugie,
wywoanie metody na rzecz obiektu wskazywanego dziaa identycznie jak dla zwykych wskanikw, a to z racji obecnoci przecionego operatora operator->. Po
trzecie wreszcie, wyuskanie scoped_ptr dziaa rwnie tak, jak dla wskanikw zwykych to za spraw przecionego operatora operator*. Te wasnoci czyni stosowanie obiektw scoped_ptr (i innych wskanikw inteligentnych) tak naturalnym,
jak stosowanie najzwyklejszych goych wskanikw. Rnice sprowadzaj si wic
do wewntrznego zarzdzania czasem ycia obiektu wskazywanego, nie do skadni
odwoa.

Prawie jak auto_ptr


Zasadnicza rnica pomidzy scoped_ptr a auto_ptr sprowadza si do traktowania
prawa wasnoci do wskazywanego obiektu. Ot auto_ptr przy kopiowaniu ochoczo
dokonuje transferu wasnoci poza rdowy obiekt auto_ptr; tymczasem wskanika scoped_ptr po prostu nie mona skopiowa. Spjrzmy na poniszy program,
porwnujcy zachowanie auto_ptr i scoped_ptr w kontekcie kopiowania.
void scoped_vs_auto() {
using boost::scoped_ptr;
using std::auto_ptr;
scoped_ptr<std::string> p_scoped(new std::string("Ahoj"));
auto_ptr<std::string> p_auto(new std::string("Ahoj"));

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
43

Cz I Biblioteki oglnego przeznaczenia

44
p_scoped->size();
p_auto->size();

scoped_ptr<std::string> p_another_scoped=p_scoped;
auto_ptr<std::string> p_another_auto=p_auto;

p_another_auto->size();
(*p_auto).size();

Niniejszy przykad nie da si nawet skompilowa, bo scoped_ptr nie moe uczestniczy


w operacji konstrukcji kopiujcej ani przypisania. Tymczasem auto_ptr da si i kopiowa, i przypisywa kopiujco, przy czym te operacje realizuj przeniesienie prawa
wasnoci ze wskanika rdowego (tu p_auto) do docelowego (tu p_another_auto),
zostawiajc orygina ze wskanikiem pustym. Moe to prowadzi do nieprzyjemnych
niespodzianek, na przykad przy prbie umieszczenia obiektu auto_ptr w kontenerze3.
Gdyby z kodu usun przypisanie do p_another_scoped, program daby si skompilowa, ale z kolei w czasie wykonania prowokowaby niewiadome zachowanie, a to
z powodu prby wyuskania pustego wskanika p_auto (*p_auto).
Poniewa metoda scoped_ptr::get zwraca goy wskanik, ktry moe posuy do
rzeczy haniebnych, dlatego warto od razu zapamita dwie rzeczy, ktrych trzeba
unika. Po pierwsze, nie zwalnia samodzielnie wskanika przechowywanego w obiekcie scoped_ptr. Bdzie on usuwany ponownie przy usuwaniu tego obiektu. Po drugie,
nie kopiowa wycignitego wskanika do innego obiektu scoped_ptr (ani dowolnego
innego wskanika inteligentnego). Dwukrotne usunicie wskanika, po razie przy usuwaniu kadego z zawierajcych go obiektw scoped_ptr, moe sprowokowa nieszczcie. Krtko mwic, get naley stosowa wstrzemiliwie i tylko tam, gdzie koniecznie trzeba posugiwa si goymi wskanikami!

Wskaniki scoped_ptr a idiom prywatnej implementacji


Wskaniki scoped_ptr wietnie nadaj si do stosowania tam, gdzie wczeniej zastosowania znajdoway wskaniki zwyke albo obiekty auto_ptr, a wic na przykad
w implementacjach idiomu prywatnej implementacji (ang. pimpl)4. Stojca za nim
koncepcja sprowadza si do izolowania uytkownikw klasy od wszelkich informacji
o prywatnych czciach tej klasy. Poniewa uytkownicy klasy s uzalenieni od pliku nagwkowego teje klasy, wszelkie zmiany w tym pliku nagwkowym wymuszaj ponown kompilacj kodu uytkownikw, nawet jeli zmiany ograniczay si do
obszarw prywatnych i zabezpieczonych klasy, a wic obszarw niby niedostpnych
z zewntrz. Idiom implementacji prywatnej zakada ukrywanie szczegw prywatnych
3

Nigdy, przenigdy nie wolno umieszcza wskanikw auto_ptr w kontenerach biblioteki standardowej.
Prba taka sprowokuje zazwyczaj bd kompilacji; jeli nie, miaka czekaj powaniejsze kopoty
przyp. aut.
Wicej o samym idiomie mona przeczyta w ksice Exceptional C++ (Wyjtkowy jzyk C++
przyp. tum.) i pod adresem www.gotw.ca/gotw/024.htm przyp. aut.

44D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

45

przez przeniesienie danych i metod prywatnych do osobnego typu definiowanego


w pliku implementacji; w pliku nagwkowym mamy jedynie deklaracj zapowiadajc w typ, a w wykorzystujcej go klasie wskanik tego typu. Konstruktor klasy
przydziela obiekt typu waciwego dla prywatnej implementacji, a destruktor klasy go
zwalnia. W ten sposb mona usun niepodane zalenoci z pliku nagwkowego.
Sprbujmy skonstruowa klas implementujc idiom za porednictwem inteligentnych wskanikw.
// pimpl_sample.hpp
#if !defined (P#iP _dAiP e)
#define P#iP _dAiP e
struct impl;
class pimpl_sample {
impl* pimpl_;
public:
pimpl_sample();
~pimpl_sample();
void do_something();
};
#endif

Tak prezentuje si interfejs klasy pimpl_sample. Mamy tu te deklaracj zapowiadajc typu struct impl, reprezentujcego typ prywatnej implementacji klasy i obejmujcego prywatne skadowe i metody, definiowane w pliku implementacji klasy. W efekcie uytkownicy klasy pimpl_sample s zupenie odizolowani od jej wewntrznych
szczegw implementacji.
// pimpl_sample.cpp
#include "pimpl_sample.hpp"
#include <string>
#include <iostream>
struct pimpl_sample::impl {
void do_something_() {
std::cout << s_ << '\n';
}
std::string s_;
};
pimpl_sample::pimpl_sample()
: pimpl_(new impl) {
pimpl_->s_ = "Pimpl - idiom implementacji prywatnej";
}
pimpl_sample::~pimpl_sample() {

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
45

Cz I Biblioteki oglnego przeznaczenia

46

delete pimpl_;

void pimpl_sample::do_something() {
pimpl_->do_something_();
}

Z pozoru kod wyglda zupenie poprawnie, ale nie jest niestety doskonay. Ot taka
implementacja nie jest odporna na wyjtki! Sk w tym, e konstruktor pimpl_sample
moe zrzuci wyjtek ju po skonstruowaniu pimpl. Zgoszenie wyjtku z konstruktora
oznacza, e konstruowany obiekt nigdy w peni nie istnia, wic przy zwijaniu stosu
nie dochodzi do wywoania jego konstruktora. To za oznacza, e pami przydzielona
do wskanika pimpl_ nie zostanie zwolniona i dojdzie do wycieku. Mona temu jednak
atwo zaradzi: na ratunek przychodzi wskanik scoped_ptr!
class pimpl_sample {
struct impl;
boost::scoped_ptr<impl> pimpl;

};

Kwesti odpornoci na wyjtki zaatwiamy, przekazujc zadanie zarzdzania czasem


ycia obiektu ukrytej klasy impl na barki wskanika scoped_ptr i usuwajc jawne
zwolnienie impl z destruktora klasy pimpl_sample (za spraw scoped_ptr wywoanie
delete nie jest tam ju potrzebne). Wci trzeba jednak pamita o koniecznoci definiowania wasnego destruktora; chodzi o to, e w czasie, kiedy kompilator generowaby destruktor domylny, typ impl nie byby jeszcze znany w caoci, wic nie nastpioby wywoanie jego destruktora. Gdyby obiekt prywatnej implementacji by
wskazywany wskanikiem auto_ptr, kod taki skompilowaby si bez bdw; uycie
scoped_ptr prowokuje bd kompilacji.
Kiedy scoped_ptr wystpuje w roli skadowej klasy, trzeba rcznie definiowa dla tej
klasy konstruktor kopiujcy i kopiujcy operator przypisania. Chodzi naturalnie o to,
e wskanikw scoped_ptr nie mona kopiowa, wic klasa zawierajca takie wskaniki rwnie przestaje nadawa si do kopiowania (przynajmniej prostego kopiowania
skadowych).
Na koniec warto zaznaczy, e jeli egzemplarz implementacji prywatnej (tu pimpl)
da si bezpiecznie dzieli pomidzy egzemplarzami klasy z niej korzystajcej (tu
pimpl_sample), wtedy do zarzdzania czasem ycia obiektu implementacji naleaoby
wykorzysta wskanik klasy boost::shared_ptr. Zalety stosowania shared_ptr w takiej
sytuacji przejawiaj si zwolnieniem z koniecznoci rcznego definiowania konstruktora kopiujcego i operatora przypisania dla klasy i pustego destruktora shared_ptr
nadaje si do obsugi rwnie typw niekompletnych.

46D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

47

scoped_ptr to nie to samo, co const auto_ptr


Uwany Czytelnik zorientowa si ju zapewne, e zachowanie wskanika auto_ptr
mona by prbowa upodobni do zachowania scoped_ptr, deklarujc ten pierwszy
ze sowem const:
const auto_ptr<A> no_transfer_of_ownership(new A);

To co prawda nieze przyblienie, ale niezupene. Wci mamy tak rnic, e wskanik scoped_ptr da si wyzerowa (przestawi) wywoaniem metody reset, co pozwala
na podstawianie nowych obiektw wskazywanych w razie potrzeby. Nie da si tego
zrobi z uyciem const auto_ptr. Kolejna rnica, ju nieco mniejsza, to rnica w wymowie nazw: const auto_ptr znaczy mniej wicej tyle, co scoped_ptr, ale przy mniej
zwizym i jasnym zapisie. Za po przyswojeniu znaczenia scoped_ptr mona stosowa
go w celu jasnego wyraania intencji programisty co do zachowania wskanika. Jeli
trzeba gdzie podkreli osadzenie zasobu w zasigu, przy rwnoczesnym wyraeniu
niemonoci przekazywania wasnoci obiektu wskazywanego, naley t ch wyrazi
przez boost::scoped_ptr.

Podsumowanie
Goe wskaniki komplikuj zabezpieczanie kodu przed wyjtkami i bdami wycieku
zasobw. Automatyzacja kontroli czasu ycia obiektw przydzielanych dynamicznie
do danego zasigu za porednictwem inteligentnych wskanikw to wietny sposb
wyeliminowania wad zwykych wskanikw przy rwnoczesnym zwikszeniu czytelnoci, atwoci konserwacji i oglnie pojtej jakoci kodu. Stosowanie scoped_ptr
to jednoznaczne wyraenie zamiaru blokowania wspuytkowania i przenoszenia
prawa wasnoci obiektu wskazywanego. Przekonalimy si, e std::auto_ptr moe
podkrada wskazania innym obiektom tej klasy, co uwaa si za najwikszy grzech
auto_ptr. Wanie dlatego scoped_ptr tak wietnie uzupenia auto_ptr. Kiedy do
scoped_ptr przekazywany jest obiekt przydzielany dynamicznie, wskanik zakada,
e posiada wyczne prawo dysponowania obiektem. Poniewa wskaniki scoped_ptr
s niemal zawsze przydzielane jako obiekty (zmienne bd skadowe) automatyczne,
mamy gwarancj ich usunicia przy wychodzeniu z zasigu, co prowadzi do pewnego
zwolnienia obiektw wskazywanych, niezalenie od tego, czy opuszczenie zasigu
byo planowe (instrukcj return), czy awaryjne, wymuszone zgoszeniem wyjtku.
Wskaniki scoped_ptr naley stosowa tam, gdzie:
t w zasigu obarczonym ryzykiem zgoszenia wyjtku wystpuje wskanik;
t funkcja ma kilka cieek wykonania i kilka punktw powrotu;
t czas ycia obiektu przydzielanego dynamicznie ma by ograniczony

do pewnego zasigu;
t wana jest odporno na wyjtki (czyli prawie zawsze!).

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
47

Cz I Biblioteki oglnego przeznaczenia

48

scoped_array
Nagwek:

"boost/scoped_array.hpp"

Potrzeb tworzenia dynamicznie przydzielanych tablic elementw zwyko si zaspokaja za pomoc kontenera std::vector, ale w co najmniej dwch przypadkach uzasadnione jest uycie zwykych tablic: przy optymalizowaniu kodu (bo implementacja
kontenera vector moe wprowadza narzuty czasowe i pamiciowe) i przy wyraaniu
jasnej intencji co do sztywnego rozmiaru tablicy5. Tablice przydzielane dynamicznie
prowokuj zagroenia waciwe dla zwykych wskanikw, z dodatkowym (zbyt czstym) ryzykiem omykowego zwolnienia tablicy wywoaniem delete zamiast delete [].
Omyki te zdarzyo mi si widywa w najmniej oczekiwanych miejscach, nawet
w powszechnie stosowanych, komercyjnych implementacjach klas kontenerw! Klasa
scoped_array jest dla tablic tym, czym scoped_ptr dla wskanikw: przejmuje odpowiedzialno za zwolnienie pamici tablicy. Tyle e scoped_array robi to za pomoc
operatora delete [].
Przyczyna, dla ktrej scoped_array jest osobn klas, a nie na przykad specjalizacj
scoped_ptr, tkwi w niemonoci rozrnienia wskanikw pojedynczych elementw
i wskanikw caych tablic za pomoc technik metaprogramowania. Mimo wysikw
nikt nie znalaz jeszcze pewnego sposobu rnicowania tych wskanikw; sk w tym,
e wskaniki tablic daj si tak atwo przeksztaca we wskaniki pojedynczych obiektw i nie zachowuj w swoim typie adnych informacji o tym, e wskazyway wanie
tablice. W efekcie trzeba zastosowania wskanikw zwykych i wskanikw tablic
rnicowa samodzielnie, stosujc dla nich jawnie scoped_ptr i scoped_array tak
jak samodzielnie trzeba pamita o zwalnianiu wskazywanych tablic wywoaniem
delete [] zamiast delete. Uciekajc si do stosowania scoped_array, zyskujemy automatyzm zwalniania tablicy i jasno intencji: wiadomo od razu, e chodzi o wskanik tablicy, a nie pojedynczego elementu.
Wskanik scoped_array przypomina bardzo scoped_ptr; jedn z niewielu rnic jest
przecianie (w tym pierwszym) operatora indeksowania operator[], umoliwiajcego
stosowanie naturalnej skadni odwoa do elementw tablicy.
Wskanik scoped_array naley uzna za pod kadym wzgldem lepsz alternatyw
dla klasycznych tablic przydzielanych dynamicznie. Przejmuje on zarzdzanie czasem
ycia tablic tak, jak scoped_ptr przejmuje zarzdzenie czasem ycia obiektw wskazywanych. Trzeba jednak pamita, e w bardzo wielu przypadkach jest alternatyw
gorsz ni kontener std::vector (elastyczniejszy i dajcy wiksze moliwoci). Wskanik scoped_array zdaje si przewysza kontenery std::vector jedynie tam, gdzie trzeba
jasno wyrazi ch sztywnego ograniczenia rozmiaru tablicy.

W rzeczy samej, w zdecydowanej wikszoci przypadkw lepiej faktycznie skorzysta ze standardowej


implementacji kontenera vector. Decyzja o stosowaniu scoped_array powinna wynika z pomiarw
wydajnoci przyp. aut.

48D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

49

shared_ptr
Nagwek:

"boost/shared_ptr.hpp"

Chyba kady nietrywialny program wymaga stosowania jakiej postaci inteligentnych


wskanikw ze zliczaniem odwoa do obiektw wskazywanych. Takie wskaniki
eliminuj konieczno kodowania skomplikowanej logiki sterujcej czasem ycia obiektw wspuytkowanych przez pewn liczb innych obiektw. Kiedy warto licznika
odwoa spadnie do zera, oznacza to brak obiektw zainteresowanych uytkowaniem
danego zasobu i moliwo jego zwolnienia. Inteligentne wskaniki ze zliczaniem
odwoa mona podzieli na ingerencyjne i nieingerencyjne (ang. odpowiednio: intrusive i non-intrusive). Te pierwsze wymagaj od klas obiektw zarzdzanych udostpniania specjalnych metod albo skadowych, za pomoc ktrych realizowane jest
zliczanie odwoa. Oznacza to konieczno projektowania klas zasobw wspuytkowanych z uwzgldnieniem wymaganej infrastruktury albo pniejszego uzdatniania
takich klas przez np. pakowanie ich w klasy implementujce infrastruktur zliczania
odwoa. Z kolei wskaniki zliczajce odwoania w sposb nieingerencyjny nie wymagaj niczego od typu obiektu zarzdzanego. Wskaniki zliczajce odwoania zakadaj wyczno wasnoci pamici skojarzonej z przechowywanymi wskanikami.
Kopot ze wspuytkowaniem obiektu bez pomocy ze strony inteligentnych wskanikw polega na tym, e cho trzeba wreszcie zwolni taki obiekt, nie wiadomo,
kto i kiedy miaby to zrobi. Bez pomocy ze strony mechanizmu zliczania odwoa
trzeba arbitralnie i zewntrznie ograniczy czas ycia obiektu wspuytkowanego,
co oznacza zwykle zwizanie jego uytkownikw nadmiernie silnymi zalenociami.
To z kolei niekorzystnie wpywa na prostot kodu i jego zdatno do wielokrotnego
uycia.
Klasa obiektu zarzdzanego moe przejawia wasnoci predestynujce j do stosowania ze wskanikami zliczajcymi odwoania. Takimi cechami mog by kosztowno
operacji kopiowania albo wspuytkowanie czci implementacji pomidzy wieloma
egzemplarzami klasy. S te sytuacje, w ktrych nie istnieje jawny waciciel wspuytkowanego zasobu. Stosowanie inteligentnych wskanikw ze zliczaniem odwoa umoliwia dzielenie wasnoci pomidzy obiektami, ktre wymagaj dostpu do
wsplnego zasobu. Wskaniki zliczajce odwoania umoliwiaj rwnie przechowywanie wskanikw obiektw w kontenerach biblioteki standardowej bez ryzyka
wyciekw pamici, zwaszcza w obliczu wyjtkw albo operacji usuwania elementw
z kontenera. Z kolei przechowywanie wskanikw w kontenerach pozwala na wykorzystanie zalet polimorfizmu, zwikszenie efektywnoci skadowania (w przypadku klas
zakadajcych kosztowne kopiowanie) i moliwo skadowania tych samych obiektw
w wielu specjalizowanych kontenerach, wybranych ze wzgldu na np. efektywno
sortowania.
Po stwierdzeniu chci zastosowania inteligentnego wskanika zliczajcego odwoania
trzeba zdecydowa midzy implementacj ingerencyjn i nieingerencyjn. Niemal
zawsze lepszym wyborem s wskaniki nieingerencyjne, a to z racji ich uniwersalnoci, braku wpywu na istniejcy kod i elastycznoci. Wskaniki nieingerencyjne mona stosowa z klasami, ktrych z pewnych wzgldw nie mona zmienia. Zwykle
klas adaptuje si do wsppracy z inteligentnym wskanikiem ingerencyjnym przez

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
49

Cz I Biblioteki oglnego przeznaczenia

50

wyprowadzenie klasy pochodnej z klasy bazowej zliczajcej odwoania. Taka modyfikacja klasy moe by kosztowniejsza, ni si zdaje. W najlepszym przypadku trzeba
ponie koszt w postaci zacienienia zalenoci i zmniejszenia zdatnoci klasy do
uycia w innych kontekstach6. Zwykle oznacza to rwnie zwikszenie rozmiaru
obiektw klasy adaptowanej, co w niektrych kontekstach ogranicza jej przydatno7.
Obiekt klasy shared_ptr mona skonstruowa na bazie zwykego wskanika, innego
obiektu shared_ptr i obiektw klasy std::auto_ptr bd boost:weak_ptr. Do konstruktora shared_ptr mona te przekaza drugi argument, w postaci dealokatora
(ang. deleter). Jest on pniej wykorzystywany do obsugi operacji usunicia zasobu
wspuytkowanego. Przydaje si w zarzdzaniu takimi zasobami, ktre nie byy przydzielane wywoaniem new i nie powinny by zwalniane zwykym delete (przykady
tworzenia wasnych dealokatorw zostan zaprezentowane dalej). Po skonstruowaniu
obiektu shared_ptr mona go stosowa jak najzwyklejszy wskanik, z tym wyjtkiem, e nie trzeba jawnie zwalnia obiektu wskazywanego.
Poniej prezentowana jest czciowa deklaracja szablonu shared_ptr; wystpuj tu
najwaniejsze skadowe i metody, ktre za chwil doczekaj si krtkiego omwienia.
namespace boost {
template <typename T> class shared_ptr {
public:
template<class Y> explicit shared_ptr(Y* p);
template<class Y, class D> shared_ptr(Y* p, D d);
~shared_ptr();
shared_ptr(const shared_ptr & r);
template <class Y> explicit shared_ptr(const weak_ptr<Y>& r);
template <classY> explicit shared_ptr(std::auto_ptr<Y>& r);
shared_ptr& operator=(const shared_ptr & r);
void reset();
T& operator*() const;
T* operator->() const;
T* get() const;
bool unique() const;
long use_count() const;
operator nieokrelony-typ-logiczny() const;
6

Wemy choby przypadek, kiedy jedn klas obiektu zarzdzanego trzeba by przystosowa do stosowania
z kilkoma klasami ingerencyjnych, inteligentnych wskanikw zliczajcych odwoania. Owe rne klasy
bazowe infrastruktury zliczania odwoa mogyby by niezgodne ze sob; gdyby za tylko jedna z klas
wskanikw bya kategorii ingerencyjnej, stosowanie wskanikw nieingerencyjnych odbywaoby si
ze zbdnym wtedy narzutem jednej klasy bazowej przyp. aut.
Z drugiej strony nieingerencyjne wskaniki inteligentne wymagaj dla siebie przydziau dodatkowej
pamici dla niezbdnej infrastruktury zliczania odwoa przyp. aut.

50D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

};

51

void swap(shared_ptr<T>& b);

template <class T, class >


shared_ptr<T> static_pointer_cast(const shared_ptr< >& r);

Metody
template<class Y> explicit shared_ptr(Y* p);

Konstruktor przejmujcy w posiadanie przekazany wskanik p. Argument wywoania powinien by poprawnym wskanikiem Y. Konstrukcja powoduje ustawienie licznika odwoa na 1. Jedynym wyjtkiem, jaki moe by zrzucony z konstruktora, jest
std::bad_alloc (w mao prawdopodobnym przypadku, kiedy nie ma pamici do
przydzielenia licznika odwoa).
template<class Y, class D> shared_ptr(Y* p, D d);

Konstruktor przyjmujcy par argumentw. Pierwszy z nich to zasb, ktry ma przej


pod opiek tworzonego wskanika shared_ptr, drugi to obiekt odpowiedzialny za
zwolnienie zasobu wskazywanego przy usuwaniu wskanika. Zasb jest przekazywany do obiektu zwalniajcego wywoaniem d(p). Z tego wzgldu poprawne wartoci p
zale od d. Jeli nie uda si przydzieli licznika odwoa, konstruktor zrzuci wyjtek
std::bad_alloc.
shared_ptr(const shared_ptr & r);

Zasb wskazywany przez r jest wspdzielony z nowo tworzonym wskanikiem shared_ptr, co powoduje zwikszenie licznika odwoa o 1. Konstruktor kopiujcy nie
zrzuca wyjtkw.
template <class Y> explicit shared_ptr(const weak_ptr<Y>& r);

Konstruuje wskanik shared_ptr na podstawie wskanika weak_ptr (omawianego


w dalszej czci rozdziau). Pozwala to na zabezpieczenie zastosowania weak_ptr
w aplikacjach wielowtkowych przez zwikszenie licznika odwoa do zasobu wspuytkowanego wskazywanego przez argument typu weak_ptr (wskaniki weak_ptr nie
wpywaj na stan licznikw odwoa do zasobw wspdzielonych). Jeli wskanik
weak_ptr jest pusty (to jest r.use_count() == 0), konstruktor shared_ptr zrzuca wyjtek typu bad_weak_ptr.
template <classY> explicit shared_ptr(std::auto_ptr<Y>& r);

Konstrukcja wskanika shared_ptr na bazie auto_ptr oznacza przejcie wasnoci


wskanika przechowywanego w r przez utworzenie jego kopii i wywoanie na rzecz
rdowego obiektu auto_ptr metody release. Licznik odwoa po konstrukcji ma
warto 1. Oczywicie r jest zerowane. Niemono przydziau pamici dla licznika
odwoa prowokuje wyjtek std::bad_alloc.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
51

Cz I Biblioteki oglnego przeznaczenia

52
~shared_ptr();

Destruktor klasy shared_ptr zmniejsza o 1 licznik odwoa do zasobu wskazywanego.


Jeli warto licznika spadnie w wyniku zmniejszenia do zera, destruktor zwolni obiekt
wskazywany. Polega to na wywoaniu dla operatora delete albo przekazanego w konstruktorze obiektu zwalniajcego; w tym ostatnim przypadku jedynym argumentem
wywoania obiektu zwalniajcego jest wskanik przechowywany w shared_ptr. Destruktor nie zrzuca wyjtkw.
shared_ptr& operator=(const shared_ptr & r);

Operator przypisania inicjuje wspuytkowanie zasobu z r, z zaniechaniem wspuytkowania zasobu biecego. Nie zrzuca wyjtkw.
void reset();

Metoda reset suy do rezygnacji ze wspdzielenia zasobu wskazywanego przechowywanym wskanikiem; wywoanie zmniejsza licznik odwoa do zasobu.
T& operator*() const;

Przeciony operator zwracajcy referencj obiektu (zasobu) wskazywanego. Zachowanie operatora dla pustego wskanika przechowywanego jest niezdefiniowane. Operator nie zrzuca wyjtkw.
T* operator->() const;

Przeciony operator zwracajcy przechowywany wskanik. Razem z przecionym


operatorem wyuskania operator* upodobnia zachowanie shared_ptr do zwykych
wskanikw. Operator nie zrzuca wyjtkw.
T* get() const;

Metoda get to zalecany sposb odwoywania si do wskanika przechowywanego


w obiekcie shared_ptr, kiedy istnieje podejrzenie, e wskanik ten ma warto pust
(kiedy to wywoania operatorw operator* i operator-> prowokuj niezdefiniowane
zachowanie). Zauwamy, e stan wskanika w wyraeniach logicznych mona testowa
rwnie za porednictwem funkcji niejawnej konwersji shared_ptr na typ logiczny.
Metoda nie zrzuca wyjtkw.
bool unique() const;

Metoda zwraca true, jeli obiekt shared_ptr, na rzecz ktrego nastpio wywoanie,
jest jedynym wacicielem przechowywanego wskanika. W pozostaych przypadkach
zwraca false. Metoda nie zrzuca wyjtkw.
long use_count() const;

Metoda use_count zwraca warto licznika odwoa do wskanika przechowywanego


w obiekcie shared_ptr. Przydaje si w diagnostyce, bo pozwala na zdejmowanie migawek
wartoci licznika odwoa w krytycznych punktach wykonania programu. Stosowa

52D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

53

wstrzemiliwie; dla niektrych moliwych implementacji interfejsu shared_ptr ustalenie liczby odwoa moe by kosztowne obliczeniowo albo nawet niemoliwe. Metoda
nie zrzuca wyjtkw.
operator nieokrelony-typ-logiczny() const;

Niejawna konwersja na typ logiczny, umoliwiajca stosowanie obiektw shared_ptr


w wyraeniach logicznych (i tam, gdzie oczekiwane s wyraenia logiczne). Zwracana
warto to true, jeli shared_ptr przechowuje obecnie jaki ustawiony wskanik, bd
false w pozostaych przypadkach. Zauwamy, e typ wartoci zwracanej nie jest okrelony; zastosowanie typu bool pozwalaoby na angaowanie wskanikw shared_ptr
w niektrych bezsensownych dla jego semantyki operacjach, std w implementacji
najczciej stosowany jest idiom bezpiecznej wartoci logicznej8, ktry ogranicza zastosowania wartoci zwracanej do odpowiednich testw logicznych. Metoda nie zrzuca
wyjtkw.
void swap(shared_ptr<T>& b);

Niekiedy trzeba wymieni wskaniki pomidzy dwoma obiektami shared_ptr. Metoda


swap wymienia przechowywane wskaniki oraz liczniki odwoa. Nie zrzuca wyjtkw.

Funkcje zewntrzne
template <class T, class >
shared_ptr<T> static_pointer_cast(const shared_ptr< >& r);

Wykonanie statycznego rzutowania wskanika przechowywanego w shared_ptr mona


zrealizowa poprzez wydobycie wskanika i wywoanie static_cast, ale wyniku nie
mona by skutecznie umieci w innym wskaniku shared_ptr: nowy wskanik shared_ptr uznaby, e jest pierwszym posiadaczem zasobu wskazywanego. Problem
eliminuje funkcja static_pointer_cast. Gwarantuje ona zachowanie poprawnej wartoci
licznika odwoa. Funkcja nie zrzuca wyjtkw.

Stosowanie
Zasadniczym celem stosowania shared_ptr jest eliminowanie koniecznoci kodowania logiki wykrywajcej waciwy moment zwolnienia zasobu wspuytkowanego przez wielu uytkownikw. Poniej mamy prosty przykad, gdzie dwie klasy
A i B korzystaj ze wsplnego egzemplarza wartoci typu int. Uwaga: korzystanie ze wskanikw shared_ptr wymaga wczenia do kodu pliku nagwkowego
"boost/shared_ptr.hpp".
#include "boost/shared_ptr.hpp"
#include <cassert>
class A {
8

Autorstwa Petera Dimowa przyp. aut.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
53

Cz I Biblioteki oglnego przeznaczenia

54
boost::shared_ptr<int> no_;
public:
A(boost::shared_ptr<int> no) : no_(no) {}
void value(int i) {
*no_ = i;
}
};
class B {
boost::shared_ptr<int> no_;
public:
B(boost::shared_ptr<int> no) : no_(no) {}
void value() const {
return *no_;
}
};
int main() {
boost::shared_ptr<int> temp(new int(14));
A a(temp);
B b(temp);
a.value(28);
assert(b.value()==28);
}

Klasy A i B z powyszego przykadu zawieraj skadow typu shared_ptr<int>. Przy


tworzeniu egzemplarzy A i B przekazujemy do obu konstruktorw ten sam egzemplarz
obiektu shared_ptr: temp. Oznacza to, e mamy trzy wskaniki odnoszce si do tej
samej zmiennej typu int: jeden w postaci obiektu temp i dwa zaszyte w skadowych
no_ klas A i B. Gdyby wspuytkowanie zmiennej byo realizowane za pomoc zwykych wskanikw, klasy A i B miayby ciki orzech do zgryzienia w postaci wytypowania waciwego momentu zwolnienia zmiennej. W tym przykadzie licznik odwoa do zmiennej ma a do koca funkcji main warto 3; u kresu zasigu funkcji
wszystkie obiekty shared_ptr zostan usunite, co bdzie powodowa zmniejszanie
licznika a do zera. Wyzerowanie sprowokuje za usunicie przydzielonej zmiennej
typu int.

Jeszcze o idiomie implementacji prywatnej


Idiom prywatnej implementacji rozpatrywalimy wczeniej w aspekcie wskanikw
scoped_ptr, znakomicie nadajcych si do przechowywania dynamicznie przydzielanych egzemplarzy implementacji prywatnej tam, gdzie wcielenie idiomu nie zakada
kopiowania i wspdzielenia implementacji pomidzy egzemplarzami. Nie wszystkie
klasy, w ktrych mona by zastosowa prywatn implementacj, speniaj te warunki
(co nie znaczy, e w ogle nie mona zastosowa w nich wskanikw scoped_ptr,
wymaga to jednak rcznej implementacji operacji kopiowania i przypisywania implementacji). Dla takich klas, ktre zakadaj dzielenie szczegw implementacji
pomidzy wieloma egzemplarzami, naleaoby zarzdza wsplnymi implementacjami
za pomoc wskanikw shared_ptr. Kiedy wskanikowi shared_ptr przekazany zostanie w posiadanie egzemplarz implementacji prywatnej, zyskamy za darmo moliwo

54D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

55

kopiowania i przypisywania implementacji. W przypadku scoped_ptr kopiowanie i przypisywanie byo niedozwolone, bo semantyka wskanikw scoped_ptr nie dopuszcza
kopiowania. Z tego wzgldu obsuga kopiowania i przypisa w klasach stosujcych
scoped_ptr i utrzymujcych prywatne implementacje wsplne wymagaaby rcznego
definiowania operatora przypisania i konstruktora kopiujcego. Kiedy w roli wskanika prywatnej implementacji klasy wystpuje shared_ptr, nie trzeba udostpnia
wasnego konstruktora kopiujcego. Egzemplarz implementacji bdzie wspuytkowany przez obiekty klasy; jedynie w przypadku, kiedy ktry z egzemplarzy klasy ma
by wyrniony osobnym stanem, trzeba bdzie zdefiniowa stosowny konstruktor
kopiujcy. Sama obsuga implementacji prywatnej nie rni si poza tym od przypadku,
kiedy wskazywa j wskanik scoped_ptr: wystarczy zamieni w kodzie scoped_ptr
na shared_ptr.

shared_ptr a kontenery biblioteki standardowej jzyka C++


Przechowywanie obiektw wprost w kontenerze jest niekiedy kopotliwe. Przechowywanie przez warto oznacza, e uytkownicy kontenera wydobywajcy ze obiekty
otrzymuj ich kopie, co w przypadku obiektw cechujcych si wysokim kosztem
kopiowania moe znaczco wpywa na wydajno programu. Co wicej, niektre
kontenery, zwaszcza std::vector, podejmuj operacj kopiowania przechowywanych elementw w obliczu koniecznoci zmiany rozmiaru kontenera, co znw stanowi
dodatkowy, potencjalnie waki koszt. Wreszcie semantyka typowa dla wartoci oznacza brak zachowania polimorficznego. Jeli obiekty przechowywane w kontenerach
maj przejawia zachowanie polimorficzne, naleaoby skorzysta ze wskanikw.
Jeli bd to wskaniki zwyke, staniemy w obliczu powanego problemu zarzdzania
spjnoci wskaza w poszczeglnych elementach kontenera. Choby przy usuwaniu
elementw z kontenera trzeba bdzie sprawdzi, czy istniej jeszcze uytkownicy korzystajcy z kopii wskanikw wydobytych wczeniej z kontenera; trzeba te bdzie
koordynowa odwoania do tego samego elementu inicjowane przez rnych uytkownikw. Wszystkie te zoone zadania moe z powodzeniem przej shared_ptr.
Poniszy przykad ilustruje sposb przechowywania wspdzielonych wskanikw
w kontenerze biblioteki standardowej.
#include "boost/shared_ptr.hpp"
#include <vector>
#include <iostream>
class A {
public:
virtual void sing()=0;
protected:
virtual ~A() {};
};
class B : public A {
public:
virtual void sing() {
std::cout << "do re mi fa sol la";
}

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
55

Cz I Biblioteki oglnego przeznaczenia

56
};
boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new B());
return p;
}

int main() {
typedef std::vector<boost::shared_ptr<A> > container_type;
typedef container_type::iterator iterator;
container_type container;
for (int i=0;i<10;++i) {
container.push_back(createA());
}

std::cout << "Pr ba ch ru: \n";


iterator end=container.end();
for (iterator it=container.begin();it!=end;++it) {
(*it)->sing();
}

Dwie widniejce powyej klasy A i B zawieraj po jednej metodzie wirtualnej sing.


B dziedziczy publicznie po A, a funkcja wytwrcza createA zwraca dynamicznie przydzielany egzemplarz B ujty w obiekcie shared_ptr<A>. W funkcji main dochodzi do
utworzenia wektora elementw typu shared_ptr<A> i wypenienia go dziesicioma takimi elementami; nastpnie w ptli nastpuj wywoania metody sing na rzecz kadego elementu kontenera. Gdybymy stosowali tu wskaniki zwyke, musielibymy
rcznie zwalnia zawarto kontenera. W tym przykadzie zwalniany jest automatyczne;
licznik odwoa danego elementu ma warto rwn co najmniej 1 tak dugo, jak dugo
istnieje obiekt kontenera. Kiedy ten jest usuwany, liczniki odwoa elementw s zerowane, co wymusza zwolnienie obiektw wskazywanych. Warto odnotowa, e nawet
gdyby destruktor A nie zosta zadeklarowany jako wirtualny, shared_ptr poprawnie
wywoaby przy zwalnianiu obiektu wskazywanego destruktor B!
Przykad ilustruje efektywn technik angaujc chroniony destruktor klasy A. Poniewa funkcja createA zwraca obiekt klasy shared_ptr<A>, nie byoby moliwe proste
wywoanie delete dla wskanika wyuskanego wywoaniem metody shared_ptr::get.
Oznacza to, e gdyby pozyska wskanik z shared_ptr na przykad w celu przekazania go do funkcji wymagajcej argumentu prostego typu wskanikowego nie istniaaby moliwo ryzykownego i niebezpiecznego zwolnienia obiektu wskazywanego.
W jaki wic sposb shared_ptr radzi sobie z prawidowym zwolnieniem obiektu
wskazywanego? Ot z pomoc przychodzi waciwy typ wskanika, czyli B; za destruktor B nie jest zabezpieczony przed dostpem z zewntrz. To bardzo dobry sposb
dodatkowego zabezpieczania obiektw przechowywanych za pomoc wskanikw
shared_ptr.

56D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

57

Wskaniki shared_ptr a nietypowe zasoby


Niekiedy pojawia si potrzeba zastosowania wskanika shared_ptr z zasobem takiego
typu, e jego zwolnienie nie sprowadza si do prostego wywoania delete. Takie przypadki obsuguje si za porednictwem wasnych dealokatorw. Rzecz dotyczy na
przykad uchwytw zasobw systemowych, jak FILE*, ktre naleaoby zwalnia przy
uyciu mechanizmw systemu operacyjnego, np. wywoaniem funkcji fclose. Gdyby
wic wskanik shared_ptr mia przechowywa obiekty FILE*, naleaoby zdefiniowa
klas obiektw wykorzystywanych do zwalniania FILE*, jak poniej.
class FileCloser {
public:
void operator()(F# e* file) {
std::cout << "Wywoanie FileCloser uchwytu dla F# e* -- "
"plik zostanie zamknity. \n";
if (file!=0)
fclose(file);
}
};

Powyszy obiekt funkcyjny suy do realizacji specyficznego trybu zwalniania obiektu


wskazywanego, polegajcego na wywoaniu funkcji fclose. Oto program przykadowy,
wykorzystujcy taki obiekt zwalniajcy.
int main() {
std::cout << "wskanik shared_ptr z wasnym dealokatorem.\n";
{
F# e* f=fopen("test.txt", "r");
if (f==0) {
std::cout << "Nie mona otworzy pliku\n";
throw "Nie mona otworzy pliku";
}
boost::shared_ptr<F# e>
my_shared_file(f, FileCloser());
// Pozycjonowanie kursora pliku
fseek(my_shared_file.get(), 42, dees_deT);

}
std::cout << "Plik zosta przed chwil zamknity!\n";

Zauwamy, e pozyskanie zasobu ze wskanika wymagao zastosowania skadni &*,


metody get albo get_pointer klasy shared_ptr (oczywicie protestuj przeciwko stosowaniu wyjtkowo nieczytelnego zapisu &*; mniej oczywisty jest wybr pomidzy
dwoma pozostaymi sposobami). Przykad mgby by jeszcze prostszy skoro
przy dealokacji zasobu wystarczyo wywoanie funkcji jednoargumentowej, nie trzeba
w ogle definiowa wasnej klasy dealokatorw. Uproszczony przykad mgby wyglda tak:

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
57

Cz I Biblioteki oglnego przeznaczenia

58

F# e* f=fopen("test.txt", "r");
if (f==0) {
std::cout << "Nie mona otworzy pliku\n";
throw file_exceptio();
}
boost::shared_ptr<F# e>
my_shared_file(f, &fclose);
// Pozycjonowanie kursora pliku
fseek(my_shared_file.get(), 42, dees_deT);

}
std::cout << "Plik zosta przed chwil zamknity!\n";

Wasne dealokatory s nieodzowne w przypadku zasobw, ktrych zwalnianie wymaga wdroenia specjalnej procedury. Poniewa taki obiekt nie stanowi czci typu
shared_ptr, uytkownicy nie musz posiada adnej wiedzy o zasobie pozostajcym w posiadaniu inteligentnego wskanika (musz, rzecz jasna, jedynie wiedzie,
jak tego wskanika uywa!). W przykadowym przypadku puli obiektw-zasobw
dealokator zwracaby po prostu zasb do puli. Z kolei w przypadku zasobu-jedynaka
(ang. singleton) dealokator powstrzymywaby si od jakichkolwiek operacji, zapewniajc podtrzymanie obecnoci jedynego egzemplarza zasobu.

Dealokatory a bezpieczestwo
Wiemy ju, e zabezpieczanie destruktora klasy zwiksza jej bezpieczestwo w poczeniu ze wskanikami shared_ptr. Innym sposobem osignicia podobnego poziomu
bezpieczestwa jest zadeklarowanie destruktora jako zabezpieczonego (bd prywatnego) i wykorzystanie wasnego dealokatora. Musi on zosta zaprzyjaniony z klas,
ktrej obiekty ma usuwa. Elegancki sposb implementacji takiego dealokatora polega
na zagniedeniu jego klasy w prywatnej czci klasy obiektw usuwanych, jak tutaj:
#include "boost/shared_ptr.hpp"
#include <iostream>
class A {
class deleter {
public:
void operator()(A* p) {
delete p;
}
};
friend class deleter;
public:
virtual void sing() {
std::cout << " alalalalalalalalalala";
}
static boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new A(), A::deleter());
return p;
}

58D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

59

protected:
virtual ~A() {};
};
int main() {
boost::shared_ptr<A> p=A::createA();
}

Zauwamy, e tym razem nie moemy tworzy obiektw shared_ptr<A> za pomoc


zewntrznej funkcji wytwrczej, bo zagniedona klasa dealokatora jest prywatna
wzgldem A. Taka implementacja uniemoliwia uytkownikom tworzenie obiektw A
na stosie i nie pozwala na wywoywanie delete ze wskanikiem A.

Tworzenie wskanika shared_ptr ze wskanika this


Niekiedy trzeba utworzy wskanik inteligentny shared_ptr ze wskanika this co
oznacza zaoenie, e klasa bdzie zarzdzana za porednictwem wskanika inteligentnego i trzeba w jaki sposb przekonwertowa wskanik obiektu klasy na taki
shared_ptr. Brzmi jak niemoliwe? C, rozwizaniem jest kolejne narzdzie z zestawu wskanikw inteligentnych, ktre jeszcze nie doczekao si szerszego omwienia chodzi o boost::weak_ptr. Ot wskanik weak_ptr jest obserwatorem wskanikw shared_ptr; pozwala na podgldanie wskazania bez ingerowania w warto
licznika odwoa. Zachowanie w klasie skadowej typu weak_ptr ze wskanikiem
this pozwala na pniejsze pozyskiwanie shared_ptr do this wedle potrzeb. Aby nie
trzeba byo za kadym razem rcznie pisa kodu skadujcego this w weak_ptr i potem pozyskiwa ze wskanik shared_ptr, w bibliotece Boost.Smart_ptr przewidziano
klas pomocnicz o nazwie enable_shared_from_this. Wystarczy wic wasn klas
wyprowadzi jako pochodn enable_shared_from_this. Oto przykad ilustrujcy zastosowanie klasy pomocniczej:
#include "boost/shared_ptr.hpp"
#include "boost/enable_shared_from_this.hpp"
class A;
void do_stuff(boost::shared_ptr<A> p) {

}
class A : public boost::enable_shared_from_this<A> {
public:
void call_do_stuff() {
do_stuff(shared_from_this());
}
};
int main() {
boost::shared_ptr<A> p(new A());
p->call_do_stuff();
}

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
59

Cz I Biblioteki oglnego przeznaczenia

60

Przykad pokazuje te przypadek, w ktrym do zarzdzania this potrzebny jest inteligentny wskanik shared_ptr. Ot klasa A posiada metod call_do_stuff, ktra ma
wywoa funkcj zewntrzn (wobec klasy) do_stuff, ktra z kolei oczekuje przekazania argumentu typu boost::shared_ptr<A>. W obrbie metody A::call_do_stuff
wskanik this jest najzwyklejszym wskanikiem A, ale poniewa A dziedziczy po
enable_shared_from_this, wywoanie shared_from_this zwraca wskanik this opakowany w shared_ptr. W shared_from_this, metodzie klasy pomocniczej enable_
shared_from_this, wewntrznie przechowywany weak_ptr jest konwertowany na shared_ptr, co polega na zwikszeniu licznika referencji, niezbdnego w celu zachowania
istnienia obiektu.

Podsumowanie
Wskaniki inteligentne ze zliczaniem odwoa to niezwykle cenne narzdzia. Implementacja shared_ptr z biblioteki Boost to implementacja solidna i elastyczna, ktra
swojej przydatnoci i jakoci dowioda w wyczerpujcych testach praktycznych,
w niezliczonych aplikacjach, rodowiskach i okolicznociach. Potrzeba wspuytkowania zasobw przez wielu uytkownikw jest bowiem do powszechna i najczciej wie si z niemonoci albo trudnoci ustalenia waciwego momentu bezpiecznego zwolnienia zasobu. Wskanik shared_ptr zwalnia uytkownikw z tej troski,
automatycznie opniajc zwalnianie obiektu do momentu zlikwidowania ostatniego
odwoania. Z tego wzgldu klas shared_ptr naleaoby uzna za najwaniejsz kategori wskanikw inteligentnych dostpnych w bibliotekach Boost. Oczywicie pozostae klasy wskanikw inteligentnych rwnie naley pozna, ale ta jedna jest z pewnoci najbardziej uyteczna i najbardziej warta przyswojenia i wdraania. Dodatkowa
moliwo stosowania wasnych procedur usuwania zasobw wskazywanych czyni
klas shared_ptr uniwersalnym narzdziem obsugi aspektw zarzdzania zasobami.
Wskaniki shared_ptr cechuj si niewielkim narzutem rozmiaru wzgldem wskanikw zwykych. Nie spotkaem si jeszcze osobicie z sytuacj, w ktrej ten narzut
zmuszaby programist do rezygnacji z wdroenia wskanikw shared_ptr. Doprawdy,
nie warto rcznie implementowa wasnych klas wskanikw zliczajcych odwoania
praktycznie zawsze lepiej skorzysta z gotowca w postaci shared_ptr; nie bardzo
jest jak go udoskonali.
Wskaniki shared_ptr mona skutecznie stosowa:
t tam, gdzie jest wielu uytkownikw obiektu, ale nie ma jednego jawnego

waciciela;
t tam, gdzie trzeba przechowywa wskaniki w kontenerach biblioteki

standardowej;
t tam, gdzie trzeba przekazywa wskaniki do i z bibliotek, a nie ma jawnego

wyraenia transferu wasnoci;


t tam, gdzie zarzdzanie zasobami wymaga specjalnych procedur

zwalniajcych9.

Przy pomocy wasnych klas obiektw usuwajcych przyp. aut.

60D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

61

shared_array
Nagwek:

"boost/shared_array.hpp"

Klasa shared_array to klasa inteligentnego wskanika umoliwiajca wspuytkowanie


tablic. Ma si do shared_ptr tak, jak scoped_array do scoped_ptr. Klasa shared_array
rni si od shared_ptr gwnie tym, e suy do zarzdzania nie pojedynczymi obiektami, a caymi tablicami obiektw. Przy omawianiu scoped_array wspominaem, e
w zdecydowanej wikszoci przypadkw lepszym od niego wyborem jest klasyczny
kontener biblioteki standardowej std::vector. W tym przypadku przewaga jest po
stronie shared_array, bo klasa ta pozwala na dzielenie prawa wasnoci tablic. Interfejs shared_array przypomina interfejs shared_ptr; jedynym istotnym dodatkiem jest
operator indeksowania i brak obsugi wasnych dealokatorw.
Poniewa kombinacja wskanika shared_ptr i kontenera std::vector (a konkretnie
wskanika shared_ptr na wektor std::vector) cechuje si wiksz elastycznoci ni
shared_array, nie bdziemy ilustrowa zastosowa shared_array przykadami. Kto
musi skorzysta z tej klasy, powinien sign do dokumentacji biblioteki Boost.

intrusive_ptr
Nagwek:

"boost/intrusive_ptr.hpp"

Klasa intrusive_ptr jest ingerencyjnym odpowiednikiem inteligentnego wskanika


shared_ptr. Niekiedy nie ma wyjcia i trzeba zastosowa wanie ingerencyjny inteligentny wskanik zliczajcy odwoania. Typowym scenariuszem takiej sytuacji jest
obecno gotowego kodu z wewntrznym licznikiem odwoa, ktrego z braku czasu
(albo innych wzgldw) nie mona przepisa. Moe te chodzi o przypadek, kiedy
rozmiar inteligentnego wskanika musi dokadnie zgadza si z rozmiarem wskanika
goego albo kiedy operacje przydziau licznikw odwoa wskanikw shared_ptr
degraduj wydajno (to bardzo rzadki przypadek!). Konieczno zastosowania ingerencyjnego wskanika inteligentnego jest oczywista, kiedy metoda klasy wskazywanej ma zwraca this tak, aby dao si tego wskanika uy w innym wskaniku inteligentnym (co prawda wiemy ju, e takie zadanie mona te rozwiza przy uyciu
wskanikw nieingerencyjnych). Klasa intrusive_ptr rni si od pozostaych wskanikw inteligentnych tym, e to uytkownik podaje licznik odwoa, ktrym wskanik
bdzie manipulowa.
Kiedy wskanik intrusive_ptr zwiksza bd zmniejsza licznik odwoa dla wskanika niepustego, realizuje niekwalifikowane wywoania funkcji intrusive_ptr_add_ref
i intrusive_ptr_release. Funkcje te maj zapewni poprawno wartoci licznika
odwoa i ewentualne usunicie obiektu wskazywanego, kiedy licznik zostanie wyzerowany. Trzeba wic przeciy te funkcje dla wasnego typu.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
61

Cz I Biblioteki oglnego przeznaczenia

62

Oto czciowa deklaracja szablonu intrusive_ptr, prezentujca najwaniejsze metody i funkcje zewntrzne:
namespace boost {
template<class T> class intrusive_ptr {
public:
intrusive_ptr(T* p, bool add_ref=true);
intrusive_ptr(const intrusive_ptr& r);
~intrusive_ptr();
T& operator*() const;
T* operator->() const;
T* get() const;
};

operator nieokrelony-typ-logiczny() const;

template <class T> T* get_pointer(const intrusive_ptr<T>& p);

template <class T,class > intrusive_ptr<T>


static_pointer_cast(const intrusive_ptr< >& r);

Metody
intrusive_ptr(T* p, bool add_ref=true);

Konstruktor zachowujcy wskanik p w *this. Jeli wskanik p jest niepusty i jeli


add_ref ma warto true, konstruktor inicjuje niekwalifikowane wywoanie intrusive_ptr_add_ref(p). Jeli add_ref ma warto false, konstruktor rezygnuje z wywoania funkcji intrusive_ptr_add_ref. Zrzucanie wyjtkw przez konstruktor jest
uzalenione od intrusive_ptr_add_ref konstruktor moe zrzuci wyjtek, jeli intrusive_ptr_add_ref moe zrzuci wyjtek.
intrusive_ptr(const intrusive_ptr& r);

Konstruktor kopiujcy zachowuje kopi wskanika zwracanego wywoaniem r.get()


i jeli jest to wskanik niepusty wywouje dla niego funkcj intrusive_ptr_
add_ref. Konstruktor nie zrzuca wyjtkw.
~intrusive_ptr();

Jeli przechowywany wskanik jest niepusty, destruktor intrusive_ptr podejmuje niekwalifikowane wywoanie funkcji intrusive_ptr_release, przekazujc przechowywany
wskanik jako argument wywoania. Funkcja intrusive_ptr_add_ref jest odpowiedzialna za zmniejszenie licznika odwoa i ewentualne zwolnienie obiektu wskazywanego (jeli licznik zostanie wyzerowany). Funkcja nie zrzuca wyjtkw.

62D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

63

T& operator*() const;

Operator wyuskania operator* zwraca referencj obiektu wskazywanego przez przechowywany wskanik. Jeli wskanik jest pusty, wywoanie operatora prowokuje niezdefiniowane zachowanie. Kiedy wic s wtpliwoci co do stanu wskazania, naley
si wczeniej upewni, czy intrusive_ptr zawiera niepusty wskanik. Mona to zrobi
albo za porednictwem metody get, albo poprzez testowanie obiektu intrusive_ptr
w wyraeniu logicznym. Operator wyuskania nie zrzuca wyjtkw.
T* operator->() const;

Operator zwraca przechowywany wskanik. Wywoanie operatora, jeli wskanik jest


pusty, prowokuje niezdefiniowane zachowanie. Operator nie zrzuca wyjtkw.
T* get() const;

Metoda zwraca przechowywany wskanik. Moe by skutecznie wywoywana wszdzie tam, gdzie potrzebny jest goy wskanik, nawet jeli wskanik przechowywany
jest wskanikiem pustym. Metoda get nie zrzuca wyjtkw.
operator nieokrelony-typ-logiczny() const;

Niejawna konwersja na typ zdatny do stosowania w wyraeniach logicznych. Typ


wartoci zwracanej to nie bool, bo taki typ pozwalaby na angaowanie wskanikw
intrusive_ptr w niektrych bezsensownych dla jego semantyki operacjach. Konwersja
pozwala na testowanie wartoci wskanika intrusive_ptr w kontekstach wyrae logicznych, np. if (p) (gdzie p to egzemplarz intrusive_ptr). Warto zwracana to
true, kiedy intrusive_ptr zawiera wskanik niepusty, i false, kiedy wskazanie jest
puste. Konwersja nie prowokuje wyjtkw.

Funkcje zewntrzne
template <class T> T* get_pointer(const intrusive_ptr<T>& p);

Funkcja zwraca warto p.get(), a jej rol jest gwnie wsparcie dla programowania
uoglnionego10. Moe te by wykorzystywana w konwencjach kodowania zakadajcych moliwo przecienia jej dla wskanikw goych i zewntrznych klas wskanikw inteligentnych. Niektrzy za po prostu preferuj wywoania funkcji zewntrznych przed wywoaniami metod na rzecz obiektw11. Funkcja nie zrzuca wyjtkw.

10
11

Takim funkcjom nadano miano podkadek (ang. shims) zobacz 12. pozycj bibliografii przyp. aut.
Uzasadnienie sprowadza si do zaciemnienia rozrnienia pomidzy operacjami na wskanikach
inteligentnych a operacjami na obiektach wskazywanych. Na przykad wywoania p.get() i p->get()
maj w przypadku wskanikw inteligentnych zupenie odmienne znaczenie, a rozrnienie jest
na pierwszy rzut oka mao wyrane; dla porwnania, wywoa get_pointer(p) i p->get() nie da si
mylnie zinterpretowa. Rzecz sprowadza si jednak raczej do konwencji i nawykw ni faktycznej
wyszoci jednej postaci nad drug przyp. aut.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
63

Cz I Biblioteki oglnego przeznaczenia

64
template <class T,class > intrusive_ptr<T>
static_pointer_cast(const intrusive_ptr< >& r);

Funkcja zwraca intrusive_ptr<T>(static_cast<T*>(r.get())). Inaczej ni w przypadku shared_ptr, wskaniki obiektw przechowywane w intrusive_ptr mona miao
poddawa rzutowaniu static_cast. Funkcja ta ma jednak ujednolica skadni rzutowania wszystkich wskanikw inteligentnych. Funkcja static_pointer_cast nie zrzuca
wyjtkw.

Stosowanie
Stosowanie wskanikw intrusive_ptr rni si od stosowania wskanikw shared_ptr w dwch zasadniczych aspektach. Po pierwsze, trzeba samodzielnie udostpni mechanizm zliczania odwoa. Po drugie, wskanik this mona z powodzeniem
traktowa jako wskanik inteligentny12, co czsto okazuje si wygodne (o czym bdziemy si mogli za chwil przekona). Niemniej jednak w wikszoci przypadkw
waciwym wskanikiem inteligentnym jest nieingerencyjny wskanik shared_ptr.

Stosowanie klasy intrusive_ptr wymaga wczenia do kodu pliku nagwkowego "boost/intrusive_ptr.hpp" i zdefiniowania pary funkcji intrusive_ptr_add_ref
i intrusive_ptr_release. Powinny one przyjmowa pojedynczy argument bdcy
wskanikiem typu wykorzystywanego ze wskanikami intrusive_ptr. Ewentualne
wartoci zwracane z tych funkcji s ignorowane. Zwykle dobrze jest sparametryzowa obie funkcje typem wskanika i w ramach ich implementacji przekazywa wywoanie do odpowiedniej metody konkretnego typu (np. metody add_ref czy release
klas wskazywanych). Kiedy licznik odwoa osignie warto zerow, funkcja intrusive_ptr_release powinna zadba o zwolnienie obiektu wskazywanego. Oto uoglniona implementacja obu funkcji:
template <typename T> void intrusive_ptr_add_ref(T* t) {
t->add_ref();
}
template <typename T> void intrusive_ptr_release(T* t) {
if (t->release() <= 0)
delete t;
}

Zauwamy, e funkcje powinny by definiowane w zasigu odpowiednim dla typu ich


argumentw. Oznacza to, e w wywoaniach funkcji z argumentami typu zagniedonego
w przestrzeni nazw funkcje naley zdefiniowa w teje przestrzeni nazw. Wanie dlatego wywoania inicjowane z klasy intrusive_ptr s niekwalifikowane umoliwia
to dopasowywanie typw argumentw; w niektrych przypadkach moe pojawi si
potrzeba udostpnienia wikszej liczby wersji funkcji, przez co funkcji nie naley definiowa w globalnej przestrzeni nazw. Przykad rozmieszczenia funkcji zobaczymy
niebawem; tymczasem musimy zadba o udostpnienie jakiego licznika odwoa.
12

Nie jest to moliwe w przypadku shared_ptr, o ile nie stosuje si specjalnych rodkw, np. w postaci
klasy enable_shared_from_this przyp. aut.

64D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

65

Udostpnianie licznika odwoa


Po zdefiniowaniu funkcji manipulujcych licznikiem odwoa wypadaoby udostpni
sam licznik. W prezentowanym przykadzie licznik bdzie prywatn skadow klasy
licznika, inicjalizowanym zerem; klasa bdzie ponadto udostpnia metody add_ref
i release, operujce na liczniku. Metoda add_ref bdzie zwiksza licznik odwoa,
a metoda release zmniejsza go13. Moglibymy uzupeni definicj licznika o trzeci metod, zwracajc biec liczb odwoa, wystarczy jednak, jeli liczb t bdzie
zwraca metoda release. Ponisza klasa bazowa licznika reference_counter implementuje licznik i obie postulowane metody; uzupenienie obiektw wskazywanych
o liczniki polega na dziedziczeniu po tej klasie.
class reference_counter {
int ref_count_;
public:
reference_counter() : ref_count_(0) {}
virtual ~reference_counter() {}
void add_ref() {
++ref_count_;
}
int release() {
return --ref_count_;
}

};

protected:
reference_counter& operator=(const reference_counter&) {
// Atrapa
return *this;
}
private:
// Blokada dostpu do konstruktora kopiujcego
reference_counter(const reference_counter&);

Przyczyn oznaczenia destruktora klasy reference_counter sowem virtual jest to,


e klasa ma suy jako klasa bazowa w dziedziczeniu publicznym, co z kolei wymaga, aby wskanik reference_counter pozwala na zwolnienie obiektu klasy pochodnej
wywoaniem delete. Owe zwolnienie powinno wywoa destruktor na rzecz obiektu
klasy pochodnej. Implementacja licznika jest wyjtkowo nieskomplikowana: metoda
add_ref zwiksza licznik odwoa, a release zmniejsza go i zwraca jego now warto.
Aby skorzysta z licznika odwoa, wystarczy wyprowadzi z niego (dziedziczeniem
publicznym) klas obiektw wskazywanych. Oto przykadowa klasa some_class zawierajca wewntrzny licznik odwoa i program operujcy na obiektach tej klasy za
porednictwem wskanikw intrusive_ptr:
13

W rodowisku wielowtkowym wszelkie operacje na zmiennej przechowujcej licznik odwoa


powinny by odpowiednio synchronizowane przyp. aut.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
65

Cz I Biblioteki oglnego przeznaczenia

66

#include <iostream>
#include "boost/intrusive_ptr.hpp"
class some_class : public reference_counter {
public:
some_class() {
std::cout << "some_class::some_class()\n";
}
some_class(const some_class& other) {
std::cout << "some_class(const some_class& other)\n";
}

};

~some_class() {
std::cout << "some_class::~some_class()\n";
}

int main() {
std::cout << "Przed wejciem do zasigu\n";
{
boost::intrusive_ptr<some_class> p1(new some_class());
boost::intrusive_ptr<some_class> p2(p1);
}
std::cout << "Po wyjciu z zasigu\n";
}

Wspdziaanie klasy intrusive_ptr z funkcjami intrusive_ptr_add_ref i intrusive_ptr_release ilustruje wynik uruchomienia powyszego programu:
Przed wejciem do zasigu
some_class::some_class()
some_class::~some_class()
Po wyjciu z zasigu

Wskaniki intrusive_ptr zwalniaj nas z szeregu obowizkw. Przy tworzeniu pierwszego takiego wskanika (p1) otrzymuje on nowy egzemplarz klasy some_class. Konstruktor intrusive_ptr przyjmuje faktycznie a dwa argumenty. Drugi to warto typu
bool, okrelajca, czy przy konstrukcji ma nastpi wywoanie intrusive_ptr_add_ref.
Poniewa domylna warto tego argumentu to true, konstrukcja p1 wie si ze
zwikszeniem licznika odwoa do tego egzemplarza some_class o jeden. Dalej
mamy konstrukcj drugiego wskanika intrusive_ptr: p2. Powstaje on jako kopia p1;
kiedy konstruktor p2 sprawdzi, e p1 odnosi si do niepustego wskanika, wywoa intrusive_ptr_add_ref, zwikszajc licznik odwoa do 2. Potem, wraz z kocem zasigu, koczy si czas ycia obu wskanikw. Jako pierwszy usuwany jest p2, a jego
destruktor wywouje intrusive_ptr_release, zmniejszajc licznik odwoa do 1. Nastpnie usuwany jest wskanik p1 i w ramach jego destruktora znw nastpuje wywoanie intrusive_ptr_release, ktre tym razem zeruje licznik odwoa; wedle naszej wasnej definicji intrusive_ptr_release nastpuje wtedy wywoanie delete dla
wskanika przechowywanego. Wypada zaznaczy, e implementacja reference_counter

66D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

67

nie jest zabezpieczona przed ryzykiem zwizanym z wielowtkowoci i jako taka nie
powinna by stosowana w aplikacjach wielowtkowych, chyba e w otoczce odpowiedniej synchronizacji.
Zamiast polega na uoglnionych implementacjach funkcji intrusive_ptr_add_ref
i intrusive_ptr_release, moglibymy operowa w nich bezporednio na klasie bazowej licznika (tu reference_counter). Zalet takiego podejcia jest to, e nawet jeli
klasy wyprowadzone z reference_counter bd definiowane w innych przestrzeniach
nazw, wywoania intrusive_ptr_add_ref i intrusive_ptr_release bd mogy by
realizowane przy uyciu regu ADL (wyszukiwania funkcji kandydujcych na bazie
typw argumentw). Stosowna zmiana implementacji reference_counter nie byaby
wcale skomplikowana:
class reference_counter {
int ref_count_;
public:
reference_counter() : ref_count_(0) {}
virtual ~reference_counter() {}
friend void intrusive_ptr_add_ref(reference_counter* p) {
++p->ref_count_;
}
friend void intrusive_ptr_release() {
if (--p->ref_count_==0)
delete p;
}

};

protected:
reference_counter& operator=(const reference_counter&) {
// Atrapa
return *this;
}
private:
// Blokada dostpu do konstruktora kopiujcego
reference_counter(const reference_counter&);

Traktowanie this jako wskanika inteligentnego


Wcale nie atwo wymyli taki scenariusz, w ktrym zastosowanie inteligentnego wskanika ingerencyjnego byoby faktycznie niezbdne. Wikszo (jeli nie wszystkie)
problemw da si rozwiza z uyciem wskanikw nieingerencyjnych. Jest jednak
sytuacja, w ktrej atwiej zastosowa zliczanie ingerencyjne: kiedy trzeba zwrci this
z metody, a zwrcony wskanik ma zosta przechwycony do innego inteligentnego
wskanika. Zwracanie this z obiektu typu pozostajcego w posiadaniu nieingerencyjnego wskanika inteligentnego prowokuje sytuacj, w ktrej dwa takie wskaniki
sdz, e s wacicielami tego samego obiektu, przez co oba bd prbowa jego
zwolnienia. A wiadomo, e dwukrotne zwolnienie wskanika moe doprowadzi do

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
67

Cz I Biblioteki oglnego przeznaczenia

68

krachu aplikacji. Trzeba jako powiadomi w drugi inteligentny wskanik o tym, e


zwracany zasb podlega ju pod inny wskanik inteligentny tu wanie wietnie
sprawdza si wewntrzny licznik odwoa. Poniewa logika wskanika intrusive_ptr
operuje porednio na wewntrznym liczniku odwoa do przechowywanego obiektu,
nie ma mowy o ewentualnej niespjnoci zliczania odwoa bo dochodzi do prostego zwikszenia licznika.
Przyjrzyjmy si potencjalnie problematycznej sytuacji, gdzie wspdzielenie zasobu
jest realizowane przy uyciu wskanikw shared_ptr. Bdzie to zasadniczo powtrzenie jednego z poprzednich przykadw, ilustrujcego stosowanie klasy pomocniczej enable_shared_from_this.
#include "boost/shared_ptr.hpp"
class A;
void do_stuff(boost::shared_ptr<A> p) {
//
}
class A {
public:
void call_do_stuff() {
shared_ptr<A> p(???);
do_stuff(p);
}
};
int main() {
boost::shared_ptr<A> p(new A());
p->call_do_stuff();
}

Klasa A zamierza wywoa ze swojej metody funkcj do_stuff, ale do_stuff oczekuje
przekazania wskanika shared_ptr<A>, a nie zwykego wskanika klasy A, jakim jest
this. Jak wic utworzy shared_ptr we wntrzu A::call_do_stuff? Sprbujmy przepisa definicj klasy A, tak aby poprzez dziedziczenie po reference_counter uzdatni
j do wewntrznego zliczania odwoa (intrusive_ptr), a potem doda do programu
przecion wersj do_stuff, przyjmujc argument typu intrusive_ptr<A>:
#include "boost/intrusive_ptr.hpp"
class A;
void do_stuff(boost::intrusive_ptr<A> p) {
//
}
void do_stuff(boost::shared_ptr<A> p) {
//
}

68D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

69

class A : public_reference_counter {
public:
void call_do_stuff() {
do_stuff(this);
}
};
int main() {
boost::intrusive_ptr<A> p(new A());
p->call_do_stuff();
}

Jak wida, w tej wersji A::call_do_stuff moemy przekaza this bezporednio do


funkcji oczekujcej wskanika intrusive_ptr<A>, a to dziki konstruktorowi konwertujcemu klasy intrusive_ptr.
Na zakoczenie co specjalnego: teraz A nadaje si do stosowania ze wskanikami intrusive_ptr i moemy napisa kod, ktry ujby wskanik intrusive_ptr<A> we wskaniku shared_ptr, tak aby mona byo wywoa pierwotn wersj funkcji do_stuff,
wymagajcej argumentu typu shared_ptr<A>. Gdybymy nie mogli kontrolowa kodu
rdowego funkcji do_stuff, byby to faktyczny problem. Rozwizanie ma posta
wasnego dealokatora, ktry wie o potrzebie wywoania intrusive_ptr_release. Oto
nowa wersja A::call_do_stuff:
void call_do_stuff() {
intrusive_ptr_add_ref(this);
boost::shared_ptr<A> p(this, &intrusive_ptr_release<A>);
do_stuff(p);
}

Doprawdy eleganckie rozwizanie. Kiedy nie bdzie ju adnego wskanika shared_ptr,


wywoana zostanie funkcja usuwajca, czyli intrusive_ptr_release, ktra z kolei
zmniejszy wewntrzny licznik odwoa A. Gdyby z kolei funkcje intrusive_ptr_add_ref
i intrusive_ptr_release odwoyway si bezporednio do reference_counter (jak w drugim wariancie implementacji), obiekt shared_ptr konstruowalibymy tak:
boost::shared_ptr<A> p(this, &intrusive_ptr_release);

Obsuga rnych licznikw odwoa


Rozwaalimy wczeniej moliwo obsugiwania rnych licznikw odwoa dla
rnych typw. Moe to by niezbdne przy integrowaniu istniejcych klas z rnymi
mechanizmami zliczania odwoa (na przykad klas udostpnianych przez osoby trzecie i wdraajcych wasne wersje licznikw odwoa). Dotyczy to rwnie sytuacji
rnych wymaga dla operacji zwalniania zasobu, np. przez zastpienie prostego
wywoania delete wywoaniem specjalizowanego dealokatora. Wiemy ju, e wywoania intrusive_ptr_add_ref i intrusive_ptr_release s wywoaniami niekwalifikowanymi. Oznacza to, e zasig typu argumentu (typu wskanikowego) ma wpyw

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
69

Cz I Biblioteki oglnego przeznaczenia

70

na dobr funkcji kandydujcych do wywoania przecionego i e te funkcje kandydujce powinny wobec tego by definiowane w tym samym zasigu, w ktrym
definiowany jest typ, na ktrym maj operowa. Implementacja uoglnionej wersji
intrusive_ptr_add_ref i intrusive_ptr_release w globalnej przestrzeni nazw uniemoliwiaaby utworzenie ich uoglnionych wersji w innych przestrzeniach nazw. Jeli
na przykad dana przestrze nazw wymaga specjalnej wersji tych funkcji dla wszystkich definiowanych w niej typw, trzeba by udostpni specjalizacje albo wersje
przecione dla wszystkich tych typw. Dlatego wanie nie jest podane definiowanie wersji uoglnionych obu funkcji w globalnej przestrzeni nazw; nie ma za to adnych przeciwwskaza dla wersji uoglnionych w poszczeglnych przestrzeniach nazw
waciwych dla typw argumentw.
Z racji sposobu implementowania licznika odwoa (przy uyciu klasy bazowej reference_counter) dobrym pomysem byoby udostpnienie w globalnej przestrzeni nazw
zwykej funkcji akceptujcej argument typu reference_counter*. Nie blokowaoby to
moliwoci przeciania wersjami uoglnionymi w poszczeglnych przestrzeniach
nazw. W ramach przykadu rozwamy klasy another_class i derived_class w przestrzeni nazw my_namespace:
namespace my_namespace {
class another_class : public reference_counter P
public:
void call_before_destruction() const {
std::cout <<
" otowy przed usuniciem\n";
}
};
class derived_class : public another_class {};
template <typename T> void intrusive_ptr_add_ref(T* t) {
t->add_ref();
}

template <typename T> void intrusive_ptr_release(T* t) {


if (t->release() <= 0) {
t->call_before_destruction();
delete t;
}
}

Mamy tu uoglnione wersje intrusive_ptr_add_ref i intrusive_ptr_release. Musimy wic usun wersje uoglnione tych funkcji z globalnej przestrzeni nazw i zastpi je wersjami zwykymi (nieszablonowymi), akceptujcymi w roli argumentu
wskanik klasy reference_counter. Mona by te w ogle pomin te funkcje w globalnej przestrzeni nazw, unikajc jej zamiecania. Implementacja tych funkcji z przestrzeni my_namespace zakada, e dla dwch klas my_namespace::another_class
i my_namespace::derived_class przy usuwaniu ich egzemplarzy wywoywana jest specjalna funkcja call_before_destruction. Dla innych typw, definiowanych w innych

70D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

71

przestrzeniach nazw, mona dalej rnicowa zachowanie wersji uoglnionych albo


korzysta z wersji z globalnej przestrzeni nazw, jeli takowe tam istniej. Oto krtki program ilustrujcy t koncepcj:
int main() {
boost::intrusive_ptr<my_namespace::another_class>
p1(new my_namespace::another_class());
boost::intrusive_ptr<A>
p2(new good_class());
boost::intrusive_ptr<my_namespace::derived_class>
p3(new my_namespace::derived_class());
}

Do konstruktora pierwszego wskanika intrusive_ptr przekazywany jest nowy egzemplarz klasy my_namespace::another_class. Przy rozstrzyganiu wywoania funkcji
intrusive_ptr_add_ref kompilator odnajduje wersj z przestrzeni nazw my_namespace,
czyli przestrzeni nazw waciwej dla typu argumentu: my_namespace::another_class*.
Nastpuje wic jak najbardziej prawidowe wywoanie wersji uoglnionej z teje
przestrzeni nazw. Dotyczy to rwnie wywoania intrusive_ptr_release. Jako drugi
tworzony jest wskanik p2; do konstruktora przekazywany jest wskanik klasy A. Klasa
ta wystpuje w globalnej przestrzeni nazw, wic kiedy kompilator szuka najlepszego
dopasowania wywoania intrusive_ptr_add_ref, znajduje tylko jedn wersj, akceptujc argument typu reference_counter (usunlimy przecie z globalnej przestrzeni
nazw wersje uoglnione intrusive_ptr_add_ref i intrusive_ptr_release). Poniewa
A dziedziczy publicznie po klasie reference_counter, zachodzi niejawna konwersja
i kompilator moe pomylnie zrealizowa wywoanie. Wreszcie przy tworzeniu egzemplarza klasy my_namespace::derived_class wykorzystywana jest uoglniona wersja funkcji z przestrzeni nazw my_namespace, zupenie jak poprzednio przy tworzeniu
obiektu my_namespace::another_class.
Z tego wszystkiego naley zapamita, e przy implementowaniu funkcji intrusive_ptr_add_ref i intrusive_ptr_release powinnimy je definiowa zawsze w tych
przestrzeniach nazw, z ktrych pochodz typy, na ktrych te funkcje maj operowa.
Ma to rwnie uzasadnienie czysto projektowe: powizane elementy projektu naley
przecie grupowa; do tego wsplnota przestrzeni nazw zapewnia kadorazowo wywoanie poprawnej wersji funkcji, niezalenie od istnienia wielu alternatywnych implementacji.

Podsumowanie
W wikszoci sytuacji korzystanie z boost::intrusive_ptr nie jest najlepszym pomysem, bo do wyboru jest implementacja inteligentnych wskanikw zliczajcych odwoania i nieingerencyjnych, w postaci boost::shared_ptr. Wskaniki nieingerencyjne
s elastyczniejsze od ingerencyjnych. Jednak zdarza si, e trzeba zastosowa wanie
ingerencyjne zliczanie odwoa, na przykad ze wzgldu na zastan w kodzie infrastruktur albo ch integracji z klasami zewntrznymi. Wtedy mona miao korzysta z klasy intrusive_ptr, korzystajc z podobiestwa zachowania wskanikw tej
klasy do pozostaych klas inteligentnych wskanikw biblioteki Boost. Korzystanie

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
71

Cz I Biblioteki oglnego przeznaczenia

72

z inteligentnych wskanikw Boost zapewnia spjno interfejsu we wszystkich przypadkach (rwnie wskanikw ingerencyjnych). Dla klas przeznaczonych do uycia
ze wskanikami intrusive_ptr naley przewidzie licznik odwoa. Sam wskanik
intrusive_ptr manipuluje udostpnionym licznikiem za porednictwem niekwalifikowanych wywoa dwch funkcji: intrusive_ptr_add_ref i intrusive_ptr_release;
powinny one odpowiednio manipulowa wartoci licznika odwoa i w razie potrzeby zwalnia obiekt wskazywany przez intrusive_ptr. W przypadku typw, ktre posiadaj ju gotowy wasny licznik odwoa, przystosowanie tych typw do stosowania ze wskanikami intrusive_ptr sprowadza si wanie do zaimplementowania obu
wymienionych funkcji. Niekiedy funkcje te mona sparametryzowa (uoglni) i stosowa wspln implementacj dla caych grup typw z ingerencyjnym zliczaniem
odwoa. Wersje uoglnione najlepiej osadza w przestrzeniach nazw, z ktrych wywodz si owe typy.
Wskaniki intrusive_ptr stosuje si:
t kiedy trzeba potraktowa this jako wskanik inteligentny;
t kiedy mamy do czynienia z istniejcym ju kodem uywajcym

ingerencyjnego zliczania odwoa;


t kiedy dziedzina aplikacji wymaga koniecznie zrwnania rozmiaru wskanika

inteligentnego z rozmiarem wskanika zwykego.

weak_ptr
Nagwek:

"boost/weak_ptr.hpp"

Klasa weak_ptr to klasa obserwatorw wskanikw shared_ptr. Nie wpywa na prawo wasnoci obiektu pozostajcego w posiadaniu shared_ptr. Kiedy obserwowany
przez weak_ptr wskanik shared_ptr jest zmuszony do zwolnienia pozostajcego
w jego posiadaniu zasobu, ustawia wskanik przechowywany w obserwatorze weak_ptr
na warto pust. Zapobiega to wystpowaniu w programie wiszcych wskanikw
weak_ptr. Po co nam taki weak_ptr? Ot zdarzaj si sytuacje, kiedy podana jest
moliwo podgldania i nawet uywania zasobu wspuytkowanego bez przyjmowania go w posiadanie, na przykad przy zrywaniu zalenoci cyklicznych, przy obserwowaniu wsplnego zasobu, wreszcie wanie przy unikaniu wiszcych wskanikw. Ze wskanika weak_ptr mona skonstruowa wskanik shared_ptr, tym samym
przejmujc obserwowany zasb w posiadanie (wspposiadanie).
Poniej prezentowana jest czciowa deklaracja szablonu weak_ptr, z jego najwaniejszymi elementami.
namespace boost {
template<typename T> class weak_ptr {
public:
template <typename Y>

72D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

73

weak_ptr(const shared_ptr<Y>& r);


template<class Y>
weak_ptr(weak_ptr<Y> const & r);
~weak_ptr();

};

T* get() const;
bool expired() const;
shared_ptr<T> lock() const;

Metody
template <typename Y> weak_ptr(const shared_ptr<Y>& r);

Konstruktor tworzcy wskanik weak_ptr na podstawie wskanika shared_ptr, pod


warunkiem e istnieje niejawna konwersja typu Y* na typ T*. Powstajcy obiekt weak_ptr
jest ustawiany do obserwacji zasobu reprezentowanego przez r. Warto licznika odwoa r pozostaje jednak bez zmian. Oznacza to, e zasb reprezentowany przez r
moe zosta zwolniony mimo istnienia odnoszcego si do obiektu weak_ptr. Konstruktor nie zrzuca wyjtkw.
template<class Y> weak_ptr(weak_ptr<Y> const & r);

Konstruktor kopiujcy tworzcy nowy obiekt weak_ptr, obserwujcy zasb reprezentowany przez r, bez zmiany wartoci licznika odwoa do zasobu. Konstruktor nie
zrzuca wyjtkw.
~weak_ptr();

Destruktor weak_ptr nie wpywa na warto licznika odwoa do obserwowanego zasobu. Destruktor nie zrzuca wyjtkw.
bool expired() const;

Zwraca true, jeli obserwowany zasb przeterminowa si albo uniewani si, to


znaczy zosta ju zwolniony. Jeli przechowywany w weak_ptr wskanik jest niepusty, wywoanie expired zawsze zwraca false. Metoda nie zrzuca wyjtkw.
shared_ptr<T> lock() const;

Zwraca obiekt shared_ptr odnoszcy si do zasobu obserwowanego przez wskanik


weak_ptr. Jeli weak_ptr zawiera wskanik pusty, wynikowy wskanik shared_ptr
rwnie otrzyma wskanik pusty. W przeciwnym przypadku dojdzie do zwykego zwikszenia licznika odwoa do obserwowanego zasobu. Metoda nie zrzuca
wyjtkw.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
73

Cz I Biblioteki oglnego przeznaczenia

74

Stosowanie
Zaczniemy od przykadu ilustrujcego podstawy stosowania wskanikw weak_ptr,
z naciskiem na fakt, e nie wpywaj one na liczniki odwoa obserwowanych zasobw. Prezentowane w tym dziale przykady bd z koniecznoci korzysta ze wskanikw shared_ptr, poniewa weak_ptr mao kiedy wystpuje samodzielnie. Korzystanie
ze wskanikw weak_ptr wymaga wczenia do kodu rdowego pliku nagwkowego
"boost/weak_ptr.hpp".
#include
#include
#include
#include

"boost/shared_ptr.hpp"
"boost/weak_ptr.hpp"
<iostream>
<cassert>

class A {};
int main() {
boost::weak_ptr<A> w;
assert(w.expired());
{
boost::shared_ptr<A> p(new A());
assert(p.use_count()==1);
w=p;
assert(p.use_count()==w.use_count());
assert(p.use_count()==1);
// tworzenie z weak_ptr wskanika shared_ptr
boost::shared_ptr<A> p2(w);
assert(p2==p);

}
assert(w.expired());
boost::shared_ptr<A> p3=w.lock();
assert(!p3);

Wskanik w klasy weak_ptr jest konstruowany z uyciem konstruktora domylnego,


co oznacza, e pocztkowo nie obserwuje adnego zasobu (przechowuje wskanik pusty). Aby sprawdzi, czy wskanik weak_ptr obserwuje istniejcy obiekt, naley skorzysta z metody expired. Aby rozpocz obserwacj, naley przypisa do weak_ptr
wskanik shared_ptr. W omawianym przykadzie po przypisaniu p klasy shared_ptr
do w klasy weak_ptr sprawdzamy, czy liczniki odwoa obu wskanikw s faktycznie
identyczne. Potem konstruujemy z weak_ptr wskanik shared_ptr, co jest jedn z metod pozyskania dostpu do podgldanego zasobu. Gdyby przy konstrukcji wskanika
shared_ptr wskanik weak_ptr by przeterminowany, konstruktor shared_ptr zrzuciby
wyjtek boost::bad_weak_ptr. Dalej, kiedy koczy si zasig wskanika shared_ptr p,
dochodzi do uniewanienia w. Dlatego potem, w wyniku wywoania metody lock w celu
pozyskania wskanika shared_ptr (to druga metoda pozyskania dostpu do podgldanego obiektu), otrzymujemy pusty wskanik shared_ptr. W przebiegu caego programu przykadowego wskaniki weak_ptr nie wpywaj na warto licznika odwoa
do obiektu wskazywanego.

74D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

75

W przeciwiestwie do pozostaych wskanikw inteligentnych, weak_ptr nie daje dostpu do obserwowanego wskanika za porednictwem wygodnych operatorw operator* i operator->. Chodzi o to, aby wszystkie operacje podejmowane wobec zasobu
za porednictwem wskanika weak_ptr byy bezpieczne przez sam fakt jawnoci. Inaczej
atwo byoby przypadkiem nawet odwoa si do uniewanionego, pustego wskanika
weak_ptr nie wpywa na licznik odwoa i fakt prowadzenia obserwacji zasobu nie
zabezpiecza przed przedwczesnym zwolnieniem tego zasobu. Dlatego te dostp do
obserwowanego zasobu wymaga utworzenia wskanika shared_ptr albo przez skorzystanie z konstruktora kopiujcego, albo przez wywoanie metody weak_ptr::lock.
Obie te czynnoci zwikszaj licznik odwoa, co gwarantuje podtrzymanie obecnoci zasobu.

Odwieczne pytanie
Skoro przy porzdkowaniu inteligentnych wskanikw nie ma mowy o porzdkowaniu
wedug wartoci obiektw wskazywanych, ale wedug wartoci samych wskanikw,
pojawia si pytanie o sposb stosowania takich wskanikw w kontenerach biblioteki
standardowej w aspekcie porzdkowania elementw kontenera wedug wartoci. Chodzi
choby o przypadek przetworzenia kontenera standardowym algorytmem std::sort
tak, aby doszo do uporzdkowania wartoci wskazywanych, a nie wskanikw. Problem porzdkowania wskanikw inteligentnych w kontenerach nie rni si wiele od
problemu porzdkowania kontenera zwykych wskanikw, ale ten fakt atwo przeoczy (pewnie dlatego, e przechowywanie zwykych wskanikw w kontenerach jest
na tyle problematyczne, e zazwyczaj si tego unika). Ot nie istnieje gotowa infrastruktura porwnywania wartoci wskazywanych przez inteligentne wskaniki, ale
brak bardzo atwo uzupeni. Zwykle polega to na udostpnieniu predykatu wyuskujcego inteligentne wskaniki; utwrzmy wic uniwersalny predykat uatwiajcy stosowanie algorytmw biblioteki standardowej jzyka C++ z iteratorami kontenerw
inteligentnych wskanikw tu wskanikw weak_ptr.
#include <functional>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
template <typename Func, typename T>
struct weak_ptr_unary_t :
public std::unary_function<boost::weak_ptr<T>, bool> {
T t_;
Func func_;
weak_ptr_unary_t(const Func& func, const T& t)
: t_(t), func_(func) {}
bool operator()(boost::weak_ptr<T> arg) const {
boost::shared_ptr<T> sp=arg.lock();
if (!sp) {
return false;
}
return func_(*sp, t_);
}

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
75

Cz I Biblioteki oglnego przeznaczenia

76
};

template <typename Func, typename T> weak_ptr_unary_t<Func,T>


weak_ptr_unary(const Func& func, const T& value) {
return weak_ptr_unary_t<Func,T>(func, value);
}

Obiekt funkcyjny klasy weak_ptr_unary_t jest parametryzowany funkcj do wywoania i typem jej argumentu. Fakt, e funkcja przeznaczona do wywoania jest przechowywana w obiekcie funkcyjnym, uatwia stosowanie obiektu, o czym wkrtce si przekonamy. Aby predykat nadawa si do stosowania z adapterami, klasa weak_ptr_unary_t
jest wyprowadzana jako pochodna klasy std::unary_function, dziki czemu nasza
klasa obiektu funkcyjnego posiada wszystkie definicje typw wymagane przez adaptery predykatw biblioteki standardowej. Caa mokra robota jest wykonywana w przecionym dla klasy operatorze wywoania funkcji, gdzie najpierw nastpuje konstrukcja
wskanika shared_ptr na bazie weak_ptr. To konieczne w celu podtrzymania obecnoci zasobu na czas wywoania waciwej funkcji predykatu. Potem nastpuje wywoanie funkcji (obiektu funkcyjnego) z przekazaniem argumentu (wyuskanego, a wic
w postaci referencji wskazywanego zasobu) i wartoci zapamitanej w konstruktorze
weak_ptr_unary_t. Nasz prosty obiekt funkcyjny nadaje si teraz do stosowania
z wszelkimi algorytmami. Dla wygody zdefiniowalimy rwnie funkcj pomocnicz
weak_ptr_unary, dedukujc typy argumentw i zwracajc odpowiedni dla nich obiekt
funkcyjny14. Zobaczmy, jak cao sprawdzi si w praktyce.
#include <iostream>
#include <string>
#include
#include
#include
#include

<vector>
<algorithm>
"boost/shared_ptr.hpp"
"boost/weak_ptr.hpp"

int main() {
using std::string;
using std::vector;
using boost::shared_ptr;
using boost::weak_ptr;
vector<weak_ptr<string> > vec;
shared_ptr<string> sp1(new string("Przykad"));
shared_ptr<string> sp2(new string("uycia"));
shared_ptr<string> sp3(new string("inteligentnych wskanik w z predykatami"));
vec.push_back(weak_ptr<string>(sp1));
vec.push_back(weak_ptr<string>(sp2));
vec.push_back(weak_ptr<string>(sp3));
vector<weak_ptr<string> >::iterator
it = std::find_if(vec.begin(),vec.end(),
14

Aby ten typ przystosowa do cakiem uniwersalnego stosowania, trzeba by jeszcze mnstwa kodowania
przyp. aut.

76D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

77

weak_ptr_unary(std::equal_to<string>(), string("uycia")));

if (it!=vec.end()) {
shared_ptr<string> sp(*++it);
std::cout << *sp << '\n';
}

Mamy tu przykad tworzenia kontenera vector zawierajcy wskaniki weak_ptr.


Najciekawszym tutaj wierszem kodu jest ten dugi, tworzcy obiekt funkcyjny
weak_ptr_unary_t na uytek algorytmu find_if:
vector<weak_ptr<string> >::iterator
it = std::find_if(vec.begin(),vec.end(),
weak_ptr_unary(std::equal_to<string>(), string("uycia")));

Obiekt funkcyjny jest tu tworzony przez przekazanie do funkcji pomocniczej


weak_ptr_unary na podstawie innego obiektu funkcyjnego, konkretnie std::esual_to,
wraz z cigiem, ktry ma peni rol wzorca przy porwnywaniu. Poniewa typ
weak_ptr_unary_t jest zgodny z adapterami (bo dziedziczy po std::unary_function),
moglibymy zaangaowa go do kompozycji obiektu funkcyjnego dowolnego typu.
Moglibymy na przykad szuka pierwszego cigu, ktry nie pasuje do cigu "uycia":
vector<weak_ptr<string> >::iterator
it = std::find_if(vec.begin(),vec.end(),
std::not1(
weak_ptr_unary(std::equal_to<string>(), string("uycia"))));

Inteligentne wskaniki biblioteki Boost zostay solidnie przygotowane do wsppracy


z komponentami biblioteki standardowej. Uatwia to tworzenie uytecznych i prostych w uyciu komponentw korzystajcych z takich wskanikw. Narzdzia w rodzaju pomocniczej funkcji weak_ptr_unary nie s potrzebne zbyt czsto, zwaszcza
w obliczu dostpnoci bibliotek uoglnionych szablonw wizania (ang. binders),
znacznie bardziej uniwersalnych ni weak_ptr_unary15. Biblioteki te s zazwyczaj
konstruowane z uwzgldnieniem semantyki inteligentnych wskanikw, przez co korzysta si z nich w sposb zupenie przezroczysty.

Dwa sposoby tworzenia wskanikw shared_ptr


ze wskanikw weak_ptr
Wiemy ju, e kiedy mamy wskanik weak_ptr obserwujcy pewien zasb, niekiedy
zachodzi potrzeba skorzystania z tego zasobu. W tym celu trzeba skonwertowa
wskanik weak_ptr na shared_ptr, bo weak_ptr sam w sobie nie pozwala na korzystanie z obserwowanego zasobu (a przynajmniej nie gwarantuje podtrzymania obecnoci
15

Przykadem takiej biblioteki jest Boost.Bind przyp. aut.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
77

Cz I Biblioteki oglnego przeznaczenia

78

zasobu na czas uycia). Wskanik shared_ptr mona utworzy z weak_ptr na dwa


sposoby: przez przekazanie wskanika weak_ptr do konstruktora nowego egzemplarza shared_ptr albo przez wywoanie na rzecz wskanika weak_ptr metody lock,
zwracajcej gotowy wskanik shared_ptr. Wybr metody naley uzaleni od tego,
czy pusty wskanik zaszyty w weak_ptr ma by traktowany jako niepoprawny. Konstruktor shared_ptr akceptujcy argument typu weak_ptr dla wskanika pustego zrzuci
wyjtek bad_weak_ptr. Naleaoby wic z niego korzysta tylko wtedy, kiedy brak
inicjalizacji wskanika weak_ptr ma by uznawany za bd. Z kolei metoda lock
zwrci dla pustego wskanika weak_ptr pusty wskanik shared_ptr i korzysta si z niej
wtedy, kiedy wskazanie puste jest dopuszczalne, na przykad do wykorzystania w tecie.
Co wicej, metod lock stosuje si typowo z rwnoczesn inicjalizacj i testowaniem
zasobu, jak tu:
#include
#include
#include
#include

<iostream>
<string>
"boost/shared_ptr.hpp"
"boost/weak_ptr.hpp"

int main() {
boost::shared_ptr<std::string> sp(new std::string("Pewien zas b"));
boost::weak_ptr<std::string> wp(sp);
//
if (boost::shared_ptr<std::string> p=wp.lock())
std::cout << "iamy go: " << *p << '\n';
else
std::cout << "ech, wskanik shared_ptr jest pusty\n";
}

Jak wida, wskanik shared_ptr jest inicjalizowany wartoci zwracan przez metod
lock wywoan na rzecz wskanika wp (weak_ptr). Otrzymany wskanik jest testowany pod ktem zawierania wskazania pustego. Poniewa skonstruowany tu shared_ptr
jest dostpny jedynie w obrbie ograniczonego zasigu, nie ma moliwoci przypadkowego naduycia go poza zasigiem. Inny scenariusz mielibymy w sytuacji, w ktrej wskanik weak_ptr powinien by niepusty. Wtedy atwo przeoczy testowanie
wskanika shared_ptr i aby zabezpieczy si przed niezdefiniowanym zachowaniem
(w wyniku wyuskania pustego wskanika shared_ptr), najlepiej korzysta z konwersji
przy pomocy konstruktora konwertujcego weak_ptr na shared_ptr, ktry dla pustego
wskanika zrzuci wyjtek:
#include
#include
#include
#include

<iostream>
<string>
"boost/shared_ptr.hpp"
"boost/weak_ptr.hpp"

void access_the_resource(boost::weak_ptr<std::string> wp) {


boost::shared_ptr<std::string> sp(wp);
std::cout << *sp << '\n';
}

78D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

Rozdzia 1. Biblioteka Smart_ptr

79

int main() {
boost::shared_ptr<std::string> sp(new std::string("Pewien zas b"));
boost::weak_ptr<std::string> wp(sp);
//
access_the_resource(wp);
}

Funkcja access_the_resource konstruuje wskanik shared_ptr z przekazanego wskanika weak_ptr. Nie musi w ogle testowa utworzonego wskanika, bo gdyby przekazany w wywoaniu konstruktora argument weak_ptr reprezentowa wskanik pusty,
konstruktor sam zrzuciby wyjtek typu bad_weak_ptr, co wymusioby natychmiastowe przekazanie sterowania poza zasig funkcji i uniemoliwio realizacj odwoania.
Wyjtek naleaoby oczywicie odpowiednio przechwyci i obsuy tam, gdzie to
moliwe i zasadne. Takie zabezpieczenie sprawdza si lepiej ni jawne testowanie
stanu wskanika shared_ptr i ewentualne zwracanie sterowania do wywoujcego.
Niniejszym poznalimy dwie metody pozyskiwania wskanika share_ptr na podstawie weak_ptr.

Podsumowanie
Klasa weak_ptr to ostatni element ukadanki tworzcej obraz implementacji inteligentnych wskanikw w bibliotece Boost. Abstrakcja reprezentowana przez weak_ptr
znakomicie uzupenia shared_ptr, pozwalajc choby na przerywanie zalenoci cyklicznych. Klasa weak_ptr rozwizuje te powszechny problem wiszcych wskanikw. Przy uytkowaniu wsplnego zasobu zdarza si, e niektrzy jego uytkownicy
nie powinni bra odpowiedzialnoci za zarzdzanie czasem jego ycia. Zastosowanie wtedy wskanikw zwykych nie jest rozwizaniem, bo po usuniciu ostatniego wskanika
shared_ptr wsplnego zasobu zostanie on zwolniony. Zwyky wskanik nie daje
moliwoci stwierdzenia, czy wskazywany zasb wci istnieje, czy moe ju nie. W tym
drugim przypadku prba skorzystania z zasobu moe mie katastrofalne skutki. Tymczasem obserwacja zasobu za porednictwem wskanika weak_ptr gwarantuje przekazanie do wskanika informacji o zwolnieniu zasobu i uniewanienie wskanika, co
skutecznie blokuje ryzykowne odwoania. Mamy tu do czynienia ze specjalnym
wcieleniem wzorca projektowego Observer: kiedy zasb jest zwalniany, uytkownicy, ktrzy wyrazili zainteresowanie istnieniem zasobu, s o tym fakcie informowani.
Wskaniki weak_ptr stosuje si:
t do zrywania cyklicznych zalenoci,
t do wspuytkowania zasobu bez przejmowania odpowiedzialnoci

za zarzdzanie nim,
t do eliminowania ryzykownych operacji na wiszcych wskanikach.

D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
79

80

Cz I Biblioteki oglnego przeznaczenia

Smart_ptr podsumowanie
Niniejszy rozdzia prezentowa implementacje inteligentnych wskanikw z biblioteki
Boost tak dla spoecznoci programistw C++ znaczce, e nie sposb ich znaczenia przeceni. Aby biblioteka inteligentnych wskanikw skutecznie speniaa swoje
zadania, powinna uwzgldnia i poprawnie obsugiwa cay szereg czynnikw. Czytelnik z pewnoci mia okazj korzysta z mnstwa inteligentnych wskanikw, by
moe te niektre implementacje sam tworzy lub wsptworzy; ma wic wiadomo
rozmiaru wysiku niezbdnego do dopicia implementacji na ostatni guzik. Nie wszystkie inteligentne wskaniki s tak bystre, jak by si chciao, co tylko zwiksza warto
biblioteki Boost.Smart_ptr, ktra dowioda swojej sprawnoci na tylu polach.
Inteligentne wskaniki z biblioteki Boost, jako tak nieodzowny komponent inynierii
oprogramowania, w oczywisty sposb cieszyy si wielkim zainteresowaniem programistw i testerw. Przez to trudno naleycie doceni wszystkich, ktrzy przyczynili si do sukcesu tej implementacji. Na ostateczny jej ksztat wpywao mnstwo
wnikliwych opinii i cennych wskazwek tylu osb, e nie sposb ich tu wymieni.
Nie mona jednak pomin kilku najwaniejszych osb, ktrych wysiek by dla tego
sukcesu decydujcy:
t Grega Colvina, ojca wskanikw auto_ptr, ktry zaproponowa
implementacj counted_ptr, ktra z czasem przerodzia si w dzisiejsz
klas shared_ptr;
t Bemana Dawesa, ktry wznieci na nowo dyskusj o inteligentnych

wskanikach i zaproponowa uwzgldnienie pierwotnej ich semantyki,


wedle pomysu Grega Colvina;
t Petera Dimova, ktry przemodelowa klasy inteligentnych wskanikw,
uzdatniajc je do rodowisk wielowtkowych, i doda klasy intrusive_ptr
i weak_ptr.

Co ciekawe, koncepcja inteligentnych wskanikw, cho tak dobrze rozpoznana,


wci ewoluuje. Mona si spodziewa postpu w dziedzinie inteligentnych wskanikw, a moe ju inteligentnych zasobw, ale i wspczesne implementacje s implementacjami wysokiej jakoci. Powszechno uycia biblioteki Smart_ptr wynika wanie z tej jakoci i dostosowania do powszechnych potrzeb wygrywaj najlepiej
przystosowani. Trudno o lepsze narzdzia ni te z biblioteki Boost, stale goszczce
w kuchniach znakomitych zespow programistycznych, z ktrych korzystam rwnie
osobicie (i polecam Czytelnikom!). A skoro zostay przyjte do raportu Library
Technical Report, niedugo wejd zapewne (choby czciowo) do specyfikacji biblioteki standardowej jzyka C++.

80D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc

You might also like