{"id":358,"date":"2018-10-13T14:19:00","date_gmt":"2018-10-13T12:19:00","guid":{"rendered":"http:\/\/www.kompikownia.pl\/?p=358"},"modified":"2019-02-09T17:41:05","modified_gmt":"2019-02-09T16:41:05","slug":"weak_ptr-inteligentne-wskazniki","status":"publish","type":"post","link":"https:\/\/www.kompikownia.pl\/index.php\/2018\/10\/13\/weak_ptr-inteligentne-wskazniki\/","title":{"rendered":"weak_ptr &#8211; inteligentne wska\u017aniki"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">&lt; 1<\/span> <span class=\"rt-label rt-postfix\">minut\u0119<\/span><\/span><p>Om\u00f3wili\u015bmy unique_ptr i shared_ptr. Jeste\u015bmy bardzo blisko ko\u0144ca naszej fascynuj\u0105cej w\u0119dr\u00f3wki przez \u015bwiat inteligentnych wska\u017anik\u00f3w. Zosta\u0142 nam do om\u00f3wienia ostatni gatunek. Weak_ptr. W t\u0142umaczeniu na polski by\u0142by to \u201es\u0142aby wska\u017anik\u201d. Czym mo\u017ce si\u0119 charakteryzowa\u0107? Czy potrzeba nam w programowaniu s\u0142abeuszy?<\/p>\n<p>Tytu\u0142owa \u201es\u0142abo\u015b\u0107\u201d weak_ptr polega na tym, \u017ce nie posiada on w\u0142asnego licznika referencji. Weak_ptr nie potrafi \u201ena sta\u0142e\u201d przej\u0105\u0107 kontroli nad obiektem. Je\u015bli do wska\u017anika weak_ptr przypiszemy wska\u017anik shared_ptr, to wewn\u0119trzny licznik shared_ptra nie ulegnie zmianie. Je\u015bli spe\u0142niony zostanie kt\u00f3ry\u015b z warunk\u00f3w unicestwienia shared_ptra, to obiekt z nim powi\u0105zany zostanie zniszczony. I nie ma tu znaczenia fakt, \u017ce wcze\u015bniej shared_ptr przechowuj\u0105cy ten obiekt zosta\u0142 przypisany do weak_ptr\u2019a.<\/p>\n<p>Ale co w takim wypadku stanie si\u0119 z weak_ptr, kt\u00f3remu przypisali\u015bmy shared_ptr? Intuicja podpowiada, \u017ce weak_ptr wskazuje w tym momencie na nieistniej\u0105cy obiekt. Czy to prawda? Tak, oczywi\u015bcie. Na co nam wi\u0119c taki wska\u017anik? Dowiesz si\u0119 wkr\u00f3tce. Najpierw przyjrzyjmy si\u0119, jak w\u00a0praktyce wygl\u0105da jego funkcjonowanie.<\/p>\n<h1 class=\"western\">Weak_ptr \u2013 jak dzia\u0142a? Przyk\u0142ad.<\/h1>\n<p>Zilustrujemy powy\u017csz\u0105 my\u015bl prostym przyk\u0142adem.<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\n#include <iostream>\r\n#include <memory>\r\n#include <assert.h>\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    shared_ptr<int> shared_wsk1 = make_shared<int>(30);\r\n    weak_ptr<int> weak1;\r\n    weak_ptr<int> weak2;\r\n    weak1 = shared_wsk1;\r\n    {\r\n        shared_ptr<int> shared_wsk2 = make_shared<int>(42);\r\n        weak2 = shared_wsk2;\r\n        cout<<\"shared_wsk2 ref count: \"<<shared_wsk2.use_count()<<endl;\r\n        assert(!weak2.expired());\r\n        shared_ptr<int> shared_wsk3 = weak2.lock();\r\n        cout<<*weak2.lock()<<endl;\r\n        cout<<\"shared_wsk2 ref count: \"<<shared_wsk2.use_count()<<endl;\r\n    }\r\n    assert(!weak1.expired());\r\n    cout<<*weak1.lock()<<endl;\r\n    assert(!weak2.expired());\r\n    cout<<*weak2.lock()<<endl;\r\n    return 0;\r\n}\r\n\r\n<\/pre>\n<p>Ten kr\u00f3tki program przedstawia istot\u0119 dzia\u0142ania wska\u017anika typu weak_ptr. W linijce \u00f3smej tworzymy sobie wska\u017anik shared_ptr na obiekt typu int (\u017ceby nie komplikowa\u0107). Nast\u0119pnie tworzymy sobie dwa \u201es\u0142abe wska\u017aniki\u201d.<\/p>\n<p>W linijce 12 zaczyna si\u0119 blok instrukcji. Dzi\u0119ki niemu zaobserwujemy co si\u0119 stanie z weak_ptr\u2019em, kiedy skojarzony z nim shared_ptr zostanie zniszczony.<\/p>\n<p>W linijce 13 tworzymy sobie kolejny wska\u017anik shared_ptr wskazuj\u0105cy na obiekt typu int. Za nim, w linijce 14 idzie przypisanie: weak2 do shared_wsk2. Gdyby weak2 by\u0142 wska\u017anikiem typu shared_ptr, licznik referencji podskoczy\u0142by do dw\u00f3ch. Ale tak si\u0119 nie sta\u0142o. Pokazuje to linijka 15.<\/p>\n<h2>Weak_ptr - nowe metody<\/h2>\n<p>W linijce 16 znajduje si\u0119 asercja. Warunek wewn\u0105trz niej wykorzystuje jedn\u0105 z metod, kt\u00f3ra jest charakterystyczna dla wska\u017anik\u00f3w s\u0142abych. Mowa o expired(). Metoda expired() informuje nas, czy wska\u017anik weak_ptr wskazuje w chwili obecnej na co\u015b sensownego. Je\u015bli tak, zwraca warto\u015b\u0107 true. W\u00a0przeciwnym wypadku zwraca false.<\/p>\n<p>To nie koniec nowo\u015bci. Zwr\u00f3\u0107 uwag\u0119 na linijk\u0119 17. Zauwa\u017cysz w niej kolejn\u0105 now\u0105 metod\u0119 - lock. Do czego ona s\u0142u\u017cy?<\/p>\n<p>Jak dowiedzia\u0142e\u015b si\u0119 ze wst\u0119pu, weak_ptr nie potrafi przej\u0105\u0107 obiektu na \u201ew\u0142asno\u015b\u0107\u201d. Wynika z tego wiele jego w\u0142asno\u015bci. Nie mo\u017cemy np.: bezpo\u015brednio odwo\u0142ywa\u0107 si\u0119 do obiektu, na kt\u00f3ry wskazuje weak_ptr. Chocia\u017c weak_ptr nie mo\u017ce powiedzie\u0107 o obiekcie nic, posiada on mo\u017cliwo\u015b\u0107 poinformowania nas, czy obiekt jeszcze istnieje. Aby dosta\u0107 si\u0119 do obiektu, musimy u\u017cy\u0107 metody lock(). Pozwala ona przechwyci\u0107 w\u0142asno\u015b\u0107 obiektu. Nazwa metody (lock) wskazywa\u0142aby, \u017ce co\u015b jest blokowane. Tak jest w istocie. Lock \u201eblokuje\u201d mo\u017cliwo\u015b\u0107 zwolnienia pami\u0119ci i usuni\u0119cia obiektu, na kt\u00f3ry wskazuje weak_ptr. Lock wraca wska\u017anik typu shared_ptr. Licznik referencji w tym momencie idzie w g\u00f3r\u0119, gdy\u017c dostali\u015bmy normalny, \u201epe\u0142nowarto\u015bciowy\u201d inteligentny wska\u017anik.<\/p>\n<p>Zobaczmy, co si\u0119 dzieje w dalszej cz\u0119\u015bci programu. Linijka 21 \u2013 sprawdzamy, czy weak1 dalej wskazuje na poprawn\u0105 warto\u015b\u0107. Intuicja podpowiada \u017ce tak, gdy\u017c skojarzony z nim wska\u017anik shared_ptr (a zarazem obiekt, na kt\u00f3ry wskazuje) nie zosta\u0142 zniszczony. Mo\u017cemy bez problemu skorzysta\u0107 z obiektu, na kt\u00f3ry wskazuje wska\u017anik. Pokazuje to linijka 21. Oczywi\u015bcie, musimy wcze\u015bniej \u201ezablokowa\u0107 wska\u017anik\u201d za pomoc\u0105 metody lock().<\/p>\n<p>Zastan\u00f3w si\u0119, co b\u0119dzie dzia\u0142o si\u0119 ze wska\u017anikiem wsk2. Wska\u017anik shared_ptr by\u0142 zdefiniowany w \u201ewewn\u0119trznym bloku\u201d programu, kt\u00f3rego wykonywanie ju\u017c si\u0119 zako\u0144czy\u0142o. Warto\u015b\u0107 licznika referencji shared_wsk2 spad\u0142a do zera, a zatem obiekt razem ze wska\u017anikiem zosta\u0142 zniszczony. Intuicja podpowiada, \u017ce weak1 wskazuje na jaki\u015b w tej chwili nieprawid\u0142owy obiekt. I intuicja si\u0119 nie myli. Dostali\u015bmy b\u0142\u0105d: assertion failed. Metoda weak2.expired() zwr\u00f3ci\u0142a false.<\/p>\n<h2 class=\"western\">Analiza metody lock() weak_ptr<\/h2>\n<p>W poprzednim rozdziale u\u017cywali\u015bmy metody expired() do zweryfikowania, czy dany wska\u017anik weak_ptr wskazuje na co\u015b po\u017cytecznego. Nie jest to jedyna metoda osi\u0105gni\u0119cia celu.<\/p>\n<p>Uruchom poni\u017cszy, prosty program:<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\n#include <iostream>\r\n#include <memory>\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    weak_ptr<int> w1;\r\n    weak_ptr<int> w2;\r\n    {\r\n        shared_ptr<int> wsk1 = make_shared<int>(42);\r\n        w1 = wsk1;\r\n        cout<<\"w1.lock() = \"<<w1.lock()<<endl;\r\n        cout<<\"w2.lock() = \"<<w2.lock()<<endl;\r\n    }\r\n    cout<<endl;\r\n    cout<<\"w1.lock() = \"<<w1.lock()<<endl;\r\n    cout<<\"w2.lock() = \"<<w2.lock()<<endl;\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Na pocz\u0105tku tworzymy dwa weak_ptry: w1 i w2. W wewn\u0119trznym bloku tworzymy sobie dodatkowo shared_ptra wskazuj\u0105cego na obiekt typu int. Najpierw przypisujemy w1 do tego wska\u017anika. Nast\u0119pnie sprawdzamy, na co wskazuj\u0105 w1 i w2 po zablokowaniu. Wska\u017anik, kt\u00f3ry nigdy nie zosta\u0142 u\u017cyty - w2 - wskazuje na 0, czyli nullptr. Natomiast wska\u017anik w1 przechowuje poprawny adres.<\/p>\n<p>Po opuszczeniu bloku obydwa wska\u017aniki wskazuj\u0105 na nullptr. Fajne, nie? Oznacza to, \u017ce nie zawsze musimy u\u017cywa\u0107 metody expired(). Wystarczy sprawdzi\u0107, czy zablokowanie obiektu wykona\u0142o si\u0119 prawid\u0142owo.<\/p>\n<h1 class=\"western\">Zastosowanie weak_ptr - Praktyczne przyk\u0142ady<\/h1>\n<h2 class=\"western\">weak_ptr i wisz\u0105ce wska\u017aniki<\/h2>\n<p>Wiesz ju\u017c, jak dzia\u0142a weak_ptr. Czytaj\u0105c poprzedni akapit pozna\u0142e\u015b metody, kt\u00f3re s\u0105 unikalne dla tego rodzaju wska\u017anik\u00f3w. Niemniej, dalej mo\u017cesz zastanawia\u0107\u00a0si\u0119, jakie jest praktyczne zastosowanie tego typu wynalazku. C\u00f3\u017c. Aby dobrze operowa\u0107 \u201einteligentnymi wska\u017anikami\u201d musimy mie\u0107 wpraw\u0119 i \u201ewyczuwa\u0107\u201d kiedy nale\u017cy u\u017cy\u0107 takiego, a kiedy innego.<\/p>\n<p>Weak_ptr ma sporo zastosowa\u0144. Jednym z nich jest rozwi\u0105zanie problemu tzw. \u201ewisz\u0105cych wska\u017anik\u00f3w\u201d. Na czym on polega?<\/p>\n<p>\u201eWisz\u0105cy wska\u017anik\u201d wskazuje na pami\u0119\u0107, kt\u00f3ra zosta\u0142a ju\u017c zwolniona za pomoc\u0105 operatora delete lub delete[]. Lecz wska\u017anik nie zosta\u0142 wynullowany. Je\u015bli spr\u00f3bujemy odczyta\u0107 co\u015b z takiego wska\u017anika, stanie si\u0119 co\u015b niedobrego. Program albo zwr\u00f3ci nieprawid\u0142owe dane, albo scrashuje si\u0119.<\/p>\n<p>Sp\u00f3jrz na poni\u017cszy przyk\u0142ad:<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\n#include <iostream>\r\n#include <memory>\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n    \/\/ normalne u\u017cycie\r\n    int* wsk = nullptr;\r\n    int* data = new int(25);\r\n    wsk = data;\r\n    cout<<\"*wsk = \"<<*wsk<<endl;\r\n    delete data;\r\n    data = nullptr;\r\n   \/\/ cout<<\"*wsk = \"<<*wsk<<endl; \/\/nieprawid\u0142owe odwo\u0142anie - data zosta\u0142o zwolnione, lecz wsk dalej wskazuje na zwolnion\u0105 przestrze\u0144 w pami\u0119ci. Crash gwarantowany\r\n\r\n   \/\/Jak to rozwi\u0105za\u0107 za pomoc\u0105 inteligentnych wska\u017anik\u00f3w?\r\n    weak_ptr<int> weakwsk1;\r\n    shared_ptr<int> shwsk1 = make_shared<int>(25);\r\n    weakwsk1 = shwsk1;\r\n    if(weakwsk1.lock()!=nullptr) {\r\n        cout<<\"*weakwsk1.lock() = \"<<*weakwsk1.lock()<<endl;\r\n    } else {\r\n        cout<<\"*weakwsk1 is expired\"<<endl;\r\n    }\r\n    shwsk1.reset(); \/\/ zwalniamy pami\u0119\u0107 przypisan\u0105 do shwsk1\r\n    weakwsk1 = shwsk1;\r\n    if(weakwsk1.lock()!=nullptr) {\r\n        cout<<\"*weakwsk1.lock() = \"<<*weakwsk1.lock()<<endl;\r\n    } else {\r\n        cout<<\"*weakwsk1 is expired\"<<endl;\r\n    }\r\n    return 0;\r\n}\r\n\r\n<\/pre>\n<p>Na samym pocz\u0105tku programu, w linijkach 9-14 \u015bwiadomie tworzymy wisz\u0105cy wska\u017anik (dangling pointer). Po wykonaniu 14 linijki wsk wskazuje na pami\u0119\u0107, kt\u00f3ra zosta\u0142a ju\u017c dawno zwolniona. Je\u015bli zapomnimy po zwolnieniu pami\u0119ci ustawi\u0107 wska\u017anika na nullptr, to nie ma \u017cadnej mo\u017cliwo\u015bci na unikni\u0119cie takiej sytuacji.<\/p>\n<p>Od linijki 18 operujemy ju\u017c na wska\u017anikach inteligentnych. Pr\u00f3bujemy zasymulowa\u0107 tak\u0105 sam\u0105 sytuacj\u0119 jak wcze\u015bniej. Tworzymy sobie weak_ptr weakwsk1 (odpowiednik wsk z linijki 9) i shared_ptr shwsk1 (odpowiednik data z linijki 10). Najpierw przypisujemy weakwsk1 do shwsk1. Nast\u0119pnie sprawdzamy, czy wska\u017aniki s\u0105 nadal aktualne. Pierwsza weryfikacja, kt\u00f3ra nast\u0119puje w linijce 21 powiedzie si\u0119. Potem, w 26 linijce resetujemy shwsk1. W tym momencie pami\u0119\u0107 jest zwalniana a shwsk1 wskazuje na nullptr. Co dzieje si\u0119 z weakwsk1? Pokazuje to linijka 28. Metoda lock zwraca nullptr. co oznacza, \u017ce obiekt na kt\u00f3ry wskazywa\u0142 wska\u017anik ju\u017c nie istnieje. Wy\u015bwietla si\u0119 napis weakwsk1 is expired a my zostali\u015bmy uratowani przed wisz\u0105cym wska\u017anikiem!<\/p>\n<h2 class=\"western\">weak_ptr w algorytmice - drzewo<\/h2>\n<p>Weak_ptr mo\u017ce by\u0107 przydatny przy realizacji r\u00f3\u017cnego rodzaju algorytm\u00f3w. Sp\u00f3jrz na poni\u017cszy przyk\u0142ad. Podobnie jak poprzednie, tak\u017ce i ten konsekwentnie om\u00f3wimy linijka po linijce \ud83d\ude42<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\n#include <iostream>\r\n#include <memory>\r\n#include <vector>\r\nusing namespace std;\r\nstruct TreeNode {\r\n    weak_ptr<TreeNode> parent;\r\n    vector<shared_ptr<TreeNode>> children;\r\n    int key;\r\n};\r\nvoid addChildren(shared_ptr<TreeNode> root,int value) {\r\n    shared_ptr<TreeNode> child = make_shared<TreeNode>();\r\n    child->key = value;\r\n    root->children.push_back(child);\r\n    child->parent = root;\r\n}\r\nvoid printValue(shared_ptr<TreeNode> root,int level = 1) {\r\n    if(root==nullptr) {\r\n        cout<<\"Root is empty\"<<endl;\r\n        return;\r\n    }\r\n    int new_level = level+1;\r\n    for(int i = 0;i<root->children.size();i++) {\r\n        printValue(root->children[i],new_level);\r\n    }\r\n    cout<<\"Level: \"<<level<<endl;\r\n    cout<<\"Value: \"<<root->key<<endl;\r\n}\r\nint main()\r\n{\r\n    shared_ptr<TreeNode> root = make_shared<TreeNode>();\r\n    root->key = 10;\r\n    addChildren(root,30);\r\n    addChildren(root,15);\r\n    addChildren(root->children[0],20);\r\n    addChildren(root->children[0],17);\r\n    printValue(root);\r\n    cout<<\"Clear tree\"<<endl;\r\n    root.reset();\r\n    printValue(root);\r\n    return 0;\r\n}\r\n<\/pre>\n<p>W linijce 5 definiujemy struktur\u0119 TreeNode. \u201eDzieci\u201d przechowujemy w wektorze wska\u017anik\u00f3w shared_ptr. Jest to logiczne. Lecz dlaczego wska\u017anik na rodzica jest typu weak_ptr? Jest tak, gdy\u017c dzi\u0119ki temu usuwanie element\u00f3w drzewa jest mocno u\u0142atwione. Weak_ptr pozwala nam na uzyskanie obiektu rodzica z poziomu dziecka, ale jednocze\u015bnie nie zwi\u0119ksza licznika referencji dziecka. Dzi\u0119ki temu zwyk\u0142e children[0].reset() potrafi usun\u0105\u0107 ca\u0142\u0105 ga\u0142\u0105\u017a drzewa bez \u017cadnych problem\u00f3w. Natomiast root.reset() usuwa ca\u0142e drzewo. Efekt takiego post\u0119powania wida\u0107 w linijkach 38 i 39 powy\u017cszej aplikacji.<\/p>\n<p align=\"left\"><span style=\"font-family: Liberation Sans, sans-serif;\"><span style=\"font-size: xx-large;\"><b>Podsumowuj\u0105c\u2026<\/b><\/span><\/span><\/p>\n<p align=\"left\">Weak_ptr, podobnie jak inne inteligentne wska\u017aniki, jest bardzo przydatny w niekt\u00f3rych scenariuszach. Pozwala na rozwi\u0105zanie wielu problem\u00f3w, kt\u00f3rych nie pokonaliby\u015bmy stosuj\u0105c zwykle staro\u015bwieckie wska\u017aniki udost\u0119pniane przez j\u0119zyk C. Polecam po\u0107wiczy\u0107 ich u\u017cywanie. Je\u015bli nabierzesz wprawy, nie b\u0119dziesz m\u00f3g\u0142 patrze\u0107 na relikt z czas\u00f3w C \u2013 czyli \u201eraw pointery\u201d.<\/p>\n<p>Wszystkie kody, kt\u00f3re znajduj\u0105 si\u0119 w tym artykule znajdziesz tak\u017ce w <a href=\"https:\/\/github.com\/karol221-10\/blog_apps\">moim repozytorium na Githubie<\/a>, w folderze <a href=\"https:\/\/github.com\/karol221-10\/blog_apps\/tree\/master\/weak_ptr\">weak_ptr<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">&lt; 1<\/span> <span class=\"rt-label rt-postfix\">minut\u0119<\/span><\/span> Om\u00f3wili\u015bmy unique_ptr i shared_ptr. Jeste\u015bmy bardzo blisko ko\u0144ca naszej fascynuj\u0105cej w\u0119dr\u00f3wki przez \u015bwiat inteligentnych wska\u017anik\u00f3w. Zosta\u0142 nam do om\u00f3wienia ostatni gatunek. Weak_ptr. W t\u0142umaczeniu na polski by\u0142by to \u201es\u0142aby wska\u017anik\u201d. &#8230;<\/p>\n","protected":false},"author":1,"featured_media":463,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[31],"tags":[],"_links":{"self":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/358"}],"collection":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/comments?post=358"}],"version-history":[{"count":7,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/358\/revisions"}],"predecessor-version":[{"id":698,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/358\/revisions\/698"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media\/463"}],"wp:attachment":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media?parent=358"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/categories?post=358"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/tags?post=358"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}