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?
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.
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";
}
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
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.
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());
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.
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.
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.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();
]
- Raw Pointer [ tj.
- 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 );
]
- unikalne wskaźniki [tj.
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ć.
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.
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.
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.
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