Различия между std :: make_unique и std :: unique_ptr с новым


130

Есть ли std::make_uniqueкакие-то преимущества эффективности std::make_shared?

По сравнению с ручным строительством std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));

Есть ли make_sharedкакая-то эффективность по сравнению с простым написанием длинного кода?
Эд Хил

9
@EdHeal Может, потому что make_sharedможет выделить как пространство для объекта, так и пространство для блока управления вместе в одном распределении. Цена этого заключается в том, что объект не может быть освобожден отдельно от блока управления, поэтому, если вы используете weak_ptrмного, вы можете использовать больше памяти.
bames53

Возможно, это хорошая отправная точка stackoverflow.com/questions/9302296/…
Эд Хил

Ответы:


140

За этим стоит make_uniqueв первую очередь двоякая мотивация :

  • make_uniqueбезопасен для создания временных файлов, тогда как при явном использовании newвы должны помнить правило о запрете использования безымянных временных файлов.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
    
  • Добавление make_uniquefinally означает, что мы можем сказать людям «никогда», newа не предыдущее правило «никогда» использовать, newкроме случаев, когда вы делаете unique_ptr».

Есть и третья причина:

  • make_uniqueне требует использования избыточного типа. unique_ptr<T>(new T())->make_unique<T>()

Ни одна из причин не связана с повышением эффективности времени выполнения, как это make_sharedделается при использовании (из-за избежания второго выделения за счет потенциально более высокого пикового использования памяти).

* Ожидается, что C ++ 17 будет включать изменение правила, которое означает, что это больше не небезопасно. См. Документы комитета C ++ P0400R0 и P0145R3 .


Это имело бы больше смысла , чтобы сказать std::unique_ptrи то, std::shared_ptrпочему мы можем сказать людям , чтобы «никогда не использовать new
Тимоти Шилдс

2
@TimothyShields Да, вот что я имею в виду. Просто у нас есть C ++ 11 make_sharedи make_uniqueпоследняя часть, которой раньше не хватало.
bames53

1
В любом случае, вы могли бы кратко упомянуть или указать причину отказа от использования безымянных временных библиотек?
Дэн Ниссенбаум 03

14
На самом деле, из stackoverflow.com/a/19472607/368896 , у меня это ... Из этого ответа, рассмотрим следующий вызов функции f: f(unique_ptr<T>(new T), function_that_can_throw());- процитировать ответ: Компилятор разрешено вызова (в порядке убывания): new T, function_that_can_throw(), unique_ptr<T>(...), Очевидно, если на function_that_can_throwсамом деле бросает, то вы протекаете. make_uniqueпредотвращает этот случай. Итак, на мой вопрос дан ответ.
Дэн Ниссенбаум 03

3
Одна из причин, по которой мне однажды пришлось использовать std :: unique_ptr <T> (new T ()), заключалась в том, что конструктор T был закрытым. Даже если вызов std :: make_unique был в общедоступном фабричном методе класса T, он не компилировался, потому что один из базовых методов std :: make_unique не мог получить доступ к частному конструктору. Я не хотел делать этот метод другом, потому что не хотел полагаться на реализацию std :: make_unique. Итак, единственным решением было вызвать new в моем заводском методе класса T, а затем обернуть его в std :: unique_ptr <T>.
Патрик

14

std::make_uniqueи std::make_sharedсуществуют по двум причинам:

  1. Так что вам не нужно явно указывать аргументы типа шаблона.
  2. Дополнительная безопасность исключений при использовании конструкторов std::unique_ptrили std::shared_ptr. (См. Раздел «Примечания» здесь .)

Дело не в эффективности времени выполнения. Есть кое-что о блоке управления и Tвыделении всех сразу, но я думаю, что это скорее бонус, а не мотивация для существования этих функций.


Они также существуют для обеспечения безопасности исключений.
0x499602D2

@ 0x499602D2 И это, хорошее дополнение. Эта страница говорит об этом.
Тимоти Шилдс

Для будущих читателей C ++ 17 не допускает чередования аргументов функции, поэтому аргумент в пользу безопасности исключений больше не актуален. Выделение памяти для двух параллелей std::make_sharedгарантирует, что хотя бы один из них будет заключен в интеллектуальный указатель до того, как произойдет выделение другой памяти, следовательно, никаких утечек.
MathBunny

7

Причина, по которой вам придется использовать std::unique_ptr(new A())или std::shared_ptr(new A())напрямую, вместо std::make_*()невозможности получить доступ к конструктору класса Aза пределами текущей области.


0

Рассмотрим вызов функции

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

Предположим, что new выполняется A()успешно, но new B()вызывает исключение: вы перехватываете его, чтобы возобновить нормальное выполнение вашей программы. К сожалению, стандарт C ++ не требует, чтобы объект A был уничтожен, а его память освобождена: утечка памяти происходит незаметно, и ее невозможно очистить. Обернув A и B, std::make_uniquesвы уверены, что утечки не произойдет:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

Дело в том, что std::make_unique<A>и std::make_unique<B>теперь являются временными объектами, и очистка временных объектов правильно указана в стандарте C ++: их деструкторы будут запущены, а память будет освобождена. Поэтому, если вы можете, всегда предпочитайте выделять объекты с помощью std::make_uniqueи std::make_shared.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.