Избегание косвенных циклических ссылок при использовании shared_ptr и weak_ptr - PullRequest
7 голосов
/ 26 января 2010

Я сейчас собираю приложение, которое в значительной степени опирается на shared_ptr, и пока все выглядит хорошо - я сделал свою домашнюю работу и довольно неплохо представляю некоторые подводные камни использования shared_ptr с.

Одна из наиболее известных проблем с shared_ptr - это циклические зависимости - эти проблемы можно решить путем хранения weak_ptr s, которые не влияют на время жизни объектов в цепочке. Тем не менее, я изо всех сил пытаюсь разобраться, когда необходимо сохранить указатель на внешний объект через weak_ptr - Я не уверен, запрещен ли он, не рекомендуется или безопасен .

Следующая диаграмма описывает то, что я имею в виду (черные стрелки указывают shared_ptr; пунктирные обозначают weak_ptr):

альтернативный текст http://img694.imageshack.us/img694/6628/sharedweakptr.png

  • Родитель содержит shared_ptr s для двух детей, оба из которых указывают на родителя с помощью weak_ptr.
  • В конструкторе первого потомка я получаю через родительский weak_ptr указатель на второго потомка и сохраняю его локально.

Код выглядит так:

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>

class child;
class child2;
class parent;

class parent : public boost::enable_shared_from_this<parent>
{
public:
    void createChildren()
    {
        _child2 = boost::make_shared<child2>(shared_from_this());
        _child = boost::make_shared<child>(shared_from_this());
    }

    boost::shared_ptr<child> _child;
    boost::shared_ptr<child2> _child2;
};

class child
{
public:
    child(boost::weak_ptr<parent> p)
    {
        _parent = p;
        _child2 = boost::shared_ptr<parent>(p)->_child2; // is this safe?
    }

    boost::weak_ptr<parent> _parent;
    boost::shared_ptr<child2> _child2;
};

class child2
{
public:
    child2(boost::weak_ptr<parent> p)
    {
        this->_parent = p;
    }

    boost::weak_ptr<parent> _parent;
};

int main()
{
    boost::shared_ptr<parent> master(boost::make_shared<parent>());
    master->createChildren();
}

Я проверил это, и оно кажется работает нормально (я не получаю никаких сообщений об утечках памяти), однако мой вопрос: Это безопасно? А если нет, то почему?

Ответы [ 3 ]

6 голосов
/ 26 января 2010

Кажется, что дочерний конструктор безопасен, как вы его называете. Однако это не безопасно в целом.

Проблема заключается в том, что в качестве аргумента дочернего конструктора передается слабый_птр. Это означает, что вам нужно беспокоиться о том, является ли слабый указатель для объекта, который больше не существует. Изменив этот параметр на shared_ptrs и преобразовав его в weak_ptr при сохранении, мы знаем, что объект все еще существует. Вот изменение:

child(boost::shared_ptr<parent> p)
{
    _parent = p;
    _child2 = p->_child2; // This is this safe
}
3 голосов
/ 08 октября 2011

Одной из наиболее известных проблем с shared_ptr являются циклические зависимости - эти проблемы можно решить путем хранения уязвимых мест, которые не влияют на время жизни объектов в цепочке.

Неправильно. Эта циклическая зависимость существует или не существует.

Если проблема существует, то слабая ссылка просто не подходит.

, где необходимо хранить указатель на внешний объект через слабый_птр

weak_ptr - это почти , который никогда не нужен.

Есть несколько весьма специфических случаев, когда weak_ptr подходит, но в основном это часть культа shared_ptr: вместо случайного броска shared_ptr в задачи, они случайным образом бросают половину shared_ptr и половину weak_ptr ( как видно на SO).

0 голосов
/ 26 января 2010

Вы получите исключение bad_weak_ptr, если 'p' (каким-то образом) уже был уничтожен. Так что это безопасно, если дочерний ctor ожидает исключения и не безопасно в противном случае.

...