make_shared против shared_ptr
Краткое резюме:
Просто краткий обзор: общие указатели работают на основе концепции счетчика ссылок, они поддерживают отдельный блок управления, в котором хранится этот счетчик.
Как работает shared_ptr, так это то, что они поддерживают -
сильный счетчик ссылок (S) — количество общих_ptr(ов), поддерживающих объект в рабочем состоянии. Общий объект уничтожается (и, возможно, освобождается), когда исчезает последняя сильная ссылка.
weak reference count (W) — количество активных weak_ptr(ов), которые в данный момент наблюдают за объектом + (S!=0)
Количество Strong и Weak обычно увеличивается с помощью эквивалента atomic::fetch_add с memory_order_relaxed.
Уменьшение требует более строгого порядка для обеспечения безопасного уничтожения.
Логическая модель для конструктора shared_ptr
Если shared_ptr создается из существующего указателя, который не является shared_ptr, необходимо выделить память для управляющей структуры.
Этот блок управления уничтожается и освобождается, когда исчезает последний слабый реф. Подход к построению shared_ptr состоит из двух шагов.
Логическая модель построения объекта с помощью make_shared
make_shared (или allocate_shared) Выделяет память для управляющей структуры и самого объекта в одном блоке памяти.
Затем объект создается путем идеальной пересылки аргументов его конструктору.
Плюсы make_shared вместо shared_ptr
Производительность: уменьшено количество отдельных распределений.
Местность кеша: действия, которые работают как со структурой счетчика, так и с самим объектом, будут иметь вдвое меньшее количество промахов кеша. (В случае, если промахи кеша являются серьезной проблемой, мы можем вообще отказаться от работы с указателями на одиночные объекты)
Порядок выполнения и безопасность исключений: (касается версии до C++17)
Возможный порядок выполнения
1) new Lhs("foo"))
2) new Rhs("bar"))
3) std::shared_ptr‹Lhs›
4) std::shared_ptr‹Rhs›
И одно важное преимущество, особенно в случае кодов до C++17, — это безопасность выполнения. Итак, посмотрите на этот фрагмент. Foo имеет эту сигнатуру функции, вы делаете вызов следующим образом.. теперь, до С++ 17, нет ограничений на разрешение аргументов, поэтому одно из возможных разрешений может выглядеть так — теперь то, что выдает второй шаг.. у вас есть утечка , правильно?
Исправление 1.Используйте make_shared
Исправление 2: Расширение кода
Shared_ptr Pro и make_shared
Доступ к конструктору.make_shared требуется доступ к конструктору, который он должен вызывать.
Срок службы хранилища объектов (не самого объекта) —второе преимущество связано со сроком службы хранилища объектов (а не с объектом). Речь идет об уничтожении или освобождении, когда срабатывает последний слабый счет. , то произойдет только освобождение. В случае make_shared узким местом становится одиночный блок. Для объектов большого размера в сочетании с некоторым долгим сроком службы weak_ptr это может стать проблематичным.
С помощью shared_ptr вы также можете указать пользовательское средство удаления, если это необходимо!
Вывод: если нет веских причин, следуйте этому
Как правило, make_shared предпочтительнее, чем shared_ptr, но в некоторых случаях, как указано выше, может понадобиться и shared_ptr.
использованная литература