82. Используйте подходящие идиомы для реального уменьшения емкости контейнера и удаления элементов

82. Используйте подходящие идиомы для реального уменьшения емкости контейнера и удаления элементов

Резюме

Для того чтобы действительно избавиться от излишней емкости контейнера, воспользуйтесь трюком с использованием обмена, а для реального удаления элементов из контейнера — идиомой erase-remove.

Обсуждение

Некоторые контейнеры (например, vector, string, deque) могут иметь "лишнюю" емкость, которая больше не будет использоваться. Хотя стандартная библиотека C++ не предоставляет гарантированного способа для удаления излишней емкости, следующая идиома на практике оказывается вполне работоспособной:

container<T>(c).swap(c); // Идиома "горячей посадки" для

                         // устранения излишней емкости

                         // контейнера

Для того чтобы полностью опустошить c, удалив все элементы и убрав всю емкость, идиома должна выглядеть следующим образом:

container<T>().swap(c); // Идиома для удаления всего

                        // содержимого и емкости

Кроме того, обычно для новичков в программировании с использованием STL оказывается сюрпризом то, что алгоритм remove в действительности не удаляет элементы из контейнера. Понятно, что данный алгоритм на это не способен — ведь алгоритм работает только с диапазоном итераторов и не может ничего реально удалить из контейнера без вызова функции-члена контейнера, обычно erase. Удаление сводится к перемещению элементов, которые должны быть "удалены", и возврату итератора, указывающего на элемент, следующий за последним неудаленным. Для реального удаления элементов из контейнера после вызова remove следует вызвать erase — воспользоваться идиомой erase-remove. Например, для реального удаления всех элементов, равных value, из контейнера с, можно написать:

c.erase(remove(c.begin(), c.end(), value), c.end());

Если контейнер имеет собственную версию remove или remove_if, желательно использовать именно ее.

Исключения

Описанная идиома "горячей усадки" не работает с реализациями std::string с копированием при записи. Обычно работает вызов s.reserve(0) или такой трюк, как string(s.begin(), s.end()).swap(s);, в котором использован конструктор на основе двух итераторов. На практике эти методы обычно работают и устраняют излишнюю емкость. (Было бы еще лучше, чтобы реализации std::string не использовали такой устаревший метод оптимизации, как копирование при записи; см. [Sutter02].)

Ссылки

[Josuttis99] §6.2.1 • [Meyers01] §17, §32, §44 • [Sutter00] §7 • [Sutter02] §7, §16