Почему C++ и фреймворки не использовать смарт-указатели?

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

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

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

8 ответов


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

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

но из-за отсутствия стандартного ABI, вы вообще не может безопасно передайте эти объекты через границы модуля. В ССЗ shared_ptr вероятно, отличается от MSVC shared_ptr, который тоже может отличаться от Intel shared_ptr. Даже с то же самое компилятор, эти классы не гарантированно совместимы с двоичными версиями.

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

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

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


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

  1. умные указатели стали частью стандарта совсем недавно. До тех пор они были частью других библиотек
  2. их основное использование-избежать утечек памяти; многие библиотеки не имеют собственного управления памятью; как правило, они предоставляют утилиты и API
  3. они реализованы как обертка, так как они на самом деле являются объектами, а не указателями. Что имеет дополнительную цену времени / космоса, сравненную к необработанные указатели; пользователи библиотек могут не хотеть иметь такие накладные расходы

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

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

  2. проект, который нуждается в обратной совместимости, вы не хотите использовать интеллектуальные указатели, которые имеют C++11 особенности

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

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

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

Pointer<int>::type p;

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

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

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

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

Destroy(p);

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

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
}

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

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

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

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

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


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

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

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

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

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


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

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

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


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

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

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

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

обновление: комментатор правильно заметил, что c++'s new unique_ptr (доступно начиная с TR1) не учитывает ссылки. Комментатор также имеет другое определение "умного указателя", чем я имею в виду. Возможно, он прав насчет определения.

уточнения: поток комментариев ниже освещает. Все это рекомендуется читать.


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

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

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

еще одна проблема-умные указатели являются частью STL. C++ предназначен для использования без STL.


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

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