Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 143 144 145 146 147 148 149 150 151 ... 297
Перейти на страницу:

// четыре способа добавления слова в word_count

word_count.insert({word, 1});

word_count.insert(make_pair(word, 1));

word_count.insert(pair<string, size_t>(word, 1));

word_count.insert(map<string, size_t>::value_type(word, 1));

Как уже упоминалось, по новому стандарту простейшим способом создания пары является инициализация списком аргументов в фигурных скобках. В качестве альтернативы можно вызвать функцию make_pair() или явно создать пару. Вот аргументы последнего вызова функции insert():

map<string, size_t>::value_type(s, 1)

Он создает новый объект пары соответствующего типа для вставки в карту.

Проверка значения, возвращаемого функцией insert()

Значение, возвращенное функцией insert() (или emplace()), зависит от типа контейнера и параметров. Для контейнеров с уникальными ключами есть версии функций insert() и emplace(), которые добавляют один элемент и возвращают пару, сообщающую об успехе вставки. Первая переменная-член пары — итератор на элемент с заданным ключом; второй — логическое значение, указывающее на успех вставки элемента. Если такой ключ уже был в контейнере, то функция insert() не делает ничего, а логическая часть возвращаемого значения содержит false. Если такой ключ отсутствовал, то логическая часть содержит значение true.

Для примера перепишем программу подсчета слов с использованием функции insert():

// более корректный способ подсчета слов во вводе

map<string, size_t> word_count; // пустая карта строк и чисел

string word;

while (cin >> word) {

 // вставляет элемент с ключом, равным слову, и значением 1;

 // если слово уже есть в word_count, insert() не делает ничего

 auto ret = word_count.insert({word, 1});

 if (!ret.second) // слово уже было в word_count

  ++ret.first->second; // приращение счетчика

}

Для каждой строки word осуществляется попытка вставки со значением 1. Если слово уже находится в карте, ничего не происходит. В частности, связанный со словом счетчик остается неизменным. Если слова еще нет в карте, оно добавляется, а значение его счетчика устанавливается в 1.

Оператор if проверяет логическую часть возвращаемого значения. Если это значение false, то вставка не произошла. Следовательно, слово уже было в карте word_count, поэтому следует увеличить значение связанного с ним счетчика.

Еще раз о синтаксисе

Оператор приращения счетчика в этой версии программы подсчета слов трудно понять. Разобрать это выражение будет существенно проще, если сначала расставить скобки в соответствии с приоритетом (см. раздел 4.1.2) операторов:

++((ret.first)->second); // эквивалентное выражение

Рассмотрим это выражение поэтапно.

• ret — пара, содержащая значение, возвращаемое функцией insert().

• ret.first — первая переменная-член пары, на которую указывает итератор карты, с данным ключом.

• ret.first-> — обращение к значению итератора, позволяющее получить этот элемент. Элементы карты также являются парами.

• ret.first->second — та часть пары элемента карты, которая является значением.

• ++ret.first->second — инкремент этого значения.

Таким образом, оператор инкремента получает итератор для элемента с ключом слова и увеличивает счетчик, связанный с ключом, для которого не удалась попытка вставки.

Для читателей, использующих устаревший компилятор или код, предшествующий новому стандарту, объявление и инициализация пары ret также не совсем очевидны:

pair<map<string, size_t>::iterator, bool> ret =

 word_count.insert(make_pair(word, 1));

Здесь определяется пара, вторая переменная-член которой имеет тип bool. Понять тип первой переменной-члена этой пары немного труднее. Это тип итератора, определенный типом map<string, size_t>.

Добавление элементов в контейнеры multiset и multimap

Работа программы подсчета слов зависит от того факта, что каждый ключ может присутствовать только однажды. Таким образом, с любым словом будет связан только один счетчик. Но иногда необходима возможность добавить дополнительные элементы с тем же ключом. Например, могло бы понадобиться сопоставить авторов с названиями написанных ими книг. В данном случае для каждого автора могло бы быть несколько записей, поэтому будет использован контейнер multimap, а не map. Поскольку ключи контейнеров multi не должны быть уникальным, функция insert() для них всегда вставляет элемент:

