Professional Documents
Culture Documents
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
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Cz I
Spis treci
Cz II
6 D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\!!Spis.doc
Spis treci
Rozdzia 1.
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
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
37
38
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
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.
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
39
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;
};
template<typename T>
void swap(scoped_ptr<T> & a, scoped_ptr<T> & b);
Metody
explicit scoped_ptr(T* p = 0);
40D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
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);
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;
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
42
T funkcj konwersji mona stosowa zamiast metody get w instrukcjach warunkowych do testowania stanu wskanika scoped_ptr.
void swap(scoped_ptr& b);
Funkcje zewntrzne
template<typename T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b);
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.
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
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";
}
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.
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
43
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();
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
45
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
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;
};
46D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
47
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
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.
48D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
49
shared_ptr
Nagwek:
"boost/shared_ptr.hpp"
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
49
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
};
51
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);
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);
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
51
52
~shared_ptr();
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;
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;
52D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
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;
Funkcje zewntrzne
template <class T, class >
shared_ptr<T> static_pointer_cast(const shared_ptr< >& r);
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
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
53
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);
}
54D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
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.
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
55
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());
}
56D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
57
}
std::cout << "Plik zosta przed chwil zamknity!\n";
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
57
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
59
protected:
virtual ~A() {};
};
int main() {
boost::shared_ptr<A> p=A::createA();
}
}
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
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
zwalniajcych9.
60D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
61
shared_array
Nagwek:
"boost/shared_array.hpp"
intrusive_ptr
Nagwek:
"boost/intrusive_ptr.hpp"
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
61
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;
};
Metody
intrusive_ptr(T* p, bool add_ref=true);
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
63
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;
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;
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
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;
}
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
65
};
protected:
reference_counter& operator=(const reference_counter&) {
// Atrapa
return *this;
}
private:
// Blokada dostpu do konstruktora kopiujcego
reference_counter(const reference_counter&);
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
65
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
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&);
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
67
68
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
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();
}
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
69
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();
}
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
71
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
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
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
73
};
T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
Metody
template <typename Y> weak_ptr(const shared_ptr<Y>& 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;
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
73
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);
74D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
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
76
};
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
77
weak_ptr_unary(std::equal_to<string>(), string("uycia")));
if (it!=vec.end()) {
shared_ptr<string> sp(*++it);
std::cout << *sp << '\n';
}
D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r0106.doc
77
78
<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"
78D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc
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
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
80D:\roboczy jarek\makiety poprawki i druku pdf\Wicej ni C++ Wprowadzenie do bibliotek Boost\07 druk\r01-06.doc