Метод .reset() общих интеллектуальных указателей

#c

#c

Вопрос:

Мне кажется, что метод reset в boost scoped_ptr и shared_ptr приводят к неправильному порядку построения и уничтожения:

 boost::scoped_ptr<Component> component(GetDefaultComponent());
component.reset(new BetterComponent); // 1. Creation of the new object
                                      // 2. Destruction of the old object
  

Это неправильный порядок, IMO.

Можно сначала вызвать метод reset без аргументов, а затем установить новый указатель. Однако мне это кажется обходным путем. (И то, что это «обходной путь», подразумевает, что что-то не так.)

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

Кто-нибудь знает больше?

Комментарии:

1. «Это неправильный порядок, IMO». Почему вы так считаете?

2. Поскольку дублирование в существовании может вызвать проблемы в коде уровня приложения. Особенно, если каждый объект выполняет некоторую форму регистрации / отмены регистрации при создании / уничтожении. Я действительно трачу время на отладку подобных проблем. В конечном итоге обходным путем является вызов reset() перед сбросом (new X).

3. Однако я вижу, что с точки зрения разработчиков boost ситуация иная. Поддержание согласованности на уровне boost — это их ответственность, а не код уровня приложения. Я думаю, что это аналогично тому, как разработчики ядра смотрят на систему иначе, чем их пользователи: blogs.msdn.com/b/oldnewthing/archive/2011/05/12/10163578.aspx

4. Я использую объекты, которые выполняют регистрацию / отмену регистрации при создании / уничтожении, и совершенно не понимаю, как это может вызвать проблему. Это не приведет к тому, что объекты будут регистрироваться как активные, когда они не являются активными, или отменят регистрацию самих себя, когда они активны.

Ответ №1:

Это абсолютно правильный порядок. Что произойдет, если new BetterComponent произойдет сбой? Упс. Это тот же порядок, в котором все происходит, он известен как копирование и подкачка, и это лучший способ.

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

Комментарии:

1. 1. Точно. Конструкторы могут генерировать исключения, деструкторы — нет. (Ну, не должно быть.) Следовательно, это совершенно правильный порядок.

Ответ №2:

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

 template<typename T, typename Pointer, typename... U>
void
emplace_reset(Pointeramp; pointer, Uamp;amp;... u)
{
    pointer.reset();
    pointer.reset(new T(std::forward<U>(u)...));
}
// use as: emplace_reset<BetterComponent>(component);
  

Но это не обходной путь, потому что элемент reset выполняет именно то, что рекламируется. То, о чем вы просите, — это новая функциональность.

Возможное обоснование того, что вы не предоставляете требуемую функциональность (помимо семантики безопасности исключений / транзакций), заключается в том, что указатели никогда не создавали объекты, на которые они указывают: конструкторы принимают на себя право собственности, но это все. reset таким образом, он непротиворечив.

Комментарии:

1. 1 Оставить SmartPtr ответственным за фактическое создание объекта — хорошая вещь, IMO. Потому что тогда создание, владение и уничтожение принадлежат одному и тому же объекту. Я считаю, что это делает код менее подверженным ошибкам и более простым для понимания.

2. @StackedCrooked Я предпочитаю использовать «фабрики», подобные make_shared . И все же, если бы вы написали интеллектуальный указатель, который заботился о создании, вам все равно пришлось бы тщательно отделять reset подобный элемент от emplace_reset подобного элемента из-за очень, очень разных гарантий безопасности при исключениях.