Почему нужно использовать make_shared и make_unique вместо создания умных указателей вручную
Что проверяют
Знаете ли вы настоящие аргументы за фабричные функции и, что важнее, случаи, когда make_shared применять нельзя. Ответ «так принято» не засчитывается, ждут механику: сколько аллокаций, что происходит при исключении, где лежит управляющий блок.
Как отвечать
Перечислите доводы по порядку.
Первое, в коде не остаётся голого new, тип пишется один раз, а владение видно из сигнатуры:
auto w = std::make_unique<Widget>(id, name);
auto s = std::make_shared<Widget>(id, name);Второе, безопасность при исключениях. В выражении вида f(std::unique_ptr<T>(new T), g()) компилятор раньше имел право вычислить new T, затем g(), и брошенное из g() исключение оставляло память ничьей. Стандарт C++17 запретил чередование вычисления аргументов, так что дыра закрыта, но знать про неё стоит: вопрос задают именно с этим расчётом.
Третье, и это единственный реальный выигрыш по скорости: make_shared размещает сам объект и управляющий блок со счётчиками сильных и слабых ссылок одной аллокацией вместо двух, заодно улучшая локальность.
Обязательно назовите, когда так делать не надо. Нужен свой делитер или размещение особым способом, конструктор приватный, объект пришёл из чужого API сырым указателем, либо на объект есть долгоживущие weak_ptr: память под объект не вернётся, пока жив хотя бы один из них, потому что лежит в том же блоке.
Где спотыкаются
Заявляют, что make_unique быстрее ручного создания. Аллокация там одна в обоих случаях, выигрыш только в читаемости и безопасности. Приписывать ему прирост производительности значит показать, что механику не разбирали, а вывод запомнили.
Говорят, что умные указатели полностью исключают утечки. Два объекта, ссылающиеся друг на друга через shared_ptr, не освободятся никогда, цикл разрывается через weak_ptr. И shared_ptr не делает объект потокобезопасным: атомарен счётчик ссылок, а не данные под указателем.
Не помнят про массивы и про доступность функций. std::make_unique появился в C++14, а версия make_shared для массивов только в C++20, поэтому на старом стандарте для shared_ptr на массив приходится писать конструктор с делитером вручную.