Наш текстовый процессор и его операции очень просты. Очевидно, что мы хотим создать простой и достаточно эффективный, а не “навороченный” редактор. Однако не следует ошибочно думать, что list
и vector
) в сочетании с правилами программирования (приемами), принятыми в библиотеке STL, согласно которым возврат итератора, установленного на конец последовательности, является признаком неудачи. Обратите внимание на то, что если бы мы захотели, то могли бы превратить класс Document
в контейнер STL, снабдив его итератором Text_iterator
. Мы сделали главное для представления объекта класса Document
в виде последовательности значений.
20.7. Классы vector, list и string
Почему для хранения строк мы используем класс list
, а для символов — класс vector
? Точнее, почему для хранения последовательности строк мы используем класс list
, а для хранения последовательности символов — класс vector
? Более того, почему для хранения строки мы не используем класс string
?
Сформулируем немного более общий вариант этого вопроса. Для хранения последовательности символов у нас есть четыре способа.
• char[]
(массив символов)
• vector
• string
• list
++
и использовать оператор *
для доступа к символам. Если посмотреть на примеры кода, связанного с классом Document, то мы действительно можем заменить наш класс vector
классом list
или string
без каких-либо проблем. Такая взаимозаменяемость является фундаментальным преимуществом, потому что она позволяет нам сделать выбор, ориентируясь на эффективность. Но, перед тем как рассматривать вопросы эффективности, мы должны рассмотреть логические возможности этих типов: что такого может делать каждый из них, чего не могут другие?
• Elem[]
. Не знает своего размера. Не имеет функций begin()
, end()
и других контейнерных функций-членов. Не может систематически проверять выход за пределы допустимого диапазона. Может передаваться функциям, написанным на языке C или в стиле языка C. Элементы в памяти располагаются последовательно в смежных ячейках. Размер массива фиксируется на этапе компиляции. Операции сравнения (==
и !=
) и вывода (<<
) используют указатель на первый элемент массива, а не на все элементы.
• vector
. Может выполнять практически все, включая функции insert()
и erase()
. Предусматривает индексирование. Операции над списками, такие как insert()
и erase()
, как правило, связаны с перемещением элементов (что может оказаться неэффективным для крупных элементов и при большом количестве элементов). Может проверять выход за пределы допустимого диапазона. Элементы в памяти располагаются последовательно в смежных ячейках. Объект класса vector
может увеличиваться (например, использует функцию push_back()
). Элементы вектора хранятся в массиве (непрерывно). Сравнение элементов осуществляется с помощью операторов ==
, !=
, <
, <=
, >
и >=
.
• string
. Предусматривает все обычные и полезные операции, а также специфические манипуляции текстами, такие как конкатенация (+
и +=
). Элементы хранятся в смежных ячейках памяти. Объект класса string
можно увеличивать. Сравнение элементов осуществляется с помощью операторов ==
, !=
, <
, <=
, >
и >=
.
• list
. Предусматривает все обычные и полезные операции, за исключением индексирования. Операции insert()
и delete()
можно выполнять без перемещения остальных элементов. Для хранения каждого элемента необходимы два дополнительных слова (для указателей на узлы). Объект класса list
можно увеличивать. Сравнение элементов осуществляется с помощью операторов (==
, !=
, <
, <=
, >
и >=
).
Как мы уже видели (см. разделы 17.2 и 20.5), массивы полезны и необходимы для управления памятью на самом нижнем уровне, а также для обеспечения взаимодействия с программами, написанными на языке C (подробнее об этом — в разделах 27.1.2 и 27.5). В отличие от этого, класс vector
является более предпочтительным, потому что его легче использовать, к тому же он более гибкий и безопасный.
ПОПРОБУЙТЕ