Сохранить слабый указатель на себя

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

class MyClass {
    //...
    boost::weak_ptr<MyClass> m_self;
    //...
};

boost::shared_ptr<MyClass>
Factory::Factory::Factory::CreateMyClass() {
    boost::shared_ptr<MyClass> obj(new MyClass(...));
    boost::weak_ptr<MyClass> p(obj);
    obj->storeSelfPointer(p);
    return obj;
}

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

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

РЕДАКТИРОВАТЬ: я должен упомянуть, что ни одно из мест, использующих результирующий интеллектуальный указатель, полученный в результате блокировки m_self, фактически не сохраняет интеллектуальный указатель.


person James Davidoff    schedule 26.06.2014    source источник
comment
не знал о std::shared_from_this наверное   -  person Ben Voigt    schedule 26.06.2014
comment
Это c++98, более новые вещи не используются.   -  person James Davidoff    schedule 26.06.2014
comment
Хорошо, это версия enable_shared_from_this для C++98.   -  person Ben Voigt    schedule 26.06.2014
comment
не могли бы вы рассказать о классах шаблонов с двумя параметрами, которые когда-либо создавались только для одной пары типов. а также Factory::Factory::Factory::? это интересно   -  person Cheers and hth. - Alf    schedule 26.06.2014
comment
шаблон ‹класс T, класс U› класс Foo; класс Бар; класс Биз; Foo всегда используется только как Foo‹Bar, Biz›; совершенно бессмысленное обобщение поведения.   -  person James Davidoff    schedule 26.06.2014
comment
Factory::Factory::Factory — это шутка о ее любви к чрезмерному использованию шаблонов в стиле Java. Для получения объекта необходимо зарегистрироваться на фабрике фабрики фабрики.   -  person James Davidoff    schedule 26.06.2014


Ответы (1)


Возможное использование этого «дизайна» может заключаться в использовании m_self.lock() для создания общих указателей из this.

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

Он достигает того же, что и std::enable_shared_from_this, что интересно, cppreference.com упоминает этот дизайн:

Распространенной реализацией enable_shared_from_this является хранение слабой ссылки (например, std::weak_ptr) на this. Конструкторы std::shared_ptr обнаруживают наличие базы enable_shared_from_this и назначают вновь созданный std::shared_ptr внутренне сохраненной слабой ссылке.

И стандарт С++, раздел § 20.8.2.4 10, упоминает ту же возможную реализацию:

Конструкторы shared_ptr, которые создают уникальные указатели, могут обнаруживать наличие базы enable_shared_-from_this и назначать только что созданный shared_ptr его члену __weak_this.


Возможный рефакторинг:

  • Если вы используете C++11, вы можете удалить элемент std::weak_ptr и публично наследовать от std::enable_shared_from_this<T>. Вы должны получить из него общий указатель, вызвав shared_from_this().

  • Если вы не используете C++11, но можете использовать boost, используйте boost::enable_shared_from_this, см. ускоренная документация. Вы должны получить из него общий указатель, вызвав shared_from_this().

  • Если вы не используете C++11 и не можете использовать boost, вы можете внести предложенную реализацию стандарта в свою кодовую базу, она достаточно короткая:

Код: (скопировано из § 20.8.2.4–11, удалите начальные символы подчеркивания и, вероятно, захотите переименовать его)

template<class T> class enable_shared_from_this {
    private:
     weak_ptr<T> __weak_this;
    protected:
     constexpr enable_shared_from_this() : __weak_this() { }
     enable_shared_from_this(enable_shared_from_this const &) { }
     enable_shared_from_this& operator=(enable_shared_from_this const &) { return *this; }
     ~enable_shared_from_this() { }
    public:
     shared_ptr<T> shared_from_this() { return shared_ptr<T>(__weak_this); }
     shared_ptr<T const> shared_from_this() const { return shared_ptr<T const>(__weak_this); }
};

И используйте shared_from_this() для создания общего указателя. Если вы скопируете этот код, обратите внимание, что создание общих указателей другими способами не сработает. Конструкторы общих указателей необходимо изменить (как поясняется в приведенной выше стандартной цитате).

person quantdev    schedule 26.06.2014
comment
Это не официальная документация - person Ben Voigt; 26.06.2014
comment
Истинный. На самом деле стандарт показывает точно такую ​​же возможную реализацию. - person quantdev; 26.06.2014
comment
Да, я пытался это подтвердить, но поиск в PDF не работал. Я вижу это сейчас. - person Ben Voigt; 26.06.2014
comment
Примечание, сопровождающее этот пример, довольно важно... как библиотека shared_ptr узнает о существовании вашего enable_shared_from_this клона, не говоря уже о доступе к его __weak_this? - person T.C.; 26.06.2014
comment
@Т.С. : Да, у вас не будет функций, представленных в стандарте, но при использовании shared_from_this() OP сохранит свое текущее поведение. Создание из этого shared_pointer другими способами все равно не будет работать без изменения shared_pointer ctors, я отредактирую. - person quantdev; 26.06.2014
comment
Это объяснение пахнет правильным. Я поставлю вам оценку, и я собираюсь дать ей бонусные баллы за то, что она устроила сложный беспорядок, а затем не использовала его; после блокировки m_self результирующий shared_ptr передается различным функциям с помощью const ref и никогда не копируется и не сохраняется. Другими словами, он используется как очень дорогой this. - person James Davidoff; 26.06.2014