Как избежать утечки памяти с shared_ptr?

Рассмотрим следующий код.

using boost::shared_ptr;
struct B;
struct A{
    ~A() { std::cout << "~A" << std::endl; }
    shared_ptr<B> b;    
};
struct B {
    ~B() { std::cout << "~B" << std::endl; }
    shared_ptr<A> a;
};

int main() {
    shared_ptr<A> a (new A);
    shared_ptr<B> b (new B);
    a->b = b;
    b->a = a;

    return 0;
}

нет вывода. Десктруктор не вызывается. Утечка памяти. Я всегда считал, что умный указатель помогает избежать утечек памяти.

Что делать, если мне нужны перекрестные ссылки в классах?


person Alexey Malistov    schedule 01.12.2009    source источник


Ответы (1)


Если у вас есть такие циклические ссылки, один объект должен содержать weak_ptr < / a> другому, а не shared_ptr.

Из shared_ptr введение:

Поскольку реализация использует подсчет ссылок, циклы из shared_ptr экземпляров не будут возвращены. Например, если main() содержит от shared_ptr до A, что прямо или косвенно возвращает shared_ptr к A, счетчик использования A будет равен 2. Уничтожение исходного shared_ptr оставит A висячим с счетчиком использования 1. Используйте weak_ptr, чтобы "разбить" циклы ".

Спасибо, Глен, за ссылку.

person James McNellis    schedule 01.12.2009
comment
@Alexey, вот ссылка на документы, где во введении прямо предупреждается об этой проблеме. boost.org/doc/libs/1_41_0/libs/smart_ptr/ shared_ptr.htm - person Glen; 01.12.2009
comment
Который из? И почему бы не заменить обе слабыми ссылками? Это смешно. shared_ptr использовался не просто так. - person curiousguy; 09.10.2011
comment
@curiousguy: Я не уверен, что понимаю ваш вопрос: что вы считаете смешным? Чтобы разорвать цикл, вы должны заменить одну сильную ссылку на слабую; который полностью зависит от варианта использования. Вы не можете заменить все сильные ссылки на слабые, потому что тогда все объекты будут уничтожены, так как не останется владельцев. - person James McNellis; 09.10.2011
comment
@JamesMcNellis что вы считаете смешным? Идея о том, что ссылка может быть заменена не ссылкой (так называемая слабая ссылка). Это безумие. - person curiousguy; 01.06.2012
comment
@curiousguy Это совсем не безумие. Подумайте, когда shared_ptr a и b выходят за пределы области видимости, они оба уменьшают свои внутренние счетчики ссылок. Счетчик никогда не достигает нуля, так как каждый по-прежнему имеет счет по 1 друг от друга. Деструктор НИКОГДА не вызывается, потому что счетчик никогда не достигает нуля. Они держат друг друга в заложниках. Тот или другой должен уступить право собственности; вы выбираете тот, у которого слабая ссылка. Это работает с еще большим количеством звеньев в цепи. Просто одно из звеньев цепи должно быть слабым, чтобы разорвать циклическую зависимость. - person t0rakka; 17.11.2014
comment
@SnappleLVR Предлагаемое решение безумное. Умный ptr работает по мере необходимости, поскольку семантика владения ptr подразумевает, что указанный объект не уничтожается до того, как владелец. IOW, дизайн подразумевает, что объекты не могут быть автоматически уничтожены. одно из звеньев в цепочке должно быть слабым какое? - person curiousguy; 17.11.2014
comment
Исходный дизайн подразумевает, что объекты не могут быть уничтожены автоматически. Обновленное решение гарантирует уничтожение каждого объекта. Проблема в том, что теперь слабый указатель может указывать на несуществующий объект. Сам пример в виде игрушечного кода отлично работает с этими версиями, но что-то более сложное - это совершенно другая тема. Я бы не стал программировать что-то подобное, но ответ правильный и легко доказуемо. К вашему сведению: не знал, что нажатие Enter не приводит к переходу на новую строку, а к публикации. Очевидно, я собирался написать больше выше. :) - person t0rakka; 18.11.2014