shared_ptr i cykliczna referencja

0

Witam,
Uczę się ostatnio smart pointerów. Znalazłem taki przykład :

class B;
class A
{
public:
 A(  ) : m_sptrB(nullptr) { };
 ~A( )
 {
  cout<<" A is destroyed"<<endl;
 }
 shared_ptr<B> m_sptrB;
};
class B
{
public:
 B(  ) : m_sptrA(nullptr) { };
 ~B( )
 {
  cout<<" B is destroyed"<<endl;
 }
 shared_ptr<A> m_sptrA;
};
//***********************************************************
void main( )
{
 shared_ptr<B> sptrB( new B );
 shared_ptr<A> sptrA( new A );
 sptrB->m_sptrA = sptrA;
 sptrA->m_sptrB = sptrB;
}

W takim przypadku gdy dwie klasy wskazują na siebie nawzajem nastąpi wyciek pamięci. Rozumiem, że aby temu zapobiec powinienem skorzystać z weak_ptr. Nie rozumiem tylko dlaczego tak się dzieje, dlaczego licznik referencji w shared pointer nie może dojść do zera i tym samym usunąć obiektów. Czy ktoś mógłby mi to łopatologicznie wytłumaczyć?

0

A jak ma niby dojść do zera? Przecież każdy z tych obiektów jest przez kogoś wskazywany, ergo istnieje po jednej referencji dla każdego z nich. To ze są nieosiagalne to jest zupełnie inna bajka. Zauważ że tutaj nie ma żadnego algorytmu garbage collection który wykrywałby nieosiagalne obiekty.

0
  1. shared_ptr zwalnia pamięć na obiekt jak licznik referencji dojdzie do 0
  2. licznik referencji schodzi w dół jak wywołujesz destruktor shared_ptr
  3. shared_ptr jest w klasie, więc jego destruktor zostanie wywołany przez destruktor tej klasy
  4. skoro shared_ptr odpowiada za zwolnienie klasy a klasa za zwolnienie shared_ptr to nikt tego nie zrobi.
0

Problem z cykliczną referencją dotyczy wszystkich garbage collectorów stosujących zliczanie referencji.
(shared_ptr jest rodzajem prostego GC)

Liczba referencji obiektu klasy A wynosi 1 (referencja w B) i obiektu klasy B wynosi 1 (referencja w A). To że oba obiekty są razem niedostęne z zewnątrz nie ma znaczenia - są dostępne jeden z drugiego.

Przykład innego rodzaju GC masz np. w .Net Framework: tam liczba referencji nie jest w ogóle zliczana, badana jest właśnie „osiągalność” obiektu. Jest to o tyle fajne, że można robić new, new, new bez opamiętania i nie przejmować się zwalnianiem pamięci. Ale na tym też można się przejechać - i to w drugą stronę: że GC nam sprzed nosa zabierze obiekt, który był jeszcze potrzebny. Rzadko, ale są takie sytuacje.

0

Jeśli rozumiesz dlaczego musisz tutaj użyć weak_ptr, to jak możesz nie rozumieć dlaczego nie możesz użyć shared_ptr?
W dużym skrócie:

  • weak_ptr nie jest właścicielem obiektu, więc to nie on zarządza usuwaniem i alokacją (ofx, jeśli weak_ptr jest usunięty to tymczasowy shared_ptr zwrócony przez weak_ptr musi zwolnić pamięć),
  • shared_ptr współdzieli ten sam obiekt z innymi shared_ptr (podbijając/obniżając licznik).
    W twoim przykładzie podbijasz te liczniki i doprowadzasz do zakleszczenia przez co nie ma opcji żeby zbić te liczniki do 0 i w konsekwencji zwolnić pamięć.

1 użytkowników online, w tym zalogowanych: 0, gości: 1