lstOne.push_back("Red"); // Добавление элементов в конец списка
lstOne.push_back("Green");
lstOne.push_back("Blue");
lstTwo.push_front("Orange"); // Добавление элементов в начало
lstTwo.push_front("Yellow");
lstTwo.push_front("Fuschia");
Помещение элементов в list занимает постоянное время, а не vector. Реализации list не требуется периодически изменять размер буфера, так что в них не будет возникать периодических падений производительности, как при использовании vector. list просто должен обновить набор указателей, и больше ничего.
Для удаления элементов из начала или конца list используйте pop_front или pop_back (без аргументов). Несмотря на их имя, методы «pop» не возвращают извлекаемый элемент, как это можно ожидать, исходя из обычной семантики стеков.
Обычно list выглядит так, как показано на рис. 6.2. Каждый узел содержит (по меньшей мере) три части: объект, в нем содержащийся, указатель на предыдущий узел и указатель на следующий узел. В оставшейся части рецепта я буду ссылаться на указатели на следующий и предыдущий узлы как next_ и prev_.
Рис. 6.2. Список с двунаправленными связями
Когда вы увидите, как реализован list, станет очевидно, почему некоторые операции имеют сложность, отличную от их сложности для vector. Добавление элемента в любое место list требует только изменения указателей next_ и prev_ предыдущего и следующего элементов. Приятным моментом в list является то, что при вставке и удалении элементов в помощью insert и erase устаревают значения только тех итераторов, которые указывают на затрагиваемый(е) объект(ы). Итераторы для других элементов не теряют актуальности.
Методы вставки и удаления — это insert и erase, insert в качестве первого аргумента принимает итератор, а в качестве второго — либо объект типа T, либо количество и затем объект типа T, либо начальный и конечный итераторы. Первый аргумент-итератор указывает на элемент, непосредственно перед которым должна произойти вставка. Перегрузки insert используются вот так.
list
list
// ...
string s = "Scion";
p = find(strLst.begin(), strLst.end(), // std::find из
"Toyota");
strLst.insert(p, s); // Вставить s сразу перед p
strLst.insert(p, 16, s); // Вставить 16 копий s непосредственно перед p
strLst insert(p, myOtherStrLst.begin(), // Вставить все, что содержится
myOtherStrLst.end()); // в myOtherStrLst, перед p
Удаление элементов аналогично.
p = find(strLst.begin(), strLst.end(), // std::find из
"Toyota");
strLst1.erase(p); // Удаляем этот элемент
strLst2.erase(p, strLst.end()); // Удаляем от p до конца
strLst3.clear(); // Удаляем все элементы
В дополнение к методам стандартных контейнеров list предоставляет несколько своих. Первый — это splice.
splice делает то, что означает его имя: он объединяет два list в один. Вот как можно объединить lstTwo с lstOne из примера 6.5.
list
std::find(lstOne.begin(), // список
lstOne.end(), "Green");
lstOne.splice(p, lstTwo); // Вставляем lstTwo непосредственно перед "Green"
p — это итератор, который указывает на элемент в lstOne. lstTwo вставляется в lstTwo непосредственно перед p. Как и в случае со вставкой, все, что здесь требуется сделать, — это изменить указатели next_ и prev_ соответствующих узлов, так что эта операция занимает постоянное время. После объединения lstTwo с lstOne первый очищается, и именно поэтому этот параметр не объявлен как const. Также можно вставить в lstOne один элемент или диапазон элементов из lstTwo. В обоих случаях элементы, объединяемые с другим списком, удаляются из первоначального.