Різниця полягає в тому, що він std::make_sharedвиконує одне купірування, тоді як виклик std::shared_ptrконструктора виконує два.
Де відбуваються купи-виділення?
std::shared_ptr управляє двома суб'єктами:
- блок управління (зберігає метадані, такі як перерахунок, стираний видалювач тощо)
- керований об’єкт
std::make_sharedвиконує єдиний облік купівлі-розподілу простору, необхідного як для блоку управління, так і для даних. В іншому випадку new Obj("foo")викликає розподіл купи для керованих даних, і std::shared_ptrконструктор виконує ще один для керуючого блоку.
Для отримання додаткової інформації ознайомтесь із примітками про реалізацію на сторінці cppreference .
Оновлення I: Виняток-Безпека
ПРИМІТКА (2019/08/30) : Це не проблема, оскільки C ++ 17 через зміни в порядку оцінки аргументів функції. Зокрема, кожен аргумент функції потрібно повністю виконати перед оцінкою інших аргументів.
Оскільки ОП, здається, цікавить питання щодо виключень, що стосуються безпеки, я оновив свою відповідь.
Розглянемо цей приклад,
void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),
std::shared_ptr<Rhs>(new Rhs("bar")));
Оскільки C ++ дозволяє довільний порядок оцінки підвиражень, одним із можливих впорядкувань є:
new Lhs("foo"))
new Rhs("bar"))
std::shared_ptr<Lhs>
std::shared_ptr<Rhs>
Тепер, припустимо, ми отримуємо виняток, кинутий на етапі 2 (наприклад, поза винятком пам'яті, Rhsконструктор викинув якийсь виняток). Тоді ми втрачаємо пам’ять, виділену на кроці 1, оскільки нічого не матиме шанс очистити її. Основна проблема тут полягає в тому, що необроблений покажчик не передається std::shared_ptrконструктору відразу.
Один із способів виправити це - зробити їх окремими рядками, щоб цього довільного впорядкування не відбулося.
auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);
Кращим способом вирішити це, звичайно, є використання std::make_sharedзамість цього.
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
Оновлення II: Недоліки std::make_shared
Цитуючи коментарі Кейсі :
Оскільки існує лише одне розподілення, пам'ять пуанте не може бути розміщена доти, поки блок управління не буде використаний. А weak_ptrможе підтримувати блок управління живим нескінченно довго.
Чому екземпляри weak_ptrs підтримують живий блок управління?
Повинен бути спосіб weak_ptrs визначити, чи керований об'єкт все-таки дійсний (наприклад, для lock). Вони роблять це шляхом перевірки кількості shared_ptrs, що володіє керованим об'єктом, що зберігається в блоці управління. Результат полягає в тому, що блоки керування живі, доки shared_ptrпідрахунок та weak_ptrкількість не потрапляють у 0.
Повертатися до std::make_shared
Оскільки std::make_sharedздійснює єдиний розподіл купи як для керуючого блоку, так і для керованого об'єкта, немає можливості звільнити пам'ять для блоку управління та керованого об'єкта незалежно. Треба чекати, поки ми зможемо звільнити і блок управління, і керований об’єкт, що трапляється, поки немає живих shared_ptrабо weak_ptrs.
Припустимо, ми замість цього виконали два купі-виділення для керуючого блоку та керованого об'єкта через newта shared_ptrконструктор. Тоді ми звільняємо пам'ять керованого об'єкта (можливо, раніше), коли немає shared_ptrживих, і звільняємо пам'ять для блоку управління (можливо, пізніше), коли немає weak_ptrживих.