Эффективное использование 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
:
Поделиться:
Популярные книги
Хранилище
5. Лесовик
Фантастика:
фэнтези
рпг
7.43
рейтинг книги
Морской волк. 1-я Трилогия
1. Морской волк
Фантастика:
альтернативная история
8.71
рейтинг книги
Искатель 1
1. Валинор
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Анти-Ксенонская Инициатива
7. Фронтир
Фантастика:
боевая фантастика
космическая фантастика
космоопера
5.00
рейтинг книги
На границе империй. Том 10. Часть 8
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
Мастер решений
3. Специалист по выживанию
Фантастика:
боевая фантастика
космическая фантастика
6.20
рейтинг книги
Барон нарушает правила
3. Закон сильного
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Хозяин Теней
1. Безбожник
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Последний реанорец. Том IX
8. Высшая Речь
Фантастика:
фэнтези
попаданцы
аниме
5.75
рейтинг книги
Я до сих пор князь. Книга XXII
22. Дорогой барон!
Фантастика:
юмористическое фэнтези
аниме
попаданцы
5.00
рейтинг книги
Источник
11. Бедовый
Фантастика:
юмористическое фэнтези
городское фэнтези
мистика
7.00
рейтинг книги
Неучтенный элемент. Том 10
10. Антимаг. Вне системы
Фантастика:
фэнтези
5.00
рейтинг книги
Последний Паладин. Том 13
13. Путь Паладина
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Дитя прибоя
Дитя прибоя
Фантастика:
боевая фантастика
попаданцы
фэнтези
5.00