multimap<string, string> authors;

// добавляет первый элемент с ключом Barth, John

authors.insert({"Barth, John", "Sot-Weed Factor"});

// ok: добавляет второй элемент с ключом Barth, John

authors.insert({"Barth, John", "Lost in the Funhouse"});

У контейнеров, допускающих совпадение ключей, функция insert() получает один элемент и возвращает итератор на новый элемент. Нет никакой необходимости возвращать логическое значение, поскольку в эти контейнеры функция insert() всегда добавляет новый элемент.

Упражнения раздела 11.3.2

Упражнение 11.20. Перепишите программу подсчета слов из раздела 11.1 так, чтобы использовать функцию insert() вместо индексации. Какая версия программы по-вашему проще? Объясните почему.

Упражнение 11.21. С учетом того, что word_count является картой типов string и size_t, а также того, что word имеет тип string, объясните следующий цикл:

while (cin >> word)

 ++word_count.insert({word, 0}).first->second;

Упражнение 11.22. С учетом, что map<string, vector<int>>, напишите типы, используемые как аргументы, и возвращаемое значение версии функции insert(), вставляющей один элемент.

Упражнение 11.23. Перепишите карту, хранящую вектора имен детей с ключом в виде фамилии семьи из упражнений раздела 11.2.1, так, чтобы использовался контейнер multimap.

11.3.3. Удаление элементов

Ассоциативные контейнеры определяют три версии функции erase(), описанные в табл. 11.5. Подобно последовательным контейнерам, можно удалить один элемент или диапазон элементов, передав функции erase() итератор или пару итераторов. Эти версии функции erase() подобны соответствующим функциям последовательных контейнеров: указанный элемент (элементы) удаляется и возвращается тип void.

Таблица 11.5. Удаление элементов ассоциативного контейнера

c.erase(k) Удаляет из карты с элемент с ключом k. Возвращает значение типа size_type, указывающее количество удаленных элементов c.erase(p) Удаляет из карты с элемент, обозначенный итератором p. Итератор p должен относиться к фактически существующему элементу карты с, он не может быть равен итератору, возвращаемому функцией c.end(). Возвращает итератор на элемент после позиции p или c.end(), если итератор p обозначает последний элемент контейнера с c.erase(b, е) Удаляет элементы в диапазоне, обозначенном парой итераторов b и е. Возвращает итератор е

Ассоциативные контейнеры предоставляют дополнительную версию функции erase(), получающую аргумент типа key_type. Эта версия удаляет все элементы, если таковые вообще имеются, с заданным ключом и возвращает количество удаленных элементов. Эту версию можно использовать для удаления определенных слов из контейнера word_count прежде, чем вывести результат:

// удалить по ключу, возвратить количество удаленных элементов

if (word_count.erase(removal_word))

 cout << "ok: " << removal_word << " removedn";

else

 cout << "oops: " << removal_word << " not found!n";

Для контейнеров с уникальными ключами функция erase() всегда возвращает нуль или единицу. Если возвращается значение нуль, значит, удаляемого элемента не было в контейнере.

Для контейнеров с не уникальными ключами функция erase() возвращает количество удаленных элементов и может быть больше единицы:

auto cnt = authors.erase("Barth, John");

Если authors — это контейнер multimap, созданный в разделе 11.3.2, то переменная cnt будет содержать значение 2.

11.3.4. Индексация карт

Контейнеры map и unordered_map предоставляют оператор индексирования и соответствующую функцию at() (см. раздел 9.3.2), представленные в табл. 11.6. Типы контейнеров set не поддерживают индексацию, поскольку в наборе нет никакого "значения", связанного с ключом. Элементы сами являются ключами, поэтому операция "доступа к значению, связанному с ключом", бессмысленна. Нельзя индексировать контейнер multimap или unordered_multimap, поскольку с заданным ключом может быть ассоциировано несколько значений.

1 ... 143 144 145 146 147 148 149 150 151 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии