2013-08-18 4 views
25

В чем разница между:станд :: shared_ptr: make_shared <Foo>() против shared_ptr <T> (новый Foo)

std::shared_ptr<int> p = std::shared_ptr<int>(new int); 

и

std::shared_ptr<int> p = std::make_shared<int>(); 

?

Какой я должен предпочесть и почему?

P. S. Довольно уверен, что на это, должно быть, уже был дан ответ, но я не могу найти аналогичный вопрос.

+2

Второй должен быть 'std :: make_shared ()'. – nosid

+3

Да, но не в этом случае. Всегда используйте 'make_shared'.Дополнительную информацию см. В следующем URL-адресе: http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/ – nosid

+0

@Deduplicator: как, черт возьми, мой вопрос является дубликатом одного вопроса спустя год ? –

ответ

39

Оба примера гораздо более многословен, чем это необходимо:

std::shared_ptr<int> p(new int); // or '=shared_ptr<int>(new int)' if you insist 
auto p = std::make_shared<int>(); // or 'std::shared_ptr<int> p' if you insist 

В чем разница?

Главное отличие состоит в том, что для первого требуется два распределения памяти: один для управляемого объекта (new int) и один для подсчета ссылок. make_shared должен выделить один блок памяти и создать в нем.

Какой я должен предпочесть и почему?

Обычно вы используете make_shared, так как это более эффективно. Как отмечено в другом ответе, он также избегает любой возможности утечки памяти, поскольку у вас никогда не было необработанного указателя на управляемый объект.

Однако, как отмечается в комментариях, у него есть потенциальный недостаток, заключающийся в том, что память не будет выпущена при уничтожении объекта, если есть все еще слабые указатели, препятствующие удалению общего счета.

+15

У него есть недостаток: блок памяти поддерживается до тех пор, пока на объект отсутствуют слабые указатели. – Xeo

+1

@Xeo: Спасибо, я забыл об этом. –

+0

+1 для комментария слабого указателя. хороший. – bjackfly

11

От en.cppreference.com

В отличие от этого, декларация std::shared_ptr<T> p(new T(Args...))выполняет по меньшей мере, два выделения памяти, которые могут нести ненужные накладные расходы.

Кроме того, f(shared_ptr<int>(new int(42)), g())может привести к утечке памяти, если g выбрасывает исключение. Эта проблема не существует, если используется make_shared.

Поэтому я бы порекомендовал подход make_shared, если это возможно.

+0

Спасибо. Жаль, что есть проблемы с 'shared_ptr' и' make_shared' на Mac (они все еще находятся в пространстве имен 'tr1', поэтому я должен определить макросы для написания переносного кода с использованием общих указателей). Я имею в виду, что для использования 'make_shared' требуется дополнительный макрос. –

+0

Плохо, да. Какой компилятор вы используете? В Debian 'g ++ 4.7' этой проблемы нет. –

+1

@ VioletGiraffe: на Mac вы можете указать -stdlib = libC++ (в Xcode найти «стандартную библиотеку C++» в настройках сборки и выбрать «libC++»). Это даст вам C++ 11 std :: lib. –

9

Помните, что make_shared ограничивает использование функций распределения/освобождения по умолчанию, поэтому, если вы хотите иметь больше контроля, make_shared не является опцией. Другими словами, что-то вроде

std::shared_ptr<uint8_t>(p, [](uint8_t *p){ /*user code */}); 

невозможно используя make_shared. Вместо этого можно использовать allocate_shared, но может быть указан только распределитель, а не дебетер. Иногда нужно контролировать распределение и удаление обернутого класса.