Как мне раздать слабый_троль этому в моем конструкторе? - PullRequest
9 голосов
/ 15 ноября 2011

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

::std::shared_ptr является естественным для обработки этого. Для обработки DAG были сделаны контрольные подсчеты.

К сожалению, зависимости должны знать своих иждивенцев - когда обновляется зависимость, она должна сообщить всем своим иждивенцам.
Это создает тривиальный цикл, который можно разорвать с помощью ::std::weak_ptr. Зависимости могут просто забыть об уходящих иждивенцах.

Но я не могу найти способ для зависимого создать ::std::weak_ptr для себя, пока он создается.

Это не работает:

object::object(shared_ptr<object> dependency)
{
     weak_ptr<object> me = shared_from_this();
     dependency->add_dependent(me);
     dependencies_.push_back(dependency);
}

Этот код приводит к тому, что деструктор вызывается до выхода из конструктора.

Есть ли хороший способ справиться с этой проблемой? Я очень доволен решением только для C ++ 11.

Ответы [ 8 ]

9 голосов
/ 15 ноября 2011

Вместо конструктора используйте функцию для построения узлов вашего графа.

std::shared_ptr<Node> mk_node(std::vector<std::shared_ptr<Node>> const &dependencies)
{
    std::shared_ptr<Node> np(new Node(dependencies));
    for (size_t i=0; i<dependencies.size(); i++)
        dependencies[i].add_dependent(np);       // makes a weak_ptr copy of np
    return np;
}

Если вы сделаете это static функцией-членом или friend вашего Node класса, вы можете сделать фактический конструктор private.

5 голосов
/ 15 ноября 2011

В принципе, вы не можете. Вам нужно shared_ptr или weak_ptr, чтобы создать weak_ptr, и, очевидно, «я» может знать о своем собственном shared_ptr только в форме weak_ptr (иначе нет смысла считать ссылки). И, конечно же, не может быть самооценки shared_ptr, когда объект еще не построен.

1 голос
/ 15 ноября 2011

Мне кажется, что вы пытаетесь сопоставить несколько разных элементов: один объект (узел в DAG) и управление коллекцией этих объектов.

class DAG { 

    class node {
        std::vector<std::weak_ptr<node> > dependents;
    public:
        node(std::vector<weak_ptr<node> > d) : dependents(d) {}
    };

    weak_ptr<node> root;
};

Теперь может быть верно, что DAG будет всегда содержать weak_ptr<node>, а не иметь дело с экземпляром node напрямую. Однако для самого узла это более или менее не имеет значения. Он должен поддерживать любой ключ / данные / и т. Д., Который он содержит, вместе со своим собственным списком зависимых лиц.

В то же время, вложив его в DAG (особенно если мы сделаем определение класса node закрытым для DAG), мы можем минимизировать доступ к node, поэтому очень мало другого кода должно быть что-нибудь о node. В зависимости от ситуации вам также может потребоваться удалить некоторые (большинство?) Функций в узле, которые компилятор сгенерирует по умолчанию (например, ctor по умолчанию, ctor копирования, оператор присваивания).

1 голос
/ 15 ноября 2011

К сожалению, зависимости должны знать своих иждивенцев.Это связано с тем, что при обновлении зависимости необходимо сообщить всем своим иждивенцам.И есть тривиальный цикл.К счастью, этот цикл можно разорвать с помощью :: std :: weak_ptr.Зависимости могут просто забыть об уходящих иждивенцах.

Звучит так, как будто зависимость не может ссылаться на зависимого, если он не существует.(Например, если зависимый объект уничтожен, зависимый объект тоже уничтожен - в конце концов, это DAG)

Если это так, вы можете просто раздать простые указатели (в данном случае this),Вам никогда не понадобится проверять внутри зависимости, жив ли зависимый, потому что, если зависимый умер, то зависимость также должна была умереть.

То, что объект принадлежит shared_ptr, не означаетчто все указатели на него сами должны быть shared_ptrs или weak_ptrs - просто вам нужно определить четкую семантику относительно того, когда указатели станут недействительными.

1 голос
/ 15 ноября 2011

Вы не можете.

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

1 голос
/ 15 ноября 2011

Я понимаю ваш вопрос как концептуально связанный с сборкой мусора проблемами.

GC - это немодулярная функция: она имеет дело с некоторым глобальным свойством программы (точнее, живые данные - это глобальное, немодулярное свойство внутри программы - есть ситуации, когда вы не можете с этим справиться модульным и композиционным способом.). Стандартные библиотеки AFAIK, STL или C ++ мало помогают для глобальных функций программы.

Возможным ответом может быть использование (или реализация самостоятельно) сборщика мусора; Boehm GC может быть вам полезен, если вы можете использовать его во всей вашей программе.

И вы также можете использовать алгоритмы GC (даже копирование поколений) для решения вашей проблемы.

0 голосов
/ 24 апреля 2012

Это меня тоже сводит с ума.

Я подумал о том, чтобы принять политику использования указателей для прерывания циклов ... Но я действительно не нашел этого, потому что мне действительно нравится то, насколько ясны намеренияслабый_птр - это когда вы видите его в своем коде (вы знаете, что он существует для прерывания циклов).

Сейчас я склоняюсь к написанию своего собственного класса слабого_птра.

0 голосов
/ 15 ноября 2011

Может быть, это поможет:

наследовать от enable_shared_from_this , который в основном содержит weak_ptr.Это позволит вам использовать this->shared_from_this();

shared_ptr для проверки, наследуется ли класс от класса, и использовать классы weak_ptr при указании на объект (предотвращает подсчет ссылок двумя общими указателями по-разному)

Подробнее об этом: cppreference

...