Відмінності між 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є якась ефективність щодо простого написання довгого коду руки?
Ed Heal

9
@EdHeal Це може, тому що make_sharedможе виділити як простір для об'єкта, так і простір для блоку управління разом в одному розподілі. Вартість цього полягає в тому, що об'єкт не може бути розміщений окремо від блоку управління, тому, якщо ви weak_ptrбагато використовуєте, то, можливо, ви отримаєте більше пам'яті.
bames53

Можливо, це гарна відправна точка stackoverflow.com/questions/9302296/…
Ed Heal

Відповіді:


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_uniqueнарешті, означає, що ми можемо сказати людям використовувати "ніколи", 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
Будь-яким способом ви могли коротко згадати чи посилатися на причину використання неіменованих тимчасових країн?
Dan Nissenbaum

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заважає цій справі. Отже, на моє запитання відповіли.
Dan Nissenbaum

3
Однією з причин мені колись довелося використовувати std :: unique_ptr <T> (новий T ()) через те, що конструктор T був приватним. Навіть якщо виклик std :: make_unique був у загальнодоступному фабричному методі класу T, він не компілювався, оскільки один із основних методів std :: make_unique не міг отримати доступ до приватного конструктора. Я не хотів робити цей метод другом, тому що не хотів покладатися на реалізацію std :: make_unique. Тож єдиним рішенням було: виклик нового в моєму заводському методі класу 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())) { ... }

Припустимо, що нове A()успішно, але нове 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.