Как избежать двухфазного конструирования агрегированных типов, на которые ссылаются общие указатели с обратными ссылками?

Предположим, у меня есть тип Aggregator и один Aggregatee, Первый знает коллекцию последних shared_ptrs. Последний имеет уникальный обратный указатель на первый:

struct Aggregatee {
private:
Aggregator& aggregator;
Aggregatee(Aggregator& aggregator)
: aggregator{aggregator} {
// PROBLEM HERE:
//   I want to put `this` into the aggregation list,
//   but I have no clue how to properly refer to `this`
//   because I don't have the smart pointer object, yet.
}
Aggregatee(Aggregatee const& from) { /* ... */ }
public:
template <typename Args...>
static std::shared_ptr<Aggregatee> create(Args... args) {
return std::make_shared<Aggregatee>(args...);
}
};

struct Aggregator {
std::set<std::shared_ptr<Aggregatee>> aggregation;
};

Очевидно, я могу отложить регистрацию Aggregatee в Aggregator объект после make_shared с частным register функция, но пахнет как двухфазный, так как объект временно непоследовательно инициализированный.

Есть ли известное решение для этого?

0

Решение

В зависимости от вашего приложения вы можете переместить статический метод создания из Aggregatee в обычный метод-член Aggregator. Затем вы можете создать shared_ptr и сохранить его тем же методом.

Это предполагает, что Агрегат всегда связан с Агрегатором.

0

Другие решения

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

Рассмотрим, например, двоичное дерево с обратными указателями, элементы создаются на листьях с указателями на родительский узел, а указатель немедленно сохраняется в родительском узле.

Что касается вашего конкретного вопроса, это не может быть сделано, так как вы не можете получить shared_ptr к объекту до его создания, даже с enable_shared_from_this,

0

Вы можете использовать навязчивый общий указатель. Поскольку счетчик ссылок живет внутри общего объекта, Агрегат может создать общий указатель на себя внутри своего собственного конструктора.

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

http://www.boost.org/doc/libs/1_51_0/libs/smart_ptr/intrusive_ptr.html

// shared base type contains the reference counter; how you implement the
// reference counting depends on your implementation.
struct Aggregatee : public shared {
private:
Aggregator& aggregator;

Aggregatee(Aggregator& aggregator) : aggregator{aggregator} {
// Boost allows a raw pointer to be implicitly cast to an intrusive
// pointer, but maybe your intrusive pointer type won't.
aggregator.aggregation.insert(intrusive_ptr<Aggregatee>(this));
}

Aggregatee(Aggregatee const& from) { /* ... */ }

public:
template <typename Args...>
static intrusive_ptr<Aggregatee> create(Args... args) {
return new intrusive_ptr<Aggregatee>(args...);
}
};

struct Aggregator {
std::set<intrusive_ptr<Aggregatee>> aggregation;
};

Одна вещь, о которой вы должны помнить, это то, что Aggregatee действительно хранится в умном указателе после построения.

Aggregatee aggregatee(aggregator);  // Very bad!
intrusive_ptr<Aggregatee> aggregatee(new Aggregatee(aggregator));  // Good

Кроме того, вы должны быть чертовски уверены, что ваш Агрегатор удерживает intrusive_ptr до тех пор, пока Агрегат не будет полностью построен, иначе Агрегат будет уничтожен до выхода из конструктора!

Таким образом, есть некоторые предостережения, которые приходят с этим подходом, но, насколько я знаю, это самое близкое, что вы собираетесь достичь для достижения того, о чем вы просили.

0
По вопросам рекламы [email protected]