Kiedy std:: weak PST jest przydatny?

Zacząłem studiować Inteligentne Wskaźniki C++11 i nie widzę żadnego użytecznego zastosowania std::weak_ptr. Czy ktoś może mi powiedzieć kiedy std::weak_ptr jest przydatne / konieczne?

Author: curiousguy, 2012-08-20

11 answers

Dobrym przykładem może być pamięć podręczna.

Dla Ostatnio dostępnych obiektów, chcesz zachować je w pamięci, więc trzymaj silny wskaźnik do nich. Okresowo skanujesz pamięć podręczną i decydujesz, które obiekty nie były ostatnio dostępne. Nie musisz trzymać ich w pamięci, więc pozbądź się silnego wskaźnika.

Ale co jeśli ten obiekt jest w użyciu i jakiś inny kod ma do niego silny wskaźnik? Jeśli bufor pozbędzie się swojego jedynego wskaźnika do obiektu, nigdy go nie znajdzie jeszcze raz. Tak więc pamięć podręczna zachowuje słaby wskaźnik do obiektów, które musi znaleźć, jeśli zdarzy się, że pozostaną w pamięci.

To jest dokładnie to, co robi słaby wskaźnik-pozwala zlokalizować obiekt, jeśli nadal jest w pobliżu, ale nie utrzymuje go w pobliżu, jeśli nic innego go nie potrzebuje.

 159
Author: David Schwartz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-19 23:06:28

std::weak_ptr to bardzo dobry sposób na rozwiązanie problemu zwisającego wskaźnika. Używając tylko surowych wskaźników nie jest możliwe ustalenie, czy dane odniesienia zostały zdalokowane, czy też nie. Zamiast tego, pozwalając std::shared_ptr zarządzać danymi i dostarczając std::weak_ptr użytkownikom danych, użytkownicy mogą sprawdzić ważność danych przez wywołanie expired() lub lock().

Nie można tego zrobić samemu std::shared_ptr, ponieważ wszystkie instancje std::shared_ptr mają wspólną własność danych, które nie są usuwane przed wszystkimi instancjami z std::shared_ptr są usuwane. Oto przykład jak sprawdzić wskaźnik wiszący za pomocą lock():

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}
 210
Author: sunefred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-20 21:19:05

Kolejna odpowiedź, mam nadzieję, że prostsza. (dla innych googlerów)

Załóżmy, że masz Team i Member obiekty.

Oczywiście jest to relacja: obiekt Team będzie miał wskaźniki do swojego Members. I jest prawdopodobne, że członkowie będą również mieli wskaźnik wstecz do swojego Team obiektu.

Wtedy masz cykl zależności. Jeśli użyjesz shared_ptr, obiekty nie będą już automatycznie zwalniane, gdy zrezygnujesz z odniesienia do nich, ponieważ odwołują się do siebie cyklicznie. To wyciek pamięci.

Łamiesz to używając weak_ptr. "Właściciel "zazwyczaj używa shared_ptr, a" owned " używa weak_ptr do swojego rodzica i przekształca go tymczasowo na shared_ptr, gdy potrzebuje dostępu do swojego rodzica.

Przechowuj słaby PST:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared

Następnie użyj go w razie potrzeby

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( not tempParentSharedPtr ) {
  // yes it may failed if parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope
 84
Author: Offirmo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-09 11:44:15

Oto jeden przykład, podany mi przez @jleahy: Załóżmy, że masz zbiór zadań, wykonywanych asynchronicznie i zarządzanych przez std::shared_ptr<Task>. Możesz chcieć zrobić coś z tymi zadaniami okresowo, więc zdarzenie timer może przejść std::vector<std::weak_ptr<Task>> i dać zadaniom coś do zrobienia. Jednocześnie jednak zadanie mogło jednocześnie zadecydować, że nie jest już potrzebne i umrzeć. Timer może więc sprawdzić, czy zadanie jest nadal żywe, tworząc współdzielony wskaźnik ze słabego wskaźnika i używając tego współdzielony wskaźnik, pod warunkiem, że nie jest null.

 17
Author: Kerrek SB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-19 23:07:41

weak_ptr dobrze jest również sprawdzić poprawność usuwania obiektu-szczególnie w testach jednostkowych. Typowy przypadek użycia może wyglądać tak:

std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());
 12
Author: Cookie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-05 12:55:53

Shared_ptr : przechowuje rzeczywisty obiekt.

Weak_ptr : używa lock do połączenia z rzeczywistym właścicielem lub zwraca NULL w przeciwnym razie.

słaby ptr

Z grubsza rzecz biorąc, weak_ptr rola jest podobna do roli Agencji Mieszkaniowej . Bez agentów, aby dostać dom na czynsz, będziemy musieli sprawdzić losowe domy w mieście. Agenci upewniają się, że odwiedzamy tylko te domy, które są wciąż dostępne i dostępne do wynajęcia.

 9
Author: Saurav Sahu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-03 07:33:49

Są przydatne Z Boost.Asio, gdy nie masz gwarancji, że obiekt docelowy nadal istnieje, gdy wywoływana jest asynchroniczna obsługa. Sztuczka polega na związaniu weak_ptr do asynchronicznego obiektu obsługi, używając std::bind lub przechwytywania lambda.

void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}
Jest to wariant idiomu self = shared_from_this() często spotykanego w Boost.Przykłady Asio, gdzie oczekująca asynchroniczna obsługa nie przedłuży żywotność obiektu docelowego, ale nadal jest Bezpieczna, jeśli obiekt docelowy zostanie usunięty.
 9
Author: Emile Cormier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 14:26:42

Podczas korzystania ze wskaźników ważne jest, aby zrozumieć różne typy wskaźników dostępne i kiedy ma sens korzystanie z każdego z nich. Istnieją cztery rodzaje wskaźników w dwóch kategoriach w następujący sposób:

  • raw pointers:
    • Raw Pointer [ tj. SomeClass* ptrToSomeClass = new SomeClass();]
  • Inteligentne Wskaźniki:
    • unikalne wskaźniki [tj. std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() ); ]
    • Shared Pointers [ tj. std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() );]
    • słabe wskaźniki [ tj. std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr ); ]

Raw pointers (czasami określane jako "starsze wskaźniki" lub "wskaźniki C") zapewniają zachowanie wskaźnika "gołe kości" i są częstym źródłem błędów i wycieków pamięci. Surowe wskaźniki nie zapewniają możliwości śledzenia własności zasobu, a programiści muszą ręcznie wywołać "delete", aby upewnić się, że nie tworzą wycieku pamięci. Staje się to trudne, jeśli zasób jest współdzielony, ponieważ może być trudne, aby wiedzieć, czy jakiekolwiek obiekty nadal wskazują na zasób. Z tych powodów surowe wskaźniki powinny być zazwyczaj unikane i używane tylko w sekcjach o krytycznym wpływie na wydajność kodu o ograniczonym zakresie.

Unique pointers to podstawowy inteligentny wskaźnik, który "posiada" podstawowy surowy wskaźnik do zasobu i jest odpowiedzialny za wywołanie delete i zwolnienie przydzielonej pamięci, gdy obiekt, który jest właścicielem unikalnego wskaźnika, wyjdzie poza zakres. Nazwa "unique" odnosi się do faktu, że tylko jeden obiekt może "posiadać" unikalny wskaźnik w danym momencie. Własność może zostać przeniesiona na inny obiekt poprzez polecenia move, ale unikalnego wskaźnika nie można kopiować ani udostępniać. Z tych powodów, unikalne wskaźniki są dobrą alternatywą dla surowych wskaźników w przypadku, gdy tylko jeden obiekt potrzebuje wskaźnika w danym czasie, co zwalnia programistę z konieczności zwalniania pamięci pod koniec cyklu życia posiadającego obiekt.

Wskaźnik współdzielony to inny rodzaj inteligentnego wskaźnika, który jest podobny do unikalnych wskaźników, ale pozwala wielu obiektom na posiadanie współdzielonego wskaźnika. Jak unique pointer, shared pointers są odpowiedzialne za zwolnienie przydzielonej pamięci po wykonaniu wszystkich obiektów wskazujących na zasób. Osiąga to za pomocą techniki zwanej liczeniem odniesienia. Za każdym razem, gdy nowy obiekt przejmuje własność współdzielonego wskaźnika, liczba referencji jest zwiększana o jeden. Podobnie, gdy obiekt wykracza poza zakres lub przestaje wskazywać na zasób, liczba referencji jest zmniejszana o jeden. Gdy liczba referencji osiągnie zero, przydzielona pamięć jest zwalniana. Na z tych powodów współdzielone wskaźniki są bardzo potężnym rodzajem inteligentnego wskaźnika, który powinien być używany w dowolnym momencie, gdy wiele obiektów musi wskazywać na ten sam zasób.

Wreszcie, słabe wskaźniki to kolejny rodzaj inteligentnego wskaźnika, który zamiast wskazywać bezpośrednio na zasób, wskazuje na inny wskaźnik (słaby lub współdzielony). Słabe wskaźniki nie mogą uzyskać bezpośredniego dostępu do obiektu, ale mogą stwierdzić, czy obiekt nadal istnieje lub czy wygasł. Słaby wskaźnik może być tymczasowo zamieniony na smart wskaźnik dostępu do obiektu wskazywanego (pod warunkiem, że nadal istnieje). Aby zilustrować, rozważ następujący przykład:

  • jesteś zajęty i masz nakładające się spotkania: spotkanie A i spotkanie B
  • decydujesz się na spotkanie A, a twój współpracownik idzie na spotkanie B
  • powiedz współpracownikowi, że jeśli spotkanie B nadal trwa po zakończeniu spotkania A, dołączysz
  • następujące dwa scenariusze mogą się rozegrać:
    • spotkanie A kończy się, a spotkanie B nadal trwa, więc dołączasz
    • Spotkanie A się kończy, a spotkanie B również się kończy, więc nie dołączasz

W przykładzie masz słaby wskaźnik do spotkania B. nie jesteś "właścicielem" spotkania B, więc może się ono zakończyć bez Ciebie i nie wiesz, czy się skończyło, czy nie, chyba że sprawdzisz. Jeśli to się nie skończyło, możesz dołączyć i uczestniczyć, w przeciwnym razie nie możesz. Jest to inne niż posiadanie współdzielonego wskaźnika do spotkania B, ponieważ będziesz wtedy "właścicielem" zarówno spotkania A, jak i spotkania B (uczestnicząc w obu jednocześnie).

Przykład pokazuje, jak działa słaby wskaźnik i jest przydatny, gdy obiekt musi być zewnętrznym obserwatorem , ale nie chce odpowiedzialności za własność. Jest to szczególnie przydatne w scenariuszu, w którym dwa obiekty muszą wskazywać na siebie (a.k. a. a circular reference). W przypadku współdzielonych wskaźników żaden z obiektów nie może zostać zwolniony, ponieważ są one nadal "silnie" wskazywane przez drugi obiekt. Ze słabymi wskaźnikami, obiekty mogą być dostępne, gdy są potrzebne, i uwolnione, gdy nie muszą już istnieć.

 7
Author: Jeremy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-08 03:52:16

Http://en.cppreference.com/w/cpp/memory/weak_ptr std:: weak_ptr jest inteligentnym wskaźnikiem, który zawiera odniesienie nie będące własnością ("słabe") do obiektu, który jest zarządzany przez STD::shared_ptr. Musi być przekonwertowany na std:: shared_ptr w celu uzyskania dostępu do obiektu odniesienia.

STD::weak_ptr models własność tymczasowa: gdy obiekt musi być dostępny tylko wtedy, gdy istnieje i może zostać usunięty w dowolnym momencie przez kogoś innego, std:: weak_ptr jest używany do śledzenia obiektu i jest konwertowany do std:: shared_ptr to access temporary ownership. Jeśli oryginalna metoda std::shared_ptr zostanie zniszczona w tym momencie, żywotność obiektu zostanie przedłużona do czasu zniszczenia tymczasowej metody STD:: shared_ptr.

DODATKOWO, std:: weak_ptr jest używany do łamania okrągłych odniesień std:: shared_ptr.

 1
Author: MYLOGOS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-12 11:57:47

Istnieje wada dzielonego wskaźnika: shared_pointer nie może obsługiwać zależności cyklu rodzic-dziecko. Oznacza, że jeśli Klasa rodzica używa obiektu klasy potomnej za pomocą współdzielonego wskaźnika, w tym samym pliku, jeśli Klasa potomna używa obiektu klasy rodzica. Współdzielony wskaźnik nie spowoduje zniszczenia wszystkich obiektów, nawet współdzielony wskaźnik w ogóle nie wywołuje destruktora w scenariuszu zależności cyklu. zasadniczo współdzielony wskaźnik nie obsługuje mechanizmu zliczania referencji.

To wadę możemy pokonać używając weak_pointer.

 1
Author: ashutosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-02 19:05:07

Gdy nie chcemy posiadać obiektu:

Ex:

class A
{
    shared_ptr<int> sPtr1;
    weak_ptr<int> wPtr1;
}

W powyższej klasie wPtr1 nie jest właścicielem zasobu wskazywanego przez wPtr1. Jeśli zasób zostanie usunięty, wPtr1 wygasnie.

Aby uniknąć zależności kołowej:

shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

Teraz, jeśli zrobimy shared_ptr klasy B I A, use_count obu wskaźników wynosi dwa.

Gdy shared_ptr wychodzi z zakresu, liczba nadal pozostaje 1 i dlatego obiekt A i B nie otrzymuje usunięte.

class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}

Wyjście:

A()
B()

Jak widzimy na wyjściu, wskaźnik A i B nigdy nie są usuwane, a co za tym idzie wyciek pamięci.

Aby uniknąć takiego problemu, użyj weak_ptr w klasie A zamiast shared_ptr, co ma większy sens.

 0
Author: Swapnil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-12 08:04:16