Почему библиотеки С++ и фреймворки никогда не используют интеллектуальные указатели?

В нескольких статьях я читал, что необработанные указатели почти никогда не должны использоваться. Вместо этого они всегда должны быть обернуты внутри интеллектуальных указателей, независимо от того, были ли они скопированы или разделены указателями.

Однако я заметил, что такие фреймворки, как Qt, wxWidgets и библиотеки, такие как Boost, никогда не возвращают и не ожидают интеллектуальных указателей, как будто они вообще не используют их. Вместо этого они возвращают или ожидают грубые указатели. Есть ли причина для этого? Должен ли я держаться подальше от умных указателей, когда я пишу публичный API и почему?

Просто интересно, почему рекомендуется использовать интеллектуальные указатели, когда многие крупные проекты, похоже, избегают их.

Ответ 1

Помимо того факта, что многие библиотеки были написаны до появления стандартных интеллектуальных указателей, самой большой причиной, вероятно, является отсутствие стандартного двоичного интерфейса приложений С++ (ABI).

Если вы пишете библиотеку только для заголовка, вы можете передавать интеллектуальные указатели и стандартные контейнеры в контент вашего сердца. Их источник доступен для вашей библиотеки во время компиляции, поэтому вы полагаетесь только на стабильность своих интерфейсов, а не на их реализацию.

Но из-за отсутствия стандартного ABI вы, как правило, не можете безопасно передавать эти объекты через границы модулей. GCC shared_ptr, вероятно, отличается от MSVC shared_ptr, который тоже может отличаться от Intel shared_ptr. Даже с одним и тем же компилятором эти классы не гарантируют совместимость двоичных файлов между версиями.

Суть в том, что если вы хотите распространять предварительно созданную версию вашей библиотеки, вам нужен стандартный ABI, на который можно положиться. C не имеет одного, но поставщики компилятора очень хорошо разбираются в функциональной совместимости между библиотеками C для данной платформы - существуют фактические стандарты.

Ситуация не так хороша для С++. Отдельные компиляторы могут обрабатывать взаимодействие между своими двоичными файлами, поэтому у вас есть возможность распространять версию для каждого поддерживаемого компилятора, часто GCC и MSVC. Но в свете этого большинство библиотек просто экспортируют C-интерфейс, а это означает, что они являются raw-указателями.

Однако не библиотечный код должен, как правило, предпочитать интеллектуальные указатели на raw.

Ответ 2

Могут быть много причин. Чтобы перечислить некоторые из них:

  • Умные указатели стали частью стандарта совсем недавно. До тех пор они были частью других библиотек.
  • Их основное использование - избежать утечек памяти; многие библиотеки не имеют собственного управления памятью; Как правило, они обеспечивают утилиты и API.
  • Они реализованы как оболочка, поскольку они на самом деле являются объектами, а не указателями. Который имеет дополнительные затраты времени и пространства по сравнению с необработанными указателями; Пользователи библиотек могут не иметь таких накладных расходов

Изменить. Использование интеллектуальных указателей - это полностью выбор разработчика. Это зависит от различных факторов.

  • В критичных для производительности системах вы можете не использовать интеллектуальные указатели, которые генерируют служебные данные

  • Проект, который нуждается в обратной совместимости, может не понадобиться для использования интеллектуальных указателей, имеющих специфические функции С++ 11

Edit2 В течение 24 часов имеется строка из нескольких downvotes из-за перехода ниже. Я не понимаю, почему ответ затушевывается, хотя ниже это просто добавочное предложение, а не ответ. Однако С++ всегда облегчает вам открытие опций.:) например,

template<typename T>
struct Pointer {
#ifdef <Cpp11>
  typedef std::unique_ptr<T> type;
#else
  typedef T* type;
#endif
};

И в вашем коде используйте его как:

Pointer<int>::type p;

Для тех, кто говорит, что умный указатель и необработанный указатель разные, я согласен с этим. Вышеприведенный код был просто идеей, где можно написать код, который можно заменить с помощью #define, это не принуждение;

Например, T* должен быть удален явно, но интеллектуальный указатель этого не делает. Мы можем иметь шаблонный Destroy() для обработки этого.

template<typename T>
void Destroy (T* p)
{
  delete p;
}
template<typename T>
void Destroy (std::unique_ptr<T> p)
{
  // do nothing
}

и используйте его как:

Destroy(p);

Таким же образом, для необработанного указателя мы можем скопировать его напрямую, а для интеллектуального указателя мы можем использовать специальную операцию.

Pointer<X>::type p = new X;
Pointer<X>::type p2(Assign(p));

Где Assign():

template<typename T>
T* Assign (T *p)
{
  return p;
}
template<typename T>
... Assign (SmartPointer<T> &p)
{
  // use move sematics or whateve appropriate
}

Ответ 3

Есть две проблемы с интеллектуальными указателями (pre С++ 11):

  • нестандартные, поэтому каждая библиотека имеет тенденцию изобретать свои собственные (синдром NIH и проблемы с зависимостями).
  • потенциальная стоимость

Умный указатель по умолчанию, в котором он является бесплатным, составляет unique_ptr. К сожалению, для этого требуется семантика перемещения С++ 11, которая появилась только недавно. Все другие интеллектуальные указатели имеют стоимость (shared_ptr, intrusive_ptr) или имеют менее идеальную семантику (auto_ptr).

С С++ 11 за углом, приведя std::unique_ptr, у вас возникнет соблазн подумать, что он наконец закончился... Я не настолько оптимистичен.

Только некоторые крупные компиляторы реализуют большинство С++ 11 и только в своих последних версиях. Мы можем ожидать, что крупные библиотеки, такие как QT и Boost, захотят сохранить совместимость с С++ 03 некоторое время, что несколько исключает широкое внедрение новых и блестящих интеллектуальных указателей.

Ответ 4

Вы не должны оставаться в стороне от умных указателей, их использование особенно важно в приложениях, где вам нужно передать объект.

Библиотеки обычно либо возвращают значение, либо заполняют объект. У них обычно нет объектов, которые необходимо использовать во многих местах, поэтому им не нужно использовать интеллектуальные указатели (по крайней мере, не в их интерфейсе, они могут использовать их внутри).

Я мог бы взять в качестве примера библиотеку, над которой мы работали, где через несколько месяцев развития я понял, что мы использовали указатели и интеллектуальные указатели в нескольких классах (3-5% всех классов).

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

Изменить (я не могу комментировать из-за моей репутации): передача переменных по ссылке очень гибкая: если вы хотите, чтобы объект был только для чтения, вы можете использовать константную ссылку (вы все равно можете сделать некоторые отвратительные броски, чтобы иметь возможность писать объект), но вы можете получить максимальную защиту (это же с интеллектуальными указателями). Но я согласен с тем, что гораздо проще вернуть объект.

Ответ 5

Qt бессмысленно повторно изобрел многие части стандартной библиотеки, пытаясь стать Java. Я считаю, что на самом деле у него есть свои собственные умные указатели, но в целом он едва ли является вершиной дизайна. wxWidgets, насколько я знаю, был разработан задолго до того, как были написаны полезные интеллектуальные указатели.

Что касается Boost, я полностью ожидаю, что они будут использовать интеллектуальные указатели, где это необходимо. Возможно, вам придется быть более конкретным.

Кроме того, не забывайте, что существуют интеллектуальные указатели для обеспечения прав собственности. Если API не имеет семантики владения, то зачем использовать интеллектуальный указатель?

Ответ 6

Хороший вопрос. Я не знаю конкретных статей, к которым вы обращаетесь, но время от времени я читал похожие вещи. Мое подозрение состоит в том, что авторы таких статей склонны уклоняться от программирования на языке C++. Если сценарист программирует на С++ только тогда, когда он должен, то возвращается на Java или как можно скорее, тогда он действительно не разделяет менталитет С++.

Один подозревает, что некоторые или большинство тех же авторов предпочитают менеджеров памяти, собирающих мусор. Я не знаю, но я думаю иначе, чем они.

Умные указатели великолепны, но они должны поддерживать подсчет ссылок. Сохранение ссылочного счета несет затраты - часто скромные затраты, но тем не менее, затраты - во время выполнения. Нет ничего плохого в экономии этих затрат, используя голые указатели, особенно если указатели управляются деструкторами.

Одна из превосходных особенностей С++ - это поддержка программирования встроенных систем. Использование голых указателей является частью этого.

Обновление: комментатор правильно заметил, что С++ new unique_ptr (доступный с TR1) не учитывает ссылки. У комментатора также есть другое определение "умный указатель", чем я имею в виду. Он может быть прав относительно определения.

Дальнейшее обновление: Ниже приведен поток комментариев ниже. Все это рекомендуется прочитать.

Ответ 7

Существуют и другие типы интеллектуальных указателей. Вам может понадобиться специализированный интеллектуальный указатель для чего-то вроде сетевой репликации (тот, который определяет, будет ли он доступен и отправляет какие-либо изменения на сервер или некоторые из них), сохраняет историю изменений и отмечает, что к нему был обращен доступ, поэтому его можно исследовать, когда вы сохраняете данные на диск и так далее. Не уверен, что выполнение этого указателя является лучшим решением, но использование встроенных типов интеллектуальных указателей в библиотеках может привести к тому, что люди будут заперты в них и потеряют гибкость.

Люди могут иметь всевозможные различные требования к управлению памятью и решения, выходящие за рамки интеллектуальных указателей. Я мог бы сам управлять памятью, я мог бы выделять пространство для вещей в пуле памяти, поэтому он выделялся заранее, а не во время выполнения (полезно для игр). Возможно, я использую сборку С++ с мусором (С++ 11 делает это возможным, хотя пока не существует). Или, может быть, я просто ничего не делаю, чтобы беспокоиться о том, чтобы беспокоиться о них, я могу знать, что я не буду забывать о неинициализированных объектах и ​​так далее. Может быть, я просто уверен в своей способности управлять памятью без указательного костыля.

Интеграция с C - еще одна проблема.

Другая проблема заключается в том, что интеллектуальные указатели являются частью STL. С++ предназначен для использования без STL.

Ответ 8

Это также зависит от того, в каком домене вы работаете. Я пишу игровые движки для жизни, мы избегаем повышения, как чума, в играх накладные расходы не приемлемы. В нашем основном движке мы закончили тем, что написали собственную версию stl (как и ea stl).

Если я должен был написать приложение форм, я мог бы рассмотреть возможность использования интеллектуальных указателей; но как только управление памятью является второй природой, не имея гранулированного контроля над памятью, становится тихим раздражающим.