Эффективное использование STL
Шрифт:
Контейнер
map
эмулируется на базе сортированного вектора практически так же, как и контейнер set
. Единственное принципиальное отличие заключается в том, что в качестве функций сравнения используются объекты DataCompare
: vector<Widget> vd; // Альтернатива для map<string, int>
… // Подготовительная фаза: много вставок,
// мало операций поиска
sort(vd.begin, vd.end, DataCompare); // Конец подготовительной фазы
// (при эмуляции multiset можно
// воспользоваться алгоритмом
// stable_sort - см. совет 31)
string s; // Объект с искомым значением
… // Начало фазы поиска
if (binary_search(vd.begin, vd.end, s, DataCompare))… // Поиск
// с применением binary_search
vector<Data>::iterator i = lower_bound(vd.begin, vd.end.s,
DataCompare); // Поиск с применением
if (i != vd.end && !(i->first < s))… // lower_bound: конструкция
//!(i->first<s)) описана
//в совете 45
pair<vector<Data>::iterator, vector<Data>::iterator> range =
equal_range(vd.begin, vd.end, s, DataCompare); // Поиск с применением
if (range.first != range.second)… // equal_range
… // Конец фазы поиска,
// начало фазы реорганизации
sort(vd.begin, vd.end, DataCompare); //Начало новой фазы поиска…
Как видите, после написания
DataCompare
все более или менее становится на свои места. Показанное решение часто быстрее работает и расходует меньше памяти, чем аналогичная архитектура с настоящим контейнером map
— при условии, что операции со структурой данных в вашей программе делятся на фазы, описанные на с. 99. Если подобное деление на фазы не соблюдается, использование сортированного вектора вместо стандартных ассоциативных контейнеров почти всегда оборачивается напрасной тратой времени. Совет 24. Тщательно выбирайте между map::operator[] и map::insert
Допустим, у нас имеется класс
Widget
с конструктором по умолчанию, а также конструктором и оператором присваивания с операндом типа double: class Widget {
public:
Widget;
Widget(double weight);
Widget& operator=(double weight);
…
};
Предположим, мы хотим создать контейнер
map
, ассоциирующий int
с Widget
, и инициализировать созданное множество конкретными значениями. Все выглядит очень просто: map<int, Widget> m;
m[1]=1.50;
m[2]=3.67;
m[3]=10.5;
m[4]=45.8;
m[5]=0.0003;
Настолько просто, что легко упустить, что же здесь, собственно, происходит. А это очень плохо, потому что в действительности происходящее может заметно ухудшить быстродействие программы.
Функция
operator[]
контейнеров map
никак не связана с функциями operator[]
контейнеров vector
, deque
и string
, а также со встроенным оператором []
, работающим с массивами. Функция map::operator[]
упрощает операции «обновления с возможным созданием». Иначе говоря, при наличии объявления map<K, V> m
команда m[k]=v;
проверяет, присутствует ли ключ k
в контейнере. Если ключ отсутствует, он добавляется вместе с ассоциированным значением v
. Если ключ уже присутствует, ассоциированное с ним значение заменяется на v
. Для этого
operator[]
возвращает ссылку на объект значения, ассоциированного с ключом k
, после чего v присваивается объекту, к которому относится эта ссылка. При обновлении значения, ассоциированного с существующим ключом, никаких затруднений не возникает — в контейнере уже имеется объект, ссылка на который возвращается функцией operator[]
. Но при отсутствии ключа k
готового объекта, на который можно было бы вернуть ссылку, не существует. В этом случае объект создается конструктором по умолчанию, после чего operator[]
возвращает ссылку на созданный объект. Вернемся к началу исходного примера:
map<int, Widget> m;
m[1]=1.50;
Выражение
m[1]
представляет собой сокращенную запись для m.operator[](1)
, поэтому во второй строке присутствует вызов map::operator[]
. Функция должна вернуть ссылку на ассоциированный объект Widget
. В данном примере m
еще не содержит ни одного элемента, поэтому элемент с ключом 1 не существует. Конструктор по умолчанию создает объект Widget
, ассоциируемый с ключом 1, и возвращает ссылку на этот объект. Наконец, созданному объекту Widget
присваивается значение 1.50. Иначе говоря, команда
m[1]=1.50:
функционально эквивалентна следующему фрагменту:
typedef map<int, Widget> intWidgetMap; // Вспомогательное определение типа
pair<intWidgetMap::iterator, bool> result = // Создание нового
m.insert(intWidgetMap::value_type(1, Widget)); // элемента с ключом 1
// и ассоциированным объектом, созданным
// конструктором по умолчанию; комментарии
// по поводу value_type
// приведены далее
result.first->second = 1.50; // Присваивание значения
// созданному объекту
Теперь понятно, почему такой подход ухудшает быстродействие программы. Сначала мы конструируем объект
Widget
, а затем немедленно присваиваем ему новое значение. Конечно, правильнее было бы сразу сконструировать Widget
с нужными данными вместо того, чтобы конструировать Widget
по умолчанию и затем выполнять присваивание. Следовательно, вызов operator[]
было бы правильнее заменить прямолинейным вызовом insert
:
Поделиться:
Популярные книги
Алекс и Алекс
1. Алекс и Алекс
Фантастика:
боевая фантастика
6.83
рейтинг книги
Газлайтер. Том 4
4. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Валькирия
Фантастика:
фэнтези
9.49
рейтинг книги
Ваше Сиятельство 8
8. Ваше Сиятельство
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Точка Бифуркации
1. ТБ
Фантастика:
боевая фантастика
7.33
рейтинг книги
Хозяин Стужи
1. Злой Лед
Фантастика:
аниме
фэнтези
попаданцы
7.00
рейтинг книги
Воплощение Похоти
1. Воплощение Похоти
Фантастика:
юмористическое фэнтези
попаданцы
рпг
аниме
5.00
рейтинг книги
Иной. Том 5. Адская работа
5. Иной в голове
Фантастика:
боевая фантастика
городское фэнтези
технофэнтези
рпг
5.00
рейтинг книги
Инженер Петра Великого 4
4. Инженер Петра Великого
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Deus vult
3. Путь князя
Фантастика:
фэнтези
рпг
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга XXXIV
34. Кодекс Охотника
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Звезда Чёрного Дракона
2. Нежеланная невеста
Любовные романы:
любовно-фантастические романы
4.40
рейтинг книги
Я снова не князь! Книга XVII
17. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
6.25
рейтинг книги
Гримуар темного лорда VIII
8. Гримуар темного лорда
Фантастика:
боевая фантастика
альтернативная история
аниме
фэнтези
фантастика: прочее
попаданцы
5.25