различия shared\_ptr и weak\_ptr

<p>я читаю книгу скотта мейерса "эффективный c++". было упомянуто, что есть <code>tr1::shared\_ptr</code> и <code>tr1::weak\_ptr</code>, действующие как встроенные указатели, но они отслеживают, сколько <code>tr1::shared\_ptrs</code> указывают на объект.</p><p>это известно как подсчет ссылок. это хорошо работает для предотвращения утечек ресурсов в ациклических структурах данных, но если два или более объектов содержат <code>tr1::shared\_ptrs</code>, так что цикл сформирован, цикл может держать счетчик ссылок друг друга выше нуля, даже если все внешние указатели на цикл были уничтожены.</p><p>вот где приходит <code>tr1::weak\_ptrs</code>.</p><p>мой вопрос заключается в том, как циклические структуры данных делают счетчик ссылок выше нуля. я любезно прошу пример программы на c++. как проблема решается с помощью <code>weak\_ptrs</code>? (еще раз, с примером, пожалуйста).</p>

<p><code>shared\_ptr</code> оборачивает механизм подсчета ссылок вокруг необработанного указателя. таким образом, для каждого экземпляра <code>shared\_ptr</code> счетчик ссылок увеличивается на единицу. если два объекта <code>share\_ptr</code> ссылаются друг на друга, они никогда не будут удалены, потому что у них никогда не будет нулевого счетчика ссылок.</p><p><code>weak\_ptr</code> указывает на <code>shared\_ptr</code>, но не увеличивает его счетчик ссылок. это означает, что базовый объект все еще может быть удален, даже если на него есть ссылка <code>weak\_ptr</code>.</p><p>это работает так, что <code>weak\_ptr</code> можно использовать для создания <code>shared\_ptr</code> всякий раз, когда кто-то хочет использовать базовый объект. однако если объект уже был удален, возвращается пустой экземпляр <code>shared\_ptr</code>. поскольку счетчик ссылок на базовый объект не увеличивается с помощью ссылки <code>weak\_ptr</code>, циклическая ссылка не приведет к тому, что базовый объект не будет удален.</p>

<p>позвольте мне повторить ваш вопрос: "мой вопрос, как циклические структуры данных делают счетчик ссылок выше нуля, просьба показать его с примером в программе на c++. как проблема решается с помощью <code>weak\_ptrs</code> еще раз с примером, пожалуйста".</p><p>проблема возникает с кодом c++ следующим образом (концептуально):</p><pre><code>class a { shared\_ptr<b> b; ... }; class b { shared\_ptr<a> a; ... }; shared\_ptr<a> x(new a); // +1 x->b = new b; // +1 x->b->a = x; // +1 // ref count of 'x' is 2. // ref count of 'x->b' is 1. // when 'x' leaves the scope, there will be a memory leak: // 2 is decremented to 1, and so both ref counts will be 1. // (memory is deallocated only when ref count drops to 0) </code></pre><p>чтобы ответить на вторую часть вашего вопроса: для подсчета ссылок математически невозможно иметь дело с циклами. следовательно, <code>weak\_ptr</code> (который в основном является просто урезанной версией <code>shared\_ptr</code>) <strong>не может</strong> может использоваться для решения проблемы цикла - программист решает проблему цикла.</p><p>чтобы решить эту проблему, программист должен знать об отношениях <strong>владение</strong> между объектами или должен изобрести отношения владения, если такого владения не существует естественным образом.</p><p>вышеупомянутый код c++ может быть изменен так, чтобы a владел b:</p><pre><code>class a { shared\_ptr<b> b; ... }; class b { weak\_ptr<a> a; ... }; shared\_ptr<a> x(new a); // +1 x->b = new b; // +1 x->b->a = x; // no +1 here // ref count of 'x' is 1. // ref count of 'x->b' is 1. // when 'x' leaves the scope, its ref count will drop to 0. // while destroying it, ref count of 'x->b' will drop to 0. // so both a and b will be deallocated. </code></pre><p>важный вопрос: можно ли использовать <code>weak\_ptr</code> в том случае, если программист не может определить отношения владения и не может установить статическое владение из-за отсутствия привилегий или отсутствия информации?</p><p>ответ таков: если владение объектами неясно, <code>weak\_ptr</code> <strong>не может</strong> помочь. если есть цикл, программист должен найти его и сломать. альтернативное решение - использовать язык программирования с полной сборкой мусора (например, java, c #, go, haskell) или использовать консервативный (= несовершенный) сборщик мусора, который работает с c/c++ (например, boehm gc). ,.</p>

<p>для будущих читателей.<br> просто хочу отметить, что объяснение, данное atom, отлично, вот рабочий код</p><pre><code>#include <memory> // and others using namespace std; class b; // forward declaration // for clarity, add explicit destructor to see that they are not called class a { public: shared\_ptr<b> b; ~a() {cout << "~a()" << endl; } }; class b { public: shared\_ptr<a> a; ~b() {cout << "~b()" << endl; } }; shared\_ptr<a> x(new a); //x->b share\_ptr is default initialized x->b = make\_shared<b>(); // you can't do "= new b" on shared\_ptr x->b->a = x; cout << x.use\_count() << endl; </code></pre>

<p>слабые указатели просто "наблюдают" за управляемым объектом; они не "поддерживают его жизнь" и не влияют на его жизнь. в отличие от <code>shared\_ptr</code>, когда последний <code>weak\_ptr</code> выходит из области видимости или исчезает, указанный объект все еще может существовать, поскольку <code>weak\_ptr</code> не влияет на время жизни объекта - он не имеет прав собственности. <code>weak\_ptr</code> может использоваться для определения того, существует ли объект, и для предоставления <code>shared\_ptr</code>, который можно использовать для ссылки на него.</p><p>определение <code>weak\_ptr</code> предназначено для того, чтобы сделать его относительно надежным, поэтому в результате очень мало что можно сделать напрямую с <code>weak\_ptr</code>. например, вы не можете разыменовать это; ни <code>operator*</code>, ни <code>operator-></code> не определены для <code>weak\_ptr</code>. вы не можете получить доступ к указателю на объект с ним - нет функции <code>get()</code>. существует определенная функция сравнения, позволяющая хранить <code>weak\_ptrs</code> в упорядоченном контейнере, но это все.</p>