Умные указатели не помогают против циклов в графоподобных структурах.
Например, объект A содержит интеллектуальный указатель на объект B, а объект B - обратно на объект A. Если вы освободите все указатели на A и B перед отключением A от B (или B из A) и A, и B будут удерживать друг друга и образовывать счастливую утечку памяти.
Сборка мусора может помочь в этом - он может увидеть, что оба объекта недоступны, и освободить их.
Существует проблема с подсчетом ссылок в определенных типах структур данных, которые имеют циклы. Также могут быть проблемы с доступом к интеллектуальным указателям из нескольких потоков, одновременный доступ к счетчикам ссылок может вызвать проблемы. В boost есть утилита под названием atomic.hpp , которая может решить эту проблему.
Хочу отметить ограничения производительности. Умные указатели обычно используют атомарные операции (такие как InterlockedIncrement в Win32 API) для подсчета ссылок. Эти функции значительно медленнее, чем простая целочисленная арифметика.
В большинстве случаев такое снижение производительности не является проблемой, просто убедитесь, что вы не делаете слишком много ненужных копий объекта интеллектуального указателя (предпочитайте передавать интеллектуальный указатель с помощью ссылку в вызовах функций).
Watch out at the transitions - when assigning between raw and smart pointers. Bad smart pointers - like _com_ptr_t
- make it worse by allowing implicit conversions. Most errors happen at the transition.
Watch out for cycles - as mentioned, you need weak pointers to break the cycles. However, in a complex graph that's not always easy to do.
Too much choice - most libraries offer different implementations with different advantages / drawbacks. Unfortunately, most of the time these different variants are not compatible, which becomes a probem when mixing libraries. (say, LibA uses LOKI, LibB uses boost). Having to plan ahead for enable_shared_from_this
sucks, having to decide naming conventions between intrusive_ptr
, shared_ptr
and weak_ptr
for a bunch of objects sucks.
For me, the single most e advantage of shared_ptr (or one of similar functionality) is that it is coupled to its destruction policy at creation. Both C++ and Win32 offers so many ways of getting rid of things it's not even funny. Coupling at construction time (without affecting the actual type of the pointer) means I have both policies in one place.
Помимо технических ограничений (уже упомянутых: круговые зависимости), я бы сказал, что самое важное в интеллектуальных указателях - помнить, что это все еще обходной путь для удаления объектов, выделенных кучей. Размещение стека - лучший вариант в большинстве случаев - наряду с использованием ссылок - для управления временем жизни объектов.
Многие люди сталкиваются с проблемами при использовании интеллектуальных указателей, смешанных с необработанными указателями (на те же объекты). Типичный пример - взаимодействие с API, использующим необработанные указатели.
Например; в boost :: shared_ptr
есть функция .get ()
, которая возвращает необработанный указатель. Хорошая функциональность при осторожном использовании, но многие люди спотыкаются об этом.
ИМХО это пример "дырявой абстракции".
Раймонд Чен, как известно, неоднозначно относится к интеллектуальным указателям. Есть проблемы с , когда деструктор действительно запускается (обратите внимание, деструктор запускается в строго определенное время в четко определенном порядке; просто время от времени вы забудете, что это ] после последней строки в вашей функции).
Также помните, что «умный указатель» - довольно большая категория. Я включаю std :: vector
в эту категорию ( a std :: vector
по сути является интеллектуальным массивом ).
Это довольно интересно: Smart Pointers .
Это образец главы из книги Андрея Александреску «Современный дизайн на C ++»
Вот несколько вещей
weak_ptr
. Следующая статья представляет собой очень интересную статью