Совет 8. Никогда не создавайте контейнеры, содержащие auto_ptr
Совет 8. Никогда не создавайте контейнеры, содержащие auto_ptr
Честно говоря, в книге, посвященной эффективному использованию STL, данный совет не совсем уместен. Контейнеры auto_ptr (СОАР, Containers Of Auto_Ptr) запрещены, а программа, которая попытается их использовать, не будет компилироваться. Комитет по стандартизации С++ приложил неслыханные усилия в этом направлении. Возможно, мне вообще не стоило бы говорить о контейнерах auto_ ptr — о них вам расскажет компилятор, причем в самых нелестных выражениях.
Однако многие программисты работают на платформах STL, на которых СОАР не запрещены. Более того, многие программисты по-прежнему подвержены иллюзии и видят в СОАР простое, прямолинейное, эффективное средство для борьбы с утечкой ресурсов, часто присущей контейнерам указателей (советы 7 и 33). В результате возникает искушение воспользоваться СОАР, даже если их невозможно создать.
Вскоре я объясню, почему СОАР произвели такой переполох, что Комитет по стандартизации предпринял специальные шаги по их запрещению. А пока начнем с первого недостатка, для понимания которого не нужно разбираться в auto_ptr и вообще в контейнерах: СОАР не переносимы. Да и как может быть иначе? Они запрещены стандартом С++, и наиболее передовые платформы STL уже выполняют это требование. Вероятно, со временем платформы STL, которые сейчас не соответствуют Стандарту, выполнят его требования. Когда это произойдет, программы, использующие СОАР, станут еще менее переносимыми, чем сейчас. Тот, кто заботится о переносимости своих программ, отвергнет СОАР хотя бы по этой причине.
Впрочем, не исключено, что переносимость вас не волнует. Если это так, позвольте напомнить об уникальном (а по мнению некоторых — нелепом) смысле операции копирования auto_ptr.
При копировании auto_ptr право владения объектом, на который ссылается указатель, переходит к копии, а исходному указателю присваивается NULL. Да, вы не ошиблись: копирование указателя auto_ptr приводит к его модификации.
auto_ptr<Widget> pw1(new Widget); //pw1 ссылается на Widget
auto_ptr<Widget> pw2(pw1); //pw2 ссылается на объект Widget,
//принадлежащий pw1; pw1 присваивается
//NULL (таким образом, объект Widget
//передается от pw1 к pw2)
pwl = pw2; //pw1 снова ссылается на Widget:
//pw2 присваивается NULL
Конечно, такое поведение необычно и даже по-своему интересно, но для пользователя STL в первую очередь важно то, что оно приводит к крайне неожиданным последствиям. Рассмотрим внешне безобидный фрагмент, который создает вектор auto_ptr<Widget> и сортирует его функцией, сравнивающей значения Widget:
bool WidgetAPCompare(const auto_ptr<Widget>& Ihs.
const auto_ptr<Widget>& rhs)
{
return *lhs < *rhs;// Предполагается, что для объектов Widget
// существует оператор <
}
vector<auto_ptr<Widget> > widgets; // Создать вектор и заполнить его
// указателями auto_ptr на Widget. // Помните, что этот фрагмент // не должен компилироваться!
sort(widgets.begin(),widgets.end(), // Отсортировать вектор
widgetAPCompare);
Пока все выглядит вполне разумно, да и с концептуальной точки зрения все действительно разумно — но результат разумным никак не назовешь. Например, в процессе сортировки некоторым указателям auto_ptr, хранящимся в Widget, может быть присвоено значение NULL. Сортировка вектора приводит к изменению его содержимого! Давайте разберемся, как это происходит.
Оказывается, реализация sort часто строится на некой разновидности алгоритма быстрой сортировки. Работа этого алгоритма строится на том, что некоторый элемент контейнера выбирается в качестве «опорного», после чего производится рекурсивная сортировка по значениям, большим и меньшим либо равным значению опорного элемента. Реализация такого алгоритма в sort может выглядеть примерно так:
template<class RandomAccessIterator, // Объявление sort скопировано
class Compare>// прямо из Стандарта
void sort(RandomAccessIterator first,
RandomAccessIterator last,
Compare comp)
{
// typedef описывается ниже
typedef typename iterator_traits<RandomAccessIterator>::value_type
ElementType;
RandomAccessIterator i;
...// Присвоить i указатель на опорный элемент
ElementType pivotValue(*i); // Скопировать опорный элемент в локальную
...// временную переменную; см. далее комментарий.
// Остальная сортировка
}
Если вы не привыкли читать исходные тексты STL, этот фрагмент выглядит жутковато, но в действительности в нем нет ничего страшного. Нетривиально здесь выглядит только запись iterator_traits<RandomAccessIterator>:: value_type, но это всего лишь принятое в STL обозначение типа объекта, на который указывают итераторы, переданные sort. Перед ссылкой iterator_traits<RandomAccessIterator>:: value_type должен стоять префикс typename, поскольку это имя типа, зависящее от параметра шаблона (в данном случае RandomAccessIterator), — дополнительная информация приведена на с. 20.
Проблемы возникают из-за следующей команды, которая копирует элемент из сортируемого интервала в локальный временный объект:
ElementType pivotValue(*i);
В данном случае элементом является auto_ptr<Widget>, поэтому в результате скопированному указателю auto_ptr (тому, который хранится в векторе) присваивается NULL. Более того, когда pivotValue выходит из области видимости, происходит автоматическое удаление объекта Widget, на который pivotValue ссылается. Итак, после вызова sort содержимое вектора изменяется и по меньшей мере один объект Widget удаляется. Вследствие рекурсивности алгоритма быстрой сортировки существует вероятность того, что сразу нескольким элементам вектора будет присвоено значение NULL и сразу несколько объектов Widget будут удалены, поскольку опорный элемент копируется на каждом уровне рекурсии. . ^ Подобные ловушки весьма зловредны, и Комитет по стандартизации постарался, чтобы вы заведомо не попадались в них. Уважайте их труд и никогда не создавайте контейнеры auto_ptr, даже если ваша платформа STL это позволяет.
Впрочем, это вовсе не исключает возможности создания контейнеров умных указателей. Контейнеры умных указателей вполне допустимы. В совете 50 описано, где найти умные указатели, хорошо работающие в контейнерах STL, просто auto_ptr не относится к их числу.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Контейнеры. Встроенные контейнеры
Контейнеры. Встроенные контейнеры В самом начале данной главы мы узнали, что все рассмотренные нами атрибуты стилей можно указывать для любых элементов Web-страниц: и блочных, и встроенных. Значит, мы можем задать размер шрифта и для абзаца (блочного тега <P>), и для
Контейнеры. Встроенные контейнеры
Контейнеры. Встроенные контейнеры В самом начале данной главы мы узнали, что все рассмотренные нами атрибуты стилей можно указывать для любых элементов Web-страниц: и блочных, и встроенных. Значит, мы можем задать размер шрифта и для абзаца (блочного тега <P>), и для
Ресурсы, содержащие информацию общей направленности
Ресурсы, содержащие информацию общей направленности KM.RU (www.km.ru). Информационный мультипортал (зарегистрированный как средство массовой информации), представляющий вниманию своих посетителей разнообразные сервисы, тематические порталы и постоянно обновляемую ленту
19.2.4.5. В Linux создавайте RPM-пакеты
19.2.4.5. В Linux создавайте RPM-пакеты Де-факто стандартным форматом для устанавливаемых бинарных пакетов в Linux является формат, используемый диспетчером пакетов Red Hat Linux, RPM (Red Hat Package manager). Он имеется в большинстве популярных дистрибутивов Linux и поддерживается фактически всеми
19.2.4.5. В Linux создавайте RPM-пакеты
19.2.4.5. В Linux создавайте RPM-пакеты Де-факто стандартным форматом для устанавливаемых бинарных пакетов в Linux является формат, используемый диспетчером пакетов Red Hat Linux, RPM (Red Hat Package manager). Он имеется в большинстве популярных дистрибутивов Linux и поддерживается фактически всеми
11.1.14. Классы, содержащие только данные (Struct)
11.1.14. Классы, содержащие только данные (Struct) Иногда нужно просто сгруппировать взаимосвязанные данные, не определяя никакие специфические методы обработки. Можно для этого создать класс:class Address attr_accessor :street, :city, :state def initialize(street1, city, state) @street, @city, @state = street, city, state endendbooks =
Типы, характеризуемые значениями и содержащие ссылочные типы
Типы, характеризуемые значениями и содержащие ссылочные типы Теперь, когда вы чувствуете разницу между типами, характеризуемыми значением, и ссылочными типами, давайте рассмотрим более сложный пример. Предположим, что имеется следующий ссылочный тип (класс),
Совет 25. Изучите нестандартные хэшированные контейнеры
Совет 25. Изучите нестандартные хэшированные контейнеры После первого знакомства с STL у большинства программистов неизбежно возникает вопрос: «Векторы, списки, множества... хорошо, но где же хэш-таблицы?» Действительно, хэш-таблицы не входят в стандартную библиотеку С++.
8.4.2. Шаблон auto_ptr А
8.4.2. Шаблон auto_ptr А В стандартной библиотеке С++ auto_ptr является шаблоном класса, призванным помочь программистам в манипулировании объектами, которые создаются посредством оператора new. (К сожалению, подобного шаблона для манипулирования динамическими массивами нет.
Лучше, чем никогда
Лучше, чем никогда Автор: Сергей ВильяновВесна пришла в Москву непривычно рано. Я бы, скорее всего, и не поверил в серьезность ее намерений, несмотря на +20 за окном, но в пруд рядом с домом вернулись утки. Они, конечно, не такие умные, как окрестные вороны, однако в области
Никогда не интересуйся политикой
Никогда не интересуйся политикой Завершает тему эссе молодого украинского прозаика Сергея Жадана. Текст просто потрясающий. Во-первых, по точности передачи хаоса в умах людей, подвергающихся «манипуляциям» и прочим прелестям гуманитарных технологий, о которых чаще
Вы никогда не видели BSOD
Вы никогда не видели BSOD Как уже было отмечено, BSOD (Blue Screen Of Death, голубой экран смерти) – особенность Windows. С помощью BSOD Windows общается с пользователем и сообщает номер ошибки и ее причину. Конечно, в большинстве случаев описание ошибки прочитать не получается, поскольку вместо
Кто не научится думать сам, никогда не станет специалистом
Кто не научится думать сам, никогда не станет специалистом Тот, кто еще сомневается, пусть немного поразмыслит над сегодняшними реалиями: номера телефонов родственников, друзей и знакомых хранятся в памяти мобильного телефона. Маршрут к оговоренному месту встречи с ними