Как сломать циклическую ссылку shared_ptr, используя weak_ptr

Я читал, что weak_pointers можно использовать для прерывания циклических ссылок.

Рассмотрим следующий пример циклической ссылки

struct A
{
boost::shared_ptr<A> shrd_ptr;
};


boost::shared_ptr<A> ptr_A(boost::make_shared<A>());
boost::shared_ptr<A> ptr_b(boost::make_shared<A>());
ptr_A->shrd_ptr = ptr_b;
ptr_b->shrd_ptr = ptr_A;

Теперь выше случай циклической ссылки, и я хотел знать, как я могу сломать циклическую ссылку выше, используя weak_ptr ?

Обновление: на основе полученного предложения я придумал следующее:

struct A
{
  boost::weak_ptr<A> wk_ptr;
};

    boost::shared_ptr<A> ptr_A (boost::make_shared<A>());
    boost::shared_ptr<A> ptr_B (boost::make_shared<A>());
    ptr_A->wk_ptr = ptr_B;
    ptr_B->wk_ptr = ptr_A;

Будет ли это правильным подходом?


person Rajeshwar    schedule 23.11.2014    source источник
comment
Измените A::shrd_ptr на boost::weak_ptr и удерживайте shared_ptr в другом месте.   -  person Captain Obvlious    schedule 23.11.2014


Ответы (1)


Классический пример циклических ссылок — это когда у вас есть два класса A и B, где A имеет ссылку на B, который имеет ссылку на A:

#include <memory>
#include <iostream>

struct B;
struct A {
  std::shared_ptr<B> b;  
  ~A() { std::cout << "~A()\n"; }
};

struct B {
  std::shared_ptr<A> a;
  ~B() { std::cout << "~B()\n"; }  
};

void useAnB() {
  auto a = std::make_shared<A>();
  auto b = std::make_shared<B>();
  a->b = b;
  b->a = a;
}

int main() {
   useAnB();
   std::cout << "Finished using A and B\n";
}

Если обе ссылки shared_ptr, то это говорит о том, что A владеет B, а B владеет A, что должно насторожить. Другими словами, A поддерживает жизнь B, а B поддерживает жизнь A.

В этом примере экземпляры a и b используются только в функции useAnB(), поэтому мы хотели бы, чтобы они были уничтожены, когда функция завершается, но, как мы видим, когда мы запускаем программу, деструкторы не вызываются.

Решение состоит в том, чтобы решить, кто кому принадлежит. Допустим, A владеет B, но B не владеет A, тогда мы заменяем ссылку на A в B на weak_ptr вот так:

struct B {
  std::weak_ptr<A> a;
  ~B() { std::cout << "~B()\n"; }  
};

Затем, если мы запустим программу, мы увидим, что a и b уничтожены, как мы и ожидали.

Демо

Редактировать: В вашем случае предложенный вами подход выглядит вполне верным. Отнимите право собственности у A, и кто-то другой будет владеть As.

person Chris Drew    schedule 23.11.2014
comment
останется ли проблема (циклическая ссылка), если мы позже создадим shared_ptr из a.lock()? - person RaGa__M; 09.05.2016
comment
@Explorer_N Если B не удерживает shared_ptr, созданный из a.lock(), то все в порядке. Вот расширенная версия живой демонстрации, которая включает пример использования a.lock(). - person Chris Drew; 09.05.2016
comment
Крис, Как это, создание shared_ptr увеличит количество ссылок на управляемый объект, не так ли? я также изменил auto sa = a.lock(); на static shared_ptr<A> sa;, но он работает хорошо..... мне интересно. - person RaGa__M; 09.05.2016
comment
@Explorer_N Да, auto sa = a.lock() увеличит счетчик ссылок a, но счетчик ссылок снова уменьшится, когда sa выйдет за пределы области видимости, так что это нормально. - person Chris Drew; 09.05.2016
comment
struct B { std::weak_ptr‹A› a; std::shared_ptr‹A› sa; ~B() { std::cout ‹‹ ~B()\n; } void doSomethingWithA() { sa = a.lock(); если (!sa) вернуться; std::cout ‹‹ a.value: ‹‹ sa-›value ‹‹ \n; } }; это не нарушает циклическую ссылку - person RaGa__M; 09.05.2016
comment
код, который вы вставили в поле палочки, имеет «auto sa = a.lock();» shared_ptr внутри функции, но если мы объявим shared_ptr в структуре, проблема не будет решена - person RaGa__M; 09.05.2016
comment
@Explorer_N Действительно, как я уже сказал, еслиB удерживает shared_ptr, то у вас есть циклическая ссылка. - person Chris Drew; 09.05.2016