Шрифт:
•
vector<Elem>
. Может выполнять практически все, включая функции insert
и erase
. Предусматривает индексирование. Операции над списками, такие как insert
и erase
, как правило, связаны с перемещением элементов (что может оказаться неэффективным для крупных элементов и при большом количестве элементов). Может проверять выход за пределы допустимого диапазона. Элементы в памяти располагаются последовательно в смежных ячейках. Объект класса vector
может увеличиваться (например, использует функцию push_back
). Элементы вектора хранятся в массиве (непрерывно). Сравнение элементов осуществляется с помощью операторов ==
, !=
, <
, <=
, >
и >=
. •
string
. Предусматривает все обычные и полезные операции, а также специфические манипуляции текстами, такие как конкатенация (+
и +=
). Элементы хранятся в смежных ячейках памяти. Объект класса string
можно увеличивать. Сравнение элементов осуществляется с помощью операторов ==
, !=
, <
, <=
, >
и >=
. •
list<Elem>
. Предусматривает все обычные и полезные операции, за исключением индексирования. Операции insert
и delete
можно выполнять без перемещения остальных элементов. Для хранения каждого элемента необходимы два дополнительных слова (для указателей на узлы). Объект класса list
можно увеличивать. Сравнение элементов осуществляется с помощью операторов (==
, !=
, <
, <=
, >
и >=
). Как мы уже видели (см. разделы 17.2 и 20.5), массивы полезны и необходимы для управления памятью на самом нижнем уровне, а также для обеспечения взаимодействия с программами, написанными на языке C (подробнее об этом — в разделах 27.1.2 и 27.5). В отличие от этого, класс
vector
является более предпочтительным, потому что его легче использовать, к тому же он более гибкий и безопасный. ПОПРОБУЙТЕ
Что означает этот список отличий в реальном коде? Определите массивы объектов типа
char
, vector<char>
, list<char>
и string
со значением "Hello
", передайте его в функцию в качестве аргумента, напишите количество символов в передаваемой строке, попытайтесь сравнить его со строкой "Hello
" в функции (чтобы убедиться, что вы действительно передали строку "Hello
"), а затем сравните аргумент со строкой "Howdy
", чтобы увидеть, какое из этих слов появляется в словаре первым. Скопируйте аргумент в другую переменную того же типа. ПОПРОБУЙТЕ
Выполните предыдущее задание ПОПРОБУЙТЕ для массива объектов типа
int
, vector<int>
и list<int>
со значениями { 1
, 2
, 3
, 4
, 5
} . 20.7.1. Операции insert и erase
insert
и erase
), в векторе происходит перемещение остальных элементов; это может оказаться связано с неприемлемыми затратами, если вектор содержит большое количество элементов или элементы вектора сами являются крупными объектами. Однако слишком беспокоиться об этом не следует. Мы без заметных проблем считали полмиллиона значений с плавающей точкой в вектор, используя функцию push_back
. Измерения подтвердили, что предварительное выделение памяти не приводит к заметным последствиям. Прежде чем вносить значительные изменения, стремясь к эффективности, проведите измерения (угадать степень эффективности кода трудно даже экспертам).
insert
, erase
, and push_back
), не следует хранить итераторы или указатели на элементы вектора. Если элемент будет перемещен, ваш итератор или указатель будет установлен на неправильный элемент или вообще может не ссылаться на элемент вектора. В этом заключается принципиальное преимущество класса list
(и класса map
; см. раздел 21.6) над классом vector
. Если вам необходима коллекция крупных объектов или приходится ссылаться на объекты во многих частях программы, рассмотрите возможность использовать класс list
. Сравним функции
insert
и erase
в классах vector
и list
. Сначала рассмотрим пример, разработанный специально для того, чтобы продемонстрировать принципиальные моменты.
vector<int>::iterator p = v.begin; // получаем вектор
++p; ++p; ++p; // устанавливаем итератор
// на 4-й элемент
vector<int>::iterator q = p;
++q; // устанавливаем итератор
// на 5-й элемент
p = v.insert(p,99); // итератор p ссылается на вставленный элемент
Теперь итератор
q
является неправильным. При увеличении размера вектора элементы могли быть перемещены в другое место. Если вектор v
имеет запас памяти, то он будет увеличен на том же самом месте, а итератор q
скорее всего будет ссылаться на элемент со значением 3
, а не на элемент со значением 4
, но не следует пытаться извлечь из этого какую-то выгоду.
p = v.erase(p); // итератор p ссылается на элемент,
// следующий за стертым
Иначе говоря, если за функцией
insert
следует функция erase
, то содержание вектора не изменится, но итератор q
станет некорректным. Однако если между ними мы переместим все элементы вправо от точки вставки, то вполне возможно, что при увеличении размера вектора v
все элементы будут размещены в памяти заново.