Есть ли причины не использовать Boost::shared_ptrs?
Я задал пару вопросов ( здесь и здесь) об управлении памятью, и неизменно кто-то предлагает мне использовать boost::shared_ptrs.
Учитывая, насколько они полезны, я серьезно подумываю над тем, чтобы переключить все мое приложение на использование boost::shared_ptrs.
Однако, прежде чем я прыгну обеими ногами и сделаю это, я хотел спросить: есть ли у кого-нибудь плохой опыт с boost::shared_ptrs? Есть ли какие-то подводные камни в их использовании, которые я должен остерегаться?
В данный момент они кажутся слишком хорошими, чтобы быть правдой - автоматически заботиться о большинстве моих проблем с сборкой мусора. Какой минус?
4 ответа
Недостатком является то, что они не свободны. Вы особенно не должны использовать shared_ptr
/shared_array
когда scoped_ptr
/scoped_array
(или просто старое выделение стека). Вам нужно будет вручную разорвать циклы с weak_ptr
если у тебя есть. Векторный вопрос, на который вы ссылаетесь, является одним из случаев, когда я мог бы shared_ptr
Второго вопроса я бы не стал. Не копирование - это преждевременная оптимизация, особенно если строковый класс уже делает это за вас. Если класс строки имеет счетчик ссылок, он также сможет правильно реализовать COW, что на самом деле невозможно сделать с помощью shared_ptr<string>
подход. С помощью shared_ptr
willy-nilly также представит "трение интерфейса" с внешними библиотеками /apis.
Повышение общих указателей или любой другой метод управления памятью в C++ не является панацеей. Там нет замены для тщательного кодирования. Если вы погрузитесь в использование boost::shared_ptr, помните о владении объектом и избегайте циклических ссылок. Вам нужно будет явно прерывать циклы или использовать boost::weak_ptr, где это необходимо.
Также будьте осторожны, чтобы всегда использовать boost::shared_ptr для экземпляра с момента его выделения. Таким образом, вы уверены, что у вас не будет свисающих ссылок. Один из способов обеспечить это - использовать фабричные методы, которые возвращают ваш вновь созданный объект в shared_ptr.
typedef boost::shared_ptr<Widget> WidgetPtr;
WidgetPtr myWidget = Widget::Create();
Я часто использую shared_ptr.
Поскольку Shared_ptr копируется по значению, вы можете нести затраты на копирование как значения указателя, так и счетчика ссылок, но если используется boost::intrusive_ptr, счетчик ссылок должен быть добавлен в ваш класс, и никаких дополнительных накладных расходов выше что с использованием необработанного указателя.
Однако, по моему опыту, в более чем 99% случаев издержки на копирование экземпляров boost::shared_ptr по всему коду незначительны. Обычно, как отметил CAR Hoare, преждевременная оптимизация не имеет смысла - большую часть времени другой код использует значительно больше времени, чем время для копирования небольших объектов. Ваш пробег может отличаться. Если профилирование показывает, что копирование является проблемой, вы можете переключиться на навязчивые указатели.
Как уже отмечалось выше, циклы должны быть прерваны с помощью weak_ptr, иначе произойдет утечка памяти. Это произойдет со структурами данных, такими как некоторые графы, но если, например, вы создаете древовидную структуру, где листья никогда не указывают назад, вы можете просто использовать shared_pointers для узлов дерева без каких-либо проблем.
Правильное использование shared_ptr значительно упрощает код, облегчает чтение и поддержку. Во многих случаях их использование является правильным выбором.
Конечно, как уже упоминалось, в некоторых случаях использование scoped_ptr (или scoped_array) является правильным выбором. Если pointee не является общим, не используйте общие указатели!
Наконец, самый последний стандарт C++ предоставляет шаблон std::tr1::shared_ptr, который сейчас используется на большинстве платформ, хотя я не думаю, что существует тип навязчивого указателя для tr1 (точнее, он может быть, но у меня есть не слышал об этом сам).
Динамические накладные расходы памяти (т. Е. Дополнительные выделения) плюс все накладные расходы, связанные с умными указателями с подсчетом ссылок.