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

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

Жанры

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

 …

};

Как обычно, если вы не объявите эти функции самостоятельно, компилятор сделает это за вас. Встроенные типы (

int
, указатели и т. д.) копируются простым копированием их двоичного представления. Копирующие конструкторы и операторы присваивания описаны в любом учебнике по C++. В частности, эти функции рассмотрены в советах 11 и 27 книги «Effective C++».

Теперь вам должен быть ясен смысл этого совета. Если контейнер содержит объекты, копирование которых сопряжено с большими затратами, простейшее занесение объектов в контейнер может заметно повлиять на скорость работы программы. Чем больше объектов перемещается в контейнере, тем больше памяти и тактов процессора расходуется на копирование. Более того, у некоторых объектов само понятие «копирование» имеет нетрадиционный смысл, и при занесении таких объектов в контейнер неизменно возникают проблемы (пример приведен в совете 8).

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

vector<Widget> vw;

class Special Widget: // SpecialWidget наследует от класса

 public Widget{...}; // Widget (см. ранее)

SpecialWidget sw; // sw копируется в vw как объект базового класса

vw.push_back(sw); // Специализация объекта теряется (отсекается)

Проблема отсечения предполагает, что вставка объекта производного класса в контейнер объектов базового класса обычно приводит к ошибке. А если вы хотите, чтобы полученный объект обладал поведением объекта производного класса (например, вызывал виртуальные функции объектов производного класса), вставка всегда приводит к ошибке. За дополнительной информацией обращайтесь к «Effective C++», совет 22. Другой пример проявления этой проблемы в STL описан в совете 38.

Существует простое решение, обеспечивающее эффективное, корректное и свободное от проблемы отсечения копирование — вместо объектов в контейнере хранятся указатели. Иначе говоря, вместо контейнера для хранения

Widget
создается контейнер для
Widget*
. Указатели быстро копируются, результат точно совпадает с ожидаемым (поскольку копируется базовое двоичное представление), а при копировании указателя ничего не отсекается. К сожалению, у контейнеров указателей имеются свои проблемы, обусловленные спецификой STL. Они рассматриваются в советах 7 и 33. Пытаясь справиться с этими проблемами и при этом не нажить хлопот с эффективностью, корректностью и отсечением, вы, вероятно, обнаружите симпатичную альтернативу — умные указатели. За дополнительной информацией обращайтесь к совету 7.

Если вам показалось, что STL злоупотребляет копированием, не торопитесь с выводами. Да, копирование в STL выполняется довольно часто, но в целом библиотека спроектирована с таким расчетом, чтобы избежать лишнего копирования. Более того, она избегает лишнего создания объектов. Сравните с поведением классического массива — единственного встроенного контейнера C и C++:

Widget w[maxNumWidgets]; // Создать массив объектов Widget

 // Объекты инициализируются конструктором

 // по умолчанию

В этом случае конструируются

maxNumWidgets
объектов
Widget
, даже если на практике будут использоваться лишь некоторые из них или все данные, инициализированные конструктором по умолчанию, будут немедленно перезаписаны данными, взятыми из другого источника (например, из файла). Вместо массива можно воспользоваться контейнером STL
vector
и создать вектор, динамически увеличивающийся в случае необходимости:

vector<Widget> vw; // Создать вектор, не содержащий ни одного

 // объекта Widget и увеличивающийся по мере

 // необходимости

Можно также создать пустой вектор, в котором зарезервировано место для

maxNumWidgets
объектов
Widget
, но не сконструирован ни один из этих объектов:

vector<Widget> vw;

vw.reserve(maxNumWidgets); // Функция reserve описана в совете 14

По сравнению с массивами контейнеры STL ведут себя гораздо цивилизованнее. Они создают (посредством копирования) столько объектов, сколько указано, и только по вашему требованию, а конструктор по умолчанию выполняется только с вашего разрешения. Да, контейнеры STL создают копии; да, в особенностях их работы необходимо хорошо разбираться, но не стоит забывать и о том, что они означают большой шаг вперед по сравнению с массивами.

Совет 4. Вызывайте empty вместо сравнения size с нулем

Для произвольного контейнера с следующие две команды фактически эквивалентны:

if (c.size==0)...

if (c.empty)...

Возникает вопрос — почему же предпочтение отдается одной конструкции, особенно если учесть, что

empty
обычно реализуется в виде подставляемой (inline) функции, которая просто сравнивает
size
с нулем и возвращает результат?

Причина проста: функция

empty
для всех стандартных контейнеров выполняется с постоянной сложностью, а в некоторых реализациях
list
вызов
size
требует линейных затрат времени.

Но почему списки так себя ведут? Почему они не обеспечивают выполнения

size
с постоянной сложностью? Это объясняется в основном уникальными свойствами функций врезки (
splicing
). Рассмотрим следующий фрагмент:

list<int> list1;

list<int> list2;

list1.splice( // Переместить все узлы list2

 list1.end,list2, // от первого вхождения 5

 find(list2.begin, list2.end, 5), // до последнего вхождения 10

 find(list2.rbegin, list2.rend, 10).base // в конец listl

); // Вызов base рассматривается

// в совете 28

Приведенный фрагмент не работает, если только значение 10 не входит в

list2
после 5, но пока не будем обращать на это внимания. Вместо этого зададимся вопросом: сколько элементов окажется в списке
list1
после врезки? Разумеется, столько, сколько было до врезки, в сумме с количеством новых элементов. Последняя величина равна количеству элементов в интервале, определяемом вызовами
find(list2.begin, list2.end, 5)
и
find(list2.rbegin,list2.rend,10).base
. Сколько именно? Чтобы ответить на этот вопрос, нужно перебрать и подсчитать элементы интервала. В этом и заключается проблема.

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

Я все еще не царь. Книга XXVI

Дрейк Сириус
26. Дорогой барон!
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Я все еще не царь. Книга XXVI

Сирота

Шмаков Алексей Семенович
1. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
5.00
рейтинг книги
Сирота

Мужчина моей судьбы

Ардова Алиса
2. Мужчина не моей мечты
Любовные романы:
любовно-фантастические романы
8.03
рейтинг книги
Мужчина моей судьбы

Газлайтер. Том 31

Володин Григорий Григорьевич
31. История Телепата
Фантастика:
боевая фантастика
попаданцы
альтернативная история
фэнтези
5.00
рейтинг книги
Газлайтер. Том 31

Апокриф

Вайс Александр
10. Фронтир
Фантастика:
боевая фантастика
космическая фантастика
космоопера
5.00
рейтинг книги
Апокриф

Третье правило диверсанта

Бычков Михаил Владимирович
Фантастика:
постапокалипсис
5.67
рейтинг книги
Третье правило диверсанта

Неправильный лекарь. Том 1

Измайлов Сергей
1. Неправильный лекарь
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Неправильный лекарь. Том 1

Вечный. Книга V

Рокотов Алексей
5. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга V

Убей героя

Кудрявцев Леонид Викторович
Фантастика:
детективная фантастика
5.00
рейтинг книги
Убей героя

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

Винокуров Юрий
32. Кодекс Охотника
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга XXXII

Газлайтер. Том 20

Володин Григорий Григорьевич
20. История Телепата
Фантастика:
боевая фантастика
аниме
попаданцы
5.00
рейтинг книги
Газлайтер. Том 20

Отряд

Валериев Игорь
5. Ермак
Фантастика:
альтернативная история
5.25
рейтинг книги
Отряд

Антология странного рассказа

Коллектив авторов
Проза:
современная проза
5.00
рейтинг книги
Антология странного рассказа

Почем цветочек аленький?

Луганцева Татьяна Игоревна
Женщина-цунами
Детективы:
иронические детективы
7.88
рейтинг книги
Почем цветочек аленький?