#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
подобного элемента из-за очень, очень разных гарантий безопасности при исключениях.