Я понял, что умный указатель используется для управления ресурсами и поддерживает RAII.
Но каковы угловые случаи, когда умный указатель не кажется умным и что нужно учитывать при его использовании?
Я понял, что умный указатель используется для управления ресурсами и поддерживает RAII.
Но каковы угловые случаи, когда умный указатель не кажется умным и что нужно учитывать при его использовании?
Интеллектуальные указатели не помогают избежать циклов в графических структурах.
Например, объект A содержит умный указатель на объект B и объект B - назад к объекту A. Если вы отпустите все указатели на A и B перед отключением A от B (или B от A), оба A и B будут удерживать друг друга и образовывать счастливую утечку памяти.
Сбор мусора может помочь в этом - он может видеть, что оба объекта недоступны и освобождают их.
Я хотел бы упомянуть ограничения производительности. Умеренные указатели обычно используют атомарные операции (например, InterlockedIncrement в API Win32) для подсчета ссылок. Эти функции значительно медленнее, чем простая целочисленная арифметика.
В большинстве случаев это ограничение производительности не является проблемой, просто убедитесь, что вы не делаете слишком много ненужных копий объекта интеллектуального указателя (предпочитаете передавать интеллектуальный указатель по ссылке в вызовах функций).
Это довольно интересно: Smart Pointers.
Это образец главы "Современного дизайна на C++" Андрея Александреску.
Остерегайтесь переходов - при назначении между сырыми и умными указателями. Плохие умные указатели - например, _com_ptr_t
- делают это хуже, позволяя имплицитные преобразования. Большинство ошибок происходит при переходе.
Следите за циклами - как уже упоминалось, вам нужны слабые указатели, чтобы сломать циклы. Однако на сложном графике, который не всегда легко сделать.
Слишком большой выбор - большинство библиотек предлагают разные реализации с различными преимуществами/недостатками. К сожалению, в большинстве случаев эти разные варианты несовместимы, что становится проблемой при микшировании библиотек. (скажем, LibA использует LOKI, LibB использует boost). Чтобы запланировать за enable_shared_from_this
отстой, нужно решить соглашения об именах между intrusive_ptr
, shared_ptr
и weak_ptr
для связки объектов.
Для меня единственным преимуществом shared_ptr (или одной из подобных функций) является то, что он связан с политикой уничтожения при создании. Оба С++ и Win32 предлагают так много способов избавиться от вещей, что это даже не смешно. Связь во время построения (без влияния на фактический тип указателя) означает, что у меня есть обе политики в одном месте.
Помимо технических ограничений (уже упомянутых: круговые зависимости), я бы сказал, что самое важное в умных указателях - это помнить, что это все еще обходное решение для удаления объектов, выделенных кучей. Выделение стека - лучший вариант для большинства случаев - наряду с использованием ссылок - для управления временем жизни объектов.
Вот несколько вещей
weak_ptr
приходит на помощь.Следующая статья - очень интересная статья
Интеллектуальные указатели: не может жить с Em, не может жить без Em...
Раймонд Чен, как известно, неоднозначно относится к умным указателям. Есть проблемы с когда деструктор действительно работает (учтите, что деструктор работает в четко определенное время в четко определенном порядке; просто время от времени вы забудете, что он после последней строки в вашей функции).
Также помните, что "умный указатель" - довольно большая категория. Я включаю std::vector
в эту категорию (a std::vector
по существу является интеллектуальным массивом).
Возникает проблема с подсчетом ссылок в некоторых типах структур данных, которые имеют циклы. Также могут возникать проблемы с доступом к интеллектуальным указателям из нескольких потоков, одновременный доступ к подсчетам ссылок может вызвать проблемы. Там утилита в boost называется atomic.hpp, которая может смягчить эту проблему.
Многие люди сталкиваются с проблемами при использовании интеллектуальных указателей, смешанных с необработанными указателями (к тем же объектам). Типичным примером является взаимодействие с API, использующим исходные указатели.
Например; в boost::shared_ptr
есть функция .get()
, которая возвращает необработанный указатель. Хорошая функциональность при использовании с осторожностью, но многие люди, похоже, путешествуют по ней.
ИМХО это пример "негерметичной абстракции".