Подтвердить что ты не робот

Что происходит при использовании make_shared

Мне интересно, будут ли эти две строки кода одинаковыми:

shared_ptr<int> sp(new int(1)); // double allocation?
shared_ptr<int> sp(make_shared<int>(1)); // just one allocation?

Если это правда, кто-то может объяснить, почему это только одно распределение во второй строке?

4b9b3361

Ответ 1

Первый случай не выполняет двойное распределение, он выполняет два распределения, один для управляемого объекта и один для блока управления shared_ptr.

Во втором случае cppreference имеет хорошее объяснение, почему std:: make_shared обычно выполняет только одно выделение памяти, которое он говорит (акцент мой вперед):

Эта функция обычно выделяет память для объекта T и для shared_ptr с единичным распределением памяти (это необязательное требование в стандарте). Напротив, декларация std:: shared_ptr p (новый T (Args...)) выполняет как минимум две памяти распределения, которые могут повлечь ненужные накладные расходы.

и в разделе std:: shared_ptr говорится:

Когда shared_ptr создается путем вызова std:: make_shared или std:: allocate_shared, память как для блока управления, так и для управляемый объект создается с одним распределением. Управляемый объект строится на месте в элементе данных блока управления. когда shared_ptr создается через один из конструкторов shared_ptr, управляемый объект, а блок управления должен быть выделен отдельно. В в этом случае блок управления сохраняет указатель на управляемый объект.

Это описание make_shared соответствует стандарту С++ 11, который гласит в разделе 20.7.2.2.6 shared_ptr creation

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
  shared_ptr<T> allocate_shared(const A& a, Args&&... args);

[...]

Примечания: Реализации должны выполнять не более одной памяти распределение. [Примечание. Это обеспечивает эффективность, эквивалентную интрузивный умный указатель. -end note]

[Примечание. Эти функции обычно выделяют больше памяти, чем sizeof (T), позволяющий создавать внутренние бухгалтерские структуры, такие как подсчет ссылок. -end note]

Herb Sutter имеет более подробное объяснение преимуществ использования make_shared в GotW # 89 Решение: Smart Pointers и указывает на некоторые преимущества:

  • Это сокращает накладные расходы на распределение
  • Это улучшает локальность.
  • Избегает явного нового.
  • Предотвращает проблему безопасности исключений.

Помните, что при использовании std:: weak_ptr с использованием make_shared есть некоторые недостатки.

Ответ 2

Объяснение из cppreference std:: shared_ptr в разделе Implementation notes

В типичной реализации std:: shared_ptr содержит только два указателя:

  • указатель на управляемый объект
  • указатель на блок управления

Когда shared_ptr создается путем вызова std:: make_shared или std:: allocate_shared, память как для блока управления, так и для управляемый объект создается с одним распределением. Управляемый объект строится на месте в элементе данных блока управления. когда shared_ptr создается через один из конструкторов shared_ptr, управляемый объект, а блок управления должен быть выделен отдельно. В в этом случае блок управления сохраняет указатель на управляемый объект.

Ответ 3

Существует также потенциальная тонкая ошибка: в sp(new int) вы назначаете int (указатель которого присваивается sp), чем sp сам должен выделять контрольный блок (будет содержать счетчики и дебетер).

Теперь, если выполнение этого последнего выделения sp не выполняется (низкая память), вы остаетесь с выделенной кучей int, указатель которой не удерживается кем-либо и, следовательно, невозможно удалить. (утечка памяти).