Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

Совет 8. Никогда не создавайте контейнеры, содержащие auto_ptr

Честно говоря, в книге, посвященной эффективному использованию STL, данный совет не совсем уместен. Контейнеры

auto_ptr
(COAP, Containers Of Auto_Ptr) запрещены, а программа, которая попытается их использовать, не будет компилироваться. Комитет по стандартизации C++ приложил неслыханные усилия в этом направлении. Возможно, мне вообще не стоило бы говорить о контейнерах
auto_ ptr
— о них вам расскажет компилятор, причем в самых нелестных выражениях.

Однако многие программисты работают на платформах STL, на которых COAP не запрещены.

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

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

auto_ptr
и вообще в контейнерах: COAP не переносимы. Да и как может быть иначе? Они запрещены стандартом C++, и наиболее передовые платформы STL уже выполняют это требование. Вероятно, со временем платформы STL, которые сейчас не соответствуют Стандарту, выполнят его требования. Когда это произойдет, программы, использующие COAP, станут еще менее переносимыми, чем сейчас. Тот, кто заботится о переносимости своих программ, отвергнет COAP хотя бы по этой причине.

Впрочем, не исключено, что переносимость вас не волнует. Если это так, позвольте напомнить об уникальном (а по мнению некоторых — нелепом) смысле операции копирования

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>& lhs, 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
будут удалены, поскольку опорный элемент копируется на каждом уровне рекурсии.

Поделиться:
Популярные книги

Кодекс Крови. Книга IV

Борзых М.
4. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга IV

Девятый

Каменистый Артем
1. Девятый
Фантастика:
боевая фантастика
попаданцы
9.15
рейтинг книги
Девятый

Кодекс Охотника. Книга XII

Винокуров Юрий
12. Кодекс Охотника
Фантастика:
боевая фантастика
городское фэнтези
аниме
7.50
рейтинг книги
Кодекс Охотника. Книга XII

Его маленькая большая женщина

Резник Юлия
Любовные романы:
современные любовные романы
эро литература
8.78
рейтинг книги
Его маленькая большая женщина

Саженец

Ланцов Михаил Алексеевич
3. Хозяин дубравы
Фантастика:
попаданцы
альтернативная история
фэнтези
5.00
рейтинг книги
Саженец

Свет во мраке

Михайлов Дем Алексеевич
8. Изгой
Фантастика:
фэнтези
7.30
рейтинг книги
Свет во мраке

(Не)свободные, или Фиктивная жена драконьего военачальника

Найт Алекс
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
(Не)свободные, или Фиктивная жена драконьего военачальника

Вамп

Парсиев Дмитрий
3. История одного эволюционера
Фантастика:
рпг
городское фэнтези
постапокалипсис
5.00
рейтинг книги
Вамп

Инвестиго, из медика в маги 2

Рэд Илья
2. Инвестиго
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Инвестиго, из медика в маги 2

Последняя Арена 4

Греков Сергей
4. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Последняя Арена 4

Хозяин Теней 2

Петров Максим Николаевич
2. Безбожник
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Хозяин Теней 2

Идеальный мир для Лекаря 8

Сапфир Олег
8. Лекарь
Фантастика:
юмористическое фэнтези
аниме
7.00
рейтинг книги
Идеальный мир для Лекаря 8

Неудержимый. Книга XI

Боярский Андрей
11. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XI

Двойник Короля 2

Скабер Артемий
2. Двойник Короля
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Двойник Короля 2