Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Previous revision Next revision | Previous revision | ||
smart_ptr [2008/04/16 01:25] wodny utworzono |
smart_ptr [2008/10/23 11:03] (aktualna) rnowak2 |
||
---|---|---|---|
Linia 1: | Linia 1: | ||
+ | {{smart_ptr_latin2.cpp|}} | ||
<code cpp> | <code cpp> | ||
+ | * | ||
+ | * Autor: Marcin Szewczyk, 198370 | ||
+ | * | ||
+ | * Temat: smart_ptr | ||
+ | * | ||
+ | * Inteligentne wskaźniki zawarte w module smart_ptr biblioteki boost. | ||
+ | * Sposób ich wykorzystania, różnice pomiędzy poszczególnymi typami wskaźników, | ||
+ | * nieco ciekawostek, które nie rzucają się od razu w oczy. | ||
+ | * | ||
+ | * Chociaż ten plik jest dość długi zawiera tylko garść informacji. Pełna | ||
+ | * dokumentacja, na której opierałem się pisząc ten program jest tu: | ||
+ | * http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/smart_ptr.htm | ||
+ | * | ||
+ | * Omówione tutaj typy inteligentnych wskaźników to: | ||
+ | * - scoped_ptr | ||
+ | * - scoped_array | ||
+ | * - shared_ptr | ||
+ | * - weak_ptr | ||
+ | * - intrusive_ptr | ||
+ | * - oraz std::auto_ptr nienależący do bibl. boost, ale warty porównania | ||
+ | * | ||
+ | * nie został opisany typ shared_array, ponieważ nie wnosi nic nowego do | ||
+ | * omawianych tu zagadnień | ||
+ | * | ||
+ | * Changelog: | ||
+ | * 2008.10.18 W końcu poprawiłem mały wyciek pamięci w miejscu, w którym | ||
+ | * nie użyłem smart_ptr - o ironio! | ||
+ | * | ||
+ | * Dobre rady: | ||
+ | * W tym dokumencie prezentowany jest wyciek pamięci w przypadku cykli | ||
+ | * budowanych na shared_ptr i jego brak w przypadku cykli budowanych na | ||
+ | * weak_ptr. Można uzyskać dobrą wizualizację zagadnienia przy pomocy | ||
+ | * aplikacji valgrind, | ||
+ | * Proponowany sposób użycia: | ||
+ | * valgrind --leak-check=full ./smart_ptr > /dev/null | ||
+ | * Efekt: | ||
+ | * 114 (12 direct, 102 indirect) bytes in 1 blocks are definitely lost in loss | ||
+ | record 3 of 5 | ||
+ | * at 0x4023294: operator new(unsigned) (vg_replace_malloc.c:224) | ||
+ | * by 0x8049AAE: shared_cycle() (smart_ptr.cpp:474) | ||
+ | * by 0x804B5C6: main (smart_ptr.cpp:608) | ||
+ | * | ||
+ | * Support: | ||
+ | * wodny@thlen.pl <- usuń h | ||
+ | */ | ||
+ | |||
+ | #include <cstdio> | ||
+ | #include <iostream> | ||
+ | #include <cstring> | ||
+ | #include <boost/scoped_ptr.hpp> | ||
+ | #include <boost/scoped_array.hpp> | ||
+ | #include <boost/shared_ptr.hpp> | ||
+ | #include <boost/weak_ptr.hpp> | ||
+ | #include <boost/intrusive_ptr.hpp> | ||
+ | #include <boost/enable_shared_from_this.hpp> | ||
+ | #include <vector> | ||
+ | #include <exception> | ||
+ | |||
+ | // ========================================================== | ||
+ | // Klasy potrzebne do prezentacji podstawowych cech inteligentnych wskaźników | ||
+ | |||
+ | class Harry { | ||
+ | public: | ||
+ | Harry(const char* = NULL); | ||
+ | ~Harry(); | ||
+ | |||
+ | void hello(); | ||
+ | private: | ||
+ | char *name; | ||
+ | }; | ||
+ | |||
+ | |||
+ | class Betty : public Harry { | ||
+ | public: | ||
+ | Betty(); | ||
+ | ~Betty(); | ||
+ | }; | ||
+ | |||
+ | // ---------------------------------------------------------- | ||
+ | |||
+ | Harry::Harry(const char *name_) { | ||
+ | name = name_ ? strdup(name_) : NULL; | ||
+ | printf("-- Constructing Harry (%s)\n", name); | ||
+ | } | ||
+ | |||
+ | Harry::~Harry() { | ||
+ | printf("-- Deconstructing Harry (%s)\n", name); | ||
+ | free(name); | ||
+ | } | ||
+ | |||
+ | void Harry::hello() { | ||
+ | printf("-- Harry \"%s\" says hello\n", name); | ||
+ | } | ||
+ | |||
+ | // ---------------------------------------------------------- | ||
+ | |||
+ | Betty::Betty() { | ||
+ | printf("---- Constructing Betty\n"); | ||
+ | } | ||
+ | |||
+ | Betty::~Betty() { | ||
+ | printf("---- Deconstructing Betty\n"); | ||
+ | } | ||
+ | |||
+ | // ========================================================== | ||
+ | // Klasa, która posłuży do prezentacji, jak radzą sobie inteligentne wskaźniki z | ||
+ | // cyklami | ||
+ | // | ||
+ | // Wariant dla cyklu z użyciem shared_ptr | ||
+ | |||
+ | class RingNode { | ||
+ | public: | ||
+ | RingNode(const char*, const boost::shared_ptr<RingNode>&); | ||
+ | ~RingNode(); | ||
+ | void connect(const boost::shared_ptr<RingNode>&); | ||
+ | const char* get_name(); | ||
+ | const boost::shared_ptr<RingNode>& get_node(); | ||
+ | private: | ||
+ | boost::shared_ptr<RingNode> node; | ||
+ | char* name; | ||
+ | }; | ||
+ | |||
+ | RingNode::RingNode(const char* name_, const boost::shared_ptr<RingNode>& node_) : | ||
+ | node(node_) { | ||
+ | name = name_ ? strdup(name_) : NULL; | ||
+ | printf("++ Constructing RingNode (%s)\n", name); | ||
+ | } | ||
+ | |||
+ | RingNode::~RingNode() { | ||
+ | printf("++ Deconstructing RingNode (%s)\n", name); | ||
+ | free(name); | ||
+ | } | ||
+ | |||
+ | void RingNode::connect(const boost::shared_ptr<RingNode>& node_) { | ||
+ | node = node_; | ||
+ | } | ||
+ | |||
+ | const char* RingNode::get_name() { | ||
+ | return name; | ||
+ | } | ||
+ | |||
+ | const boost::shared_ptr<RingNode>& RingNode::get_node() { | ||
+ | return node; | ||
+ | } | ||
+ | |||
+ | // ---------------------------------------------------------- | ||
+ | // Wariant cyklu z weak_ptr | ||
+ | |||
+ | class WeakRingNode { | ||
+ | public: | ||
+ | WeakRingNode(const char*, const boost::shared_ptr<WeakRingNode>&); | ||
+ | ~WeakRingNode(); | ||
+ | void connect(const boost::shared_ptr<WeakRingNode>&); | ||
+ | const char* get_name(); | ||
+ | const boost::shared_ptr<WeakRingNode> get_node(); | ||
+ | private: | ||
+ | boost::weak_ptr<WeakRingNode> node; | ||
+ | char* name; | ||
+ | }; | ||
+ | |||
+ | WeakRingNode::WeakRingNode(const char* name_, const boost::shared_ptr<WeakRingNode>& | ||
+ | node_) : node(node_) { | ||
+ | name = name_ ? strdup(name_) : NULL; | ||
+ | printf("++ Constructing RingNode (%s)\n", name); | ||
+ | } | ||
+ | |||
+ | WeakRingNode::~WeakRingNode() { | ||
+ | printf("++ Deconstructing RingNode (%s)\n", name); | ||
+ | free(name); | ||
+ | } | ||
+ | |||
+ | void WeakRingNode::connect(const boost::shared_ptr<WeakRingNode>& node_) { | ||
+ | node = node_; | ||
+ | } | ||
+ | |||
+ | const char* WeakRingNode::get_name() { | ||
+ | return name; | ||
+ | } | ||
+ | |||
+ | const boost::shared_ptr<WeakRingNode> WeakRingNode::get_node() { | ||
+ | return node.lock(); | ||
+ | } | ||
+ | |||
+ | // ========================================================== | ||
+ | // Klasa potrzebna do prezentacji intrusive_ptr | ||
+ | // czyli sama zwiększa/zmniejsza ilość referencji do danego obiektu na zlecenie | ||
+ | // intrusive_ptr oraz sama odpowiada za zniszczenie obiektu, gdy ilość odwołań | ||
+ | // spadnie do zera | ||
+ | |||
+ | class Intruded { | ||
+ | public: | ||
+ | Intruded(); | ||
+ | ~Intruded(); | ||
+ | private: | ||
+ | int counter; | ||
+ | |||
+ | friend void intrusive_ptr_add_ref(Intruded *const); | ||
+ | friend void intrusive_ptr_release(Intruded *const); | ||
+ | }; | ||
+ | |||
+ | void intrusive_ptr_add_ref(Intruded *const); | ||
+ | void intrusive_ptr_release(Intruded *const); | ||
+ | |||
+ | // ---------------------------------------------------------- | ||
+ | |||
+ | Intruded::Intruded() : counter(0) { | ||
+ | printf("%% Creating Intruded\n"); | ||
+ | } | ||
+ | |||
+ | Intruded::~Intruded() { | ||
+ | printf("%% Destroying Intruded\n"); | ||
+ | } | ||
+ | |||
+ | // Jak widać, licznik jest składową klasy, natomiast funkcje odpowiedzialne za | ||
+ | // zliczanie - nie. | ||
+ | // Na podstawie typu parametru, intrusive pointer wybiera odpowiedni zestaw | ||
+ | // funkcji do inkrementacji i dekrementacji ilości odwołań | ||
+ | |||
+ | void intrusive_ptr_add_ref(Intruded *const p) { | ||
+ | printf("%% Added. Now: %d\n", ++(p->counter)); | ||
+ | } | ||
+ | |||
+ | void intrusive_ptr_release(Intruded *const p) { | ||
+ | printf("%% Removed. Now: %d\n", --(p->counter)); | ||
+ | if(!p->counter) { | ||
+ | printf("%% Deleting Intruded because of count == 0...\n"); | ||
+ | delete p; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // ========================================================== | ||
+ | // Jak uzyskać poprawny (pod względem zliczania odwołań) shared_ptr do obiektu | ||
+ | // nie mając wiedzy na temat tego, czy już jakiś inny shared_ptr się do niego | ||
+ | // odwołuje | ||
+ | |||
+ | // Konieczne jest dziedziczenie klasy po enable_shared_from_this<> | ||
+ | |||
+ | class WithSharing : public boost::enable_shared_from_this<WithSharing> { | ||
+ | public: | ||
+ | boost::shared_ptr<WithSharing> get_shared() { | ||
+ | return shared_from_this(); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // ========================================================== | ||
+ | // ========================================================== | ||
+ | // ========================================================== | ||
+ | |||
+ | using namespace std; | ||
+ | |||
+ | // By uniknąć bardzo długich definicji poniżej | ||
+ | typedef vector< boost::shared_ptr<Harry> > SharedHarryVector; | ||
+ | |||
+ | // ========================================================== | ||
+ | // Funkcja wyświetla nagłówek tekstowy podkreślony linijką | ||
+ | |||
+ | void header(const char* label) { | ||
+ | char* bar = strdup(label); | ||
+ | cout << endl << label << endl; | ||
+ | memset(bar, '=', strlen(bar)); | ||
+ | cout << bar << endl; | ||
+ | free(bar); | ||
+ | } | ||
+ | |||
+ | // ========================================================== | ||
+ | // ========================================================== | ||
+ | |||
+ | // Funkcja jest wywoływana, ale szkodliwy fragment jest wykomentowany | ||
+ | void play_dirty_harry() { | ||
+ | header("Dirty Harry - czego ze wskaznikami robic nie wolno"); | ||
+ | |||
+ | Harry* tmp_harry; | ||
+ | boost::scoped_ptr<Harry> harry(tmp_harry = new Harry("Simple Class Harry")); | ||
+ | |||
+ | //boost::scoped_ptr<Harry> harry2(tmp_harry); | ||
+ | // Nielegalne - dwa scoped_ptr, każdy z liczbą odwołań = 1 | ||
+ | // Podczas destrukcji dwóch scoped_ptr dochodzi dwa razy do próby destrukcji | ||
+ | // jednego obiektu klasy Harry. Lepiej przedstawi to shared_ptr, który pojawi | ||
+ | // się poniżej | ||
+ | |||
+ | //boost::scoped_ptr<Harry> harry_b(harry); | ||
+ | // Kopiowanie scoped_ptr jest zakazane (konst. kopiujący w sekcji private) | ||
+ | //boost::scoped_ptr<Harry> harry_c; | ||
+ | //harry_c = harry; | ||
+ | // Analogiczna uwaga dotyczy operatora przypisania | ||
+ | |||
+ | } | ||
+ | |||
+ | void play_nice_harry() { | ||
+ | header("play_nice_harry - scoped_ptr jak zmienna automatyczna"); | ||
+ | // Jakieś tam zadania | ||
+ | // ... | ||
+ | |||
+ | // Tutaj proste zastosowanie - scoped_ptr zachowuje się jak zmienna | ||
+ | // automatyczna. Po wyjściu poza zasięg, scoped_ptr niszczy siebie, a | ||
+ | // wcześniej związany z nim obiekt. | ||
+ | // | ||
+ | // Stąd nazwa - scoped - czyli dot. zasięgu. | ||
+ | // Nawiązuje to do pomysłu "resource acquisition is initialization". Zasoby | ||
+ | // są zajmowane w momencie inicjalizacji i, co ważniejsze, zwalniane podczas | ||
+ | // "odinicjowania", czyli destrukcji po wyjściu poza zasięg. | ||
+ | |||
+ | boost::scoped_ptr<Harry> harry3(new Harry("Harry3")); | ||
+ | harry3->hello(); | ||
+ | |||
+ | Harry harry4("Harry4"); | ||
+ | harry4.hello(); | ||
+ | |||
+ | } | ||
+ | |||
+ | void play_nice_harry3() { | ||
+ | // Jednak nie zawsze wystarcza nam zmienna automatyczna. Ciekawsze jest | ||
+ | // dynamiczne powoływanie obiektów do życia operatorem "new". | ||
+ | |||
+ | header("play_nice_harry3 - operator \"new\" bez potrzeby wywolania delete"); | ||
+ | boost::scoped_ptr<Harry> harry7; | ||
+ | |||
+ | // | ||
+ | // Coś tam do roboty... | ||
+ | bool needHarry = true; | ||
+ | // Coś tam do roboty... | ||
+ | // | ||
+ | |||
+ | if(needHarry) | ||
+ | harry7.reset(new Harry("Harry7")); | ||
+ | |||
+ | // | ||
+ | // Coś tam do roboty... | ||
+ | // | ||
+ | |||
+ | if(harry7) { | ||
+ | printf("-- Stwierdzlismy, ze harry7 sie przyda - wykonajmy na nim operacje\n"); | ||
+ | // Operacje na Harrym... | ||
+ | } | ||
+ | |||
+ | // Normalnie w tym miejscu przydałoby się wywołanie delete na zwykłym | ||
+ | // wskaźniku. Ponieważ jednak nasz inteligenty wskaźnik jest zmienną | ||
+ | // automatyczną, sam usunie siebie i skojarzony obiekt | ||
+ | } | ||
+ | |||
+ | void comfort_test() throw(exception) { | ||
+ | header("Comfort test - shared_ptr jak zm. auto.; auto. usuwanie, gdy rzucany jest | ||
+ | wyjatek"); | ||
+ | boost::shared_ptr<Harry> h1; | ||
+ | boost::shared_ptr<Harry> h2; | ||
+ | |||
+ | // Coś tam... | ||
+ | |||
+ | // Potrzebujemy Harry'ego | ||
+ | h1.reset(new Harry("Potter")); | ||
+ | h2.reset(new Harry("Callahan")); | ||
+ | cout << "Callahan count: " << h2.use_count() << endl; | ||
+ | h1 = h2; | ||
+ | cout << "Callahan zjadl Pottera, count: " << h2.use_count() << endl; | ||
+ | cout << "h1: "; | ||
+ | h1->hello(); | ||
+ | cout << "h2: "; | ||
+ | h2->hello(); | ||
+ | |||
+ | // Go ahead, make my day. | ||
+ | // Rzucamy wyjątek | ||
+ | cout << "Throwing an exception .44" << endl; | ||
+ | throw exception(); | ||
+ | } | ||
+ | |||
+ | // Prezentacja, jak zachowują się intrusive_ptr | ||
+ | void test_intrusive_ptr() { | ||
+ | header("test_intrusive_ptr"); | ||
+ | boost::intrusive_ptr<Intruded> i1(new Intruded); | ||
+ | } | ||
+ | |||
+ | // Ciekawostka związana z shared_ptr | ||
+ | // Różnica, między tym, co deklarujemy jako klasę dla szablonu shared_ptr, a | ||
+ | // klasą parametru przekazywanego w konstruktorze. Jakie nieoczywiste zdarzenie | ||
+ | // może wystąpić, gdy destruktory klas są niewirtualne. | ||
+ | |||
+ | void non_virt_diffs() { | ||
+ | header("Niewirtualny destruktor. Roznice miedzy zwyklym wskaznikiem a shared_ptr"); | ||
+ | // Destructor nie jest wirtualny, stąd Betty nie jest niszczona | ||
+ | { | ||
+ | cout << "Zwykly Harry-wskaznik na Betty" << endl; | ||
+ | Harry *hb = new Betty; | ||
+ | delete hb; | ||
+ | } | ||
+ | | ||
+ | // Nieco inaczej sprawa się przestawia dla shared_ptr | ||
+ | { | ||
+ | cout << endl << "Betty-wskaznik na Betty w Harry-szablonie" << endl; | ||
+ | boost::shared_ptr<Harry> b1(new Betty); | ||
+ | // shared_ptr pamięta rzeczywisty typ wskaźnika przekazywanego w | ||
+ | // parametrze. W związku z tym używa destruktora dla tego typu (Betty) | ||
+ | // zamiast typu deklarowanego w szablonie (Harry) | ||
+ | // > przykładowa implementacja mechanizmu: | ||
+ | // > http://wodny.org/download/smart_ptr/template_memory.cpp | ||
+ | } | ||
+ | | ||
+ | { | ||
+ | cout << endl << "Harry-wskaznik na Betty w Harry-szablonie" << endl; | ||
+ | Harry *b3 = new Betty; | ||
+ | boost::shared_ptr<Harry> b2(b3); | ||
+ | // Tutaj shared_ptr nie ma okazji poznać prawdziwego typu obiektu. | ||
+ | // Traktuje go jak Harry'ego. Dla poprawnego działania, tu destruktor już | ||
+ | // musi być wirtualny. | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | void play_scoped_array() { | ||
+ | // Prosty przykład, że inteligentne wskaźniki z dopiskiem _array służą do | ||
+ | // kojarzenia ich z tablicami. Nie można by tu użyć scoped_ptr. Jest to | ||
+ | // związane z obostrzeniem, że delete stosujemy dla pojedynczego obiektu, | ||
+ | // delete[] dla tablic. Inteligenty wskaźnik nie posiadając informacji o | ||
+ | // naturze przekazanego wskaźnika źle zarządzałby pamięcią. | ||
+ | header("Zabawy ze scoped_array"); | ||
+ | boost::scoped_array<Harry> harry_a1(new Harry[3]); | ||
+ | } | ||
+ | |||
+ | void general_use() { | ||
+ | header("Ogolny sposob uzycia shared_ptr"); | ||
+ | |||
+ | Harry *hp1 = NULL; | ||
+ | boost::shared_ptr<Harry> harry_sp1(hp1 = new Harry("Shared Harry1")); | ||
+ | cout << "harry_sp1.use_count: " << harry_sp1.use_count() << endl; | ||
+ | //boost::shared_ptr<Harry> harry_sp2(hp1); | ||
+ | // Taka konstrukcja spowoduje błąd. Dwa shared_ptr będą próbowały usunąć dwa | ||
+ | // razy ten sam obiekt podczas własnej destrukcji. | ||
+ | // | ||
+ | // Istotne jest, by zauważyć, że shared_ptr nie ma jakiejś ukrytej tablicy | ||
+ | // asocjacyjnej (trzymającej się np. wzorca Singleton), która pozwalałaby mu | ||
+ | // stwierdzać, którymi wskaźnikami już się opiekuje. | ||
+ | // Poinformowanie o tym, że kolejny shared_ptr chce również odnosić się do | ||
+ | // danego zwykłego wskaźnika następuje przez wykonanie konstruktora | ||
+ | // kopiującego (opcjonalnie operatora przypisania). Od tej pory każdy z | ||
+ | // shared_ptr ustawi ilość odwołań na 2 i przy destrukcji jednego z nich, | ||
+ | // drugi inteligenty wskaźnik dowie się o tym zdarzeniu i zdekrementuje | ||
+ | // własny licznik odwołań | ||
+ | // (Mowa jest tu o logice działania, nie rzeczywistej implementacji | ||
+ | // inteligentnego wskaźnika). | ||
+ | cout << "Czas, by podzielic sie zasobem" << endl; | ||
+ | boost::shared_ptr<Harry> harry_sp2(harry_sp1); | ||
+ | boost::shared_ptr<Harry> harry_sp3; | ||
+ | harry_sp3 = harry_sp2; | ||
+ | cout << "harry_sp1.use_count: " << harry_sp1.use_count() << endl; | ||
+ | cout << "harry_sp2.use_count: " << harry_sp2.use_count() << endl; | ||
+ | cout << "harry_sp3.use_count: " << harry_sp3.use_count() << endl; | ||
+ | |||
+ | } | ||
+ | |||
+ | SharedHarryVector* get_harries() { | ||
+ | // Shared_ptr a kontenery | ||
+ | // (scoped_ptr nie może zostać użyty do takich zastosowań - nie da się | ||
+ | // skopiować kontenerowi, poza tym jego przeznaczenie jest inne) | ||
+ | header("Wektor (vector) z Harrych - uzycie shared_ptr w kontenerach STL"); | ||
+ | |||
+ | SharedHarryVector *vect = new SharedHarryVector(); | ||
+ | boost::shared_ptr<Harry> h; | ||
+ | for(int i = 0; i < 4; ++i) { | ||
+ | h.reset(new Harry("Vector Harry")); | ||
+ | cout << "Ilosc Harry'ego przed wlozeniem do wektora: " << h.use_count() << endl; | ||
+ | vect->push_back(h); | ||
+ | cout << "Po wlozeniu: " << h.use_count() << endl; | ||
+ | } | ||
+ | return vect; | ||
+ | } | ||
+ | |||
+ | void stl_container() { | ||
+ | // W nawiązaniu do powyższej funkcji | ||
+ | boost::shared_ptr< SharedHarryVector > harry_vector(get_harries()); | ||
+ | cout << "Liczność jednego z wektorowych Harrych: " << | ||
+ | harry_vector->front().use_count() << endl << endl; | ||
+ | } | ||
+ | |||
+ | void no_cycle() { | ||
+ | header("Bez cyklu"); | ||
+ | // Bez cyklu | ||
+ | boost::shared_ptr<RingNode> R3(new RingNode("RingNode3", | ||
+ | boost::shared_ptr<RingNode>((RingNode*)NULL))); | ||
+ | boost::shared_ptr<RingNode> R4(new RingNode("RingNode4", | ||
+ | boost::shared_ptr<RingNode>(R3))); | ||
+ | cout << "R3 (" << R4->get_name() << ") with count " << R3.use_count() << endl; | ||
+ | cout << "R4 (" << R4->get_name() << ") with count " << R4.use_count() << " | ||
+ | connected to " << R4->get_node()->get_name() << endl; | ||
+ | // Wszystkie węzły ulegną zniszczeniu jak powinny | ||
+ | } | ||
+ | |||
+ | void shared_cycle() { | ||
+ | header("Cykl na shared_ptr"); | ||
+ | // Większy cykl, choć uzyskany tylko pośrednio, powoduje, że węzły nie | ||
+ | // zostaną usunięte, ponieważ ilość odwołań w shared_ptr nie spadnie do zera | ||
+ | boost::shared_ptr<RingNode> R7(new RingNode("RingNode7", | ||
+ | boost::shared_ptr<RingNode>((RingNode*)NULL))); | ||
+ | boost::shared_ptr<RingNode> R8(new RingNode("RingNode8", | ||
+ | boost::shared_ptr<RingNode>(R7))); | ||
+ | boost::shared_ptr<RingNode> R9(new RingNode("RingNode9", | ||
+ | boost::shared_ptr<RingNode>(R8))); | ||
+ | R7->connect(R9); | ||
+ | cout << "R7 (" << R7->get_name() << ") with count " << R7.use_count() << " | ||
+ | connected to " << R7->get_node()->get_name() << endl; | ||
+ | cout << "R8 (" << R8->get_name() << ") with count " << R8.use_count() << " | ||
+ | connected to " << R8->get_node()->get_name() << endl; | ||
+ | cout << "R9 (" << R9->get_name() << ") with count " << R9.use_count() << " | ||
+ | connected to " << R9->get_node()->get_name() << endl; | ||
+ | |||
+ | /* | ||
+ | R7-->"R7"<--------node--------. | ||
+ | | | | ||
+ | *--node-->"R8"<--R8 | | ||
+ | | | | ||
+ | *--node-->"R9"<--R9 | ||
+ | */ | ||
+ | } | ||
+ | |||
+ | void weak_cycle() { | ||
+ | header("Cykl na weak_ptr"); | ||
+ | // "Słaby" cykl oparty o weak_ptr | ||
+ | boost::shared_ptr<WeakRingNode> R5(new WeakRingNode("WeakRingNode5", | ||
+ | boost::shared_ptr<WeakRingNode>((WeakRingNode*)NULL))); | ||
+ | boost::shared_ptr<WeakRingNode> R6(new WeakRingNode("WeakRingNode6", | ||
+ | boost::shared_ptr<WeakRingNode>(R5))); | ||
+ | R5->connect(R6); | ||
+ | boost::weak_ptr<WeakRingNode> Wtmp1(R5); | ||
+ | boost::weak_ptr<WeakRingNode> Wtmp2(R6); | ||
+ | // Jak można zauważyć, weak_ptr nie zwiększa licznika odwołań do obiektu, | ||
+ | // którego dotyczy, ani nie usuwa obiektu, gdy wielkość ta spadnie do zera. | ||
+ | // Obiekt, do którego się odwołuje może zostać usunięty "bez jego wiedzy". | ||
+ | // Dlatego właśnie współpracuje z shared_ptr. | ||
+ | cout << "R5 (" << R5->get_name() << ") with count " << R5.use_count() << " | ||
+ | connected to " << R5->get_node()->get_name() << endl; | ||
+ | cout << "R6 (" << R6->get_name() << ") with count " << R6.use_count() << " | ||
+ | connected to " << R6->get_node()->get_name() << endl; | ||
+ | // Poprzez funkcję weak_ptr.lock(), która użyta jest w get_node(), weak_ptr | ||
+ | // gwarantuje nam, że zostanie wykonana dodatkowa kopia, a shared_ptr zajmie | ||
+ | // się zwiększeniem licznika odwołań, by obiekt nie zniknął podczas | ||
+ | // wykonywania na nim operacji. | ||
+ | // Jeśli obiekt zdążył zniknąć już wcześniej, podczas tworzenia shared_ptr z | ||
+ | // weak_ptr dostaniemy wyjątek bad_weak_ptr. | ||
+ | } | ||
+ | |||
+ | void shared_and_weak() { | ||
+ | header("Relacja miedzy shared_ptr i weak_ptr"); | ||
+ | |||
+ | cout << "Oryginalny shared_ptr:" << endl; | ||
+ | boost::shared_ptr<Harry> shared1(new Harry); | ||
+ | cout << "shared1.count: " << shared1.use_count() << endl; | ||
+ | |||
+ | cout << "weak_ptr z shared_ptr:" << endl; | ||
+ | boost::weak_ptr<Harry> weak1(shared1); | ||
+ | cout << "shared1.count: " << shared1.use_count() << endl; | ||
+ | cout << "weak1.count : " << weak1.use_count() << endl; | ||
+ | |||
+ | cout << "shared_ptr z weak_ptr (z shared_ptr)" << endl; | ||
+ | boost::shared_ptr<Harry> shared2(weak1); | ||
+ | cout << "shared1.count: " << shared1.use_count() << endl; | ||
+ | cout << "weak1.count : " << weak1.use_count() << endl; | ||
+ | cout << "shared2.count: " << shared2.use_count() << endl; | ||
+ | } | ||
+ | |||
+ | void with_sharing_test(WithSharing& ws) { | ||
+ | header("Test enable_shared_from_this<>"); | ||
+ | |||
+ | boost::shared_ptr<WithSharing> ws_a(ws.get_shared()); | ||
+ | cout << "Drugi udzial, use_count: " << ws_a.use_count() << endl; | ||
+ | /* . | ||
+ | * /|\ | ||
+ | * | | ||
+ | * ------------------------------------------------- | ||
+ | * dwa niezależne fragmenty kodu, np. dwa wątki | ||
+ | * ------------------------------------------------- | ||
+ | * | | ||
+ | * \|/ | ||
+ | * ' | ||
+ | */ | ||
+ | boost::shared_ptr<WithSharing> ws_b(ws.get_shared()); | ||
+ | cout << "Trzeci udzial, use_count: " << ws_b.use_count() << endl; | ||
+ | |||
+ | } | ||
+ | |||
+ | void auto_ptr_demo() { | ||
+ | header("auto_ptr - przy boost maly sens stosowania"); | ||
+ | /* | ||
+ | * | ||
+ | http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.0/classstd_1_1auto__ptr.html | ||
+ | * | ||
+ | * "auto_ptr does not meet the CopyConstructible and Assignable | ||
+ | * requirements for Standard Library container elements and thus | ||
+ | * instantiating a Standard Library container with an auto_ptr results in | ||
+ | * undefined behavior." | ||
+ | * | ||
+ | * Reasumując - auto_ptr ciekawy nie jest. Potrafi tylko oddawać powiązanie z | ||
+ | * obiektem, natomiast nie potrafi się nim dzielić. Zastosowanie w | ||
+ | * kontenerach nie jest możliwe. | ||
+ | */ | ||
+ | |||
+ | auto_ptr<Harry> a1(new Harry); | ||
+ | cout << "NEW. " << a1.get() << endl; | ||
+ | auto_ptr<Harry> a2(a1); | ||
+ | cout << "1st transfer, source: " << a1.get() << endl; | ||
+ | cout << "1st transfer, dest. : " << a2.get() << endl; | ||
+ | auto_ptr<Harry> a3 = a2; | ||
+ | cout << "2nd transfer, source: " << a2.get() << endl; | ||
+ | cout << "2nd transfer, dest. : " << a3.get() << endl; | ||
+ | } | ||
+ | |||
+ | // ========================================================== | ||
+ | // ========================================================== | ||
+ | // ========================================================== | ||
+ | |||
+ | int main(int argc, char** argv) { | ||
+ | // ========================================================== | ||
+ | // Zestaw tego, czego robić nie wolno ze scoped_ptr | ||
+ | play_dirty_harry(); | ||
+ | |||
+ | // shared_ptr | ||
+ | general_use(); | ||
+ | |||
+ | // Mniej interesujące | ||
+ | play_nice_harry(); | ||
+ | // Bardziej interesujące | ||
+ | play_nice_harry3(); | ||
+ | | ||
+ | // scoped_array zamiast scoped_ptr - właściwe delete, konkretnie delete[] | ||
+ | play_scoped_array(); | ||
+ | |||
+ | // Niewirtualny destruktor. Różnice między zwykłym wskaźnikiem a shared_ptr | ||
+ | non_virt_diffs(); | ||
+ | |||
+ | // ========================================================== | ||
+ | // Kontener STL | ||
+ | stl_container(); | ||
+ | |||
+ | // ========================================================== | ||
+ | // Shared vs Weak a cykle | ||
+ | |||
+ | no_cycle(); | ||
+ | shared_cycle(); | ||
+ | weak_cycle(); | ||
+ | shared_and_weak(); | ||
+ | |||
+ | // ========================================================== | ||
+ | // GŁÓWNY POWÓD UŻYWANIA SMART_PTRów | ||
+ | // komfort operatora "new" | ||
+ | // z bezpieczeństwem zmiennych automatycznych | ||
+ | // ========================================================== | ||
+ | try { | ||
+ | comfort_test(); | ||
+ | } catch (...) { | ||
+ | cout << "Caught!" << endl; | ||
+ | } | ||
+ | |||
+ | // ========================================================== | ||
+ | // Test enable_shared_from_this<> | ||
+ | boost::shared_ptr<WithSharing> ws(new WithSharing); | ||
+ | with_sharing_test(*ws); | ||
+ | |||
+ | // ========================================================== | ||
+ | // std::auto_ptr | ||
+ | auto_ptr_demo(); | ||
+ | |||
+ | // ========================================================== | ||
+ | // Intrusive test | ||
+ | test_intrusive_ptr(); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | |||
</code> | </code> |