Umożliwia uzyskanie prawidłowej shared_ptr
instancji this
, gdy wszystko, co masz, to this
. Bez niego nie byłoby sposobu, aby dostać się shared_ptr
do this
, chyba że masz go już jako członek. Ten przykład z dokumentacji doładowania dla enable_shared_from_this :
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
Metoda f()
zwraca poprawną wartość shared_ptr
, mimo że nie miała instancji elementu. Pamiętaj, że nie możesz tego po prostu zrobić:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Współdzielony wskaźnik, który to zwróci, będzie miał inną liczbę odniesień niż „właściwy”, a jeden z nich straci i utrzyma wiszące odniesienie, gdy obiekt zostanie usunięty.
enable_shared_from_this
stał się częścią standardu C ++ 11. Możesz także pobrać go stamtąd, a także z doładowania.
std::shared_ptr
konstruktora na surowym wskaźniku, jeśli dziedziczystd::enable_shared_from_this
. Nie wiem, czy semantyka wzmocnienia została zaktualizowana w celu obsługi tego.std::shared_ptr
obiektu, który jest już zarządzany przez inny obiektstd::shared_ptr
, nie sprawdzi wewnętrznej słabej referencji przechowywanej wewnętrznie, a tym samym doprowadzi do nieokreślonego zachowania”. ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )shared_ptr<Y> q = p
?std::make_shared<T>
.z artykułu dr Dobbs na temat słabych wskaźników, myślę, że ten przykład jest łatwiejszy do zrozumienia (źródło: http://drdobbs.com/cpp/184402026 ):
... taki kod nie działa poprawnie:
Żaden z dwóch
shared_ptr
obiektów nie wie o drugim, więc oba będą próbowały uwolnić zasób, gdy zostaną zniszczone. To zwykle prowadzi do problemów.Podobnie, jeśli funkcja członka potrzebuje
shared_ptr
obiektu będącego właścicielem obiektu, do którego jest wywoływana, nie może po prostu utworzyć obiektu w locie:Ten kod ma ten sam problem, co we wcześniejszym przykładzie, chociaż w bardziej subtelnej formie. Podczas budowy
shared_pt
obiekt rsp1
jest właścicielem nowo przydzielonego zasobu. Kod wewnątrz funkcjiS::dangerous
składowej nie wie o tymshared_ptr
obiekcie, więcshared_ptr
obiekt, który zwraca, różni się od niegosp1
. Kopiowanie nowegoshared_ptr
obiektu dosp2
nie pomaga; gdysp2
wyjdzie poza zakres, zwolni zasób, a gdysp1
wyjdzie poza zakres, ponownie zwolni zasób.Sposobem uniknięcia tego problemu jest użycie szablonu klasy
enable_shared_from_this
. Szablon przyjmuje jeden argument typu szablonu, który jest nazwą klasy definiującej zarządzany zasób. Ta klasa musi z kolei pochodzić publicznie z szablonu; lubię to:Gdy to zrobisz, pamiętaj, że obiekt, do którego dzwonisz,
shared_from_this
musi być własnościąshared_ptr
obiektu. To nie zadziała:źródło
shared_ptr<S> sp1(new S);
mówiąc , zamiast tego może być preferowane użycieshared_ptr<S> sp1 = make_shared<S>();
, patrz na przykład stackoverflow.com/questions/18301511/…shared_ptr<S> sp2 = p->not_dangerous();
ponieważ pułapką jest to, że musisz utworzyć shared_ptr w normalny sposób, zanim zadzwoniszshared_from_this()
po raz pierwszy! Naprawdę łatwo się pomylić! W wersjach wcześniejszych niż C ++ 17 UB może wywoływaćshared_from_this()
przed utworzeniem dokładnie jednego pliku shared_ptr w normalny sposób:auto sptr = std::make_shared<S>();
lubshared_ptr<S> sptr(new S());
. Na szczęście od C ++ 17 robienie tego rzuci.S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- Dozwolone jest wywoływanie shared_from_this tylko na wcześniej współużytkowanym obiekcie, tj. Na obiekcie zarządzanym przez std :: shared_ptr <T>. W przeciwnym razie zachowanie jest niezdefiniowane (dopóki C ++ 17) nie zostanie wyrzucone std :: bad_weak_ptr (przez konstruktora shared_ptr z domyślnie skonstruowanego słaby_this) (od C ++ 17). . Tak więc rzeczywistość jest taka, że należy ją nazywaćalways_dangerous()
, ponieważ potrzebujesz wiedzy, czy została już udostępniona, czy nie.Oto moje wyjaśnienie z perspektywy orzechów (pierwsza odpowiedź nie „kliknęła” ze mną). * Zauważ, że jest to wynik badania źródła shared_ptr i enable_shared_from_thth, który jest dostarczany z Visual Studio 2012. Być może inne kompilatory implementują enable_shared_from_this inaczej inaczej ... *
enable_shared_from_this<T>
dodaje prywatnąweak_ptr<T>
instancję, doT
której przypisano „ jedną prawdziwą liczbę referencji ” dla instancjiT
.Tak więc, kiedy po raz pierwszy tworzysz
shared_ptr<T>
nowy na T *, wewnętrzny słaby_ptr T * jest inicjalizowany z przeliczeniem 1. Nowy wshared_ptr
zasadzie się na to powracaweak_ptr
.T
następnie w swoich metodach może wywoływać,shared_from_this
aby uzyskać instancjęshared_ptr<T>
tego zaplecza na tej samej wewnętrznie przechowywanej liczbie referencji . W ten sposób zawsze masz jedno miejsce, w którymT*
przechowywana jest liczba odwołań, a nie wieleshared_ptr
wystąpień, które nie znają się nawzajem, i każdy uważa, że to one sąshared_ptr
odpowiedzialne za przeliczanieT
i usuwanie go, gdy ich odwołanie -licznik osiąga zero.źródło
So, when you first create...
to, że jest to wymóg (jak mówisz, słaby_ptr nie jest inicjowany, dopóki nie przekażesz wskaźnika obiektów do ctora shared_ptr!), A to wymaganie jest tam, gdzie rzeczy mogą się potwornie źle pójść, jeśli jesteś nieostrożny. Jeśli przed wywołaniem nie utworzysz pliku shared_ptrshared_from_this
, otrzymasz UB - podobnie, jeśli utworzysz więcej niż jeden plik shared_ptr, otrzymasz również UB. Musisz jakoś upewnić się, że utworzyłeś shared_ptr dokładnie raz.enable_shared_from_this
kruchego” jest na początku krucha, ponieważ chodzi o to, aby móc uzyskaćshared_ptr<T>
odT*
, ale w rzeczywistości, kiedy dostajesz wskaźnikT* t
, generalnie nie jest bezpiecznie zakładać, że cokolwiek jest już udostępnione lub nie, i błędne zgadywanie to UB.Zauważ, że użycie boost :: intrusive_ptr nie cierpi z powodu tego problemu. Jest to często wygodniejszy sposób na obejście tego problemu.
źródło
enable_shared_from_this
umożliwia pracę z interfejsem API, który specjalnie akceptujeshared_ptr<>
. Moim zdaniem taki interfejs API zwykle robi „Zrób to źle” (ponieważ lepiej jest pozwolić, aby coś wyżej w stosie posiadało pamięć), ale jeśli jesteś zmuszony do pracy z takim interfejsem API, jest to dobra opcja.Dokładnie tak samo jest w c ++ 11 i późniejszych wersjach: od tego momentu ma możliwość powrotu
this
jako wskaźnik wspólnythis
daje surowy wskaźnik.innymi słowy, pozwala zmienić kod w ten sposób
zaangażowany w to:
źródło
shared_ptr
. Możesz zmienić interfejs, aby upewnić się, że tak jest.std::shared_ptr<Node> getParent const()
normalnie odsłoniłbym to jakoNodePtr getParent const()
zamiast. Jeśli absolutnie potrzebujesz dostępu do wewnętrznego surowego wskaźnika (najlepszy przykład: radzenie sobie z biblioteką C), jeststd::shared_ptr<T>::get
na to coś, o czym nienawidzę wspominać, ponieważ używam tego surowego wskaźnika zbyt wiele razy z niewłaściwego powodu.Innym sposobem jest dodanie
weak_ptr<Y> m_stub
członka doclass Y
. Następnie napisz:Przydatne, gdy nie możesz zmienić klasy, z której wywodzisz (np. Rozszerzając bibliotekę innych osób). Nie zapomnij zainicjować członka, np. Przez
m_stub = shared_ptr<Y>(this)
, że jest on ważny nawet podczas konstruktora.Jest OK, jeśli istnieje więcej kodów pośredniczących w hierarchii dziedziczenia, nie zapobiegnie to zniszczeniu obiektu.
Edycja: Jak poprawnie wskazał nobar użytkownika, kod zniszczyłby obiekt Y po zakończeniu przypisania i zniszczeniu zmiennych tymczasowych. Dlatego moja odpowiedź jest nieprawidłowa.
źródło
shared_ptr<>
czegoś, co nie usunie jego pointee, jest to przesada. Możesz po prostu powiedzieć,return shared_ptr<Y>(this, no_op_deleter);
gdzieno_op_deleter
jest obiekt jednoargumentowy, któryY*
nic nie bierze i nie robi.m_stub = shared_ptr<Y>(this)
zbuduje i natychmiast zniszczy tymczasowy share_ptr z tego. Po zakończeniu tej instrukcjithis
zostaną usunięte, a wszystkie kolejne odniesienia będą zwisały.enable_shared_from_this
, zachowuje onaweak_ptr
swoją wartość (wypełnioną przez ctor), zwracaną jakoshared_ptr
kiedy zadzwoniszshared_from_this
. Innymi słowy, powielasz to, coenable_shared_from_this
już zapewnia.