Перехват out_of_range, определенного в , позволяет грамотно справиться с неправильными индексами. А также можно вызвать метод what, позволяющий в зависимости от используемой реализации получить осмысленное сообщение об ошибке, как возвращаемая в коде примера 4.7:
invalid vector
Однако vector не является единственной возможностью. В C++ имеется большое количество способов хранить последовательности. Кроме vector имеются list, set и двунаправленные очереди (deque — double-ended queue). Все они поддерживают множество одинаковых операций, и каждый поддерживает свои собственные. Кроме того, каждый имеет различную алгоритмическую сложность, требования по хранению и семантику. Так что имеется богатый выбор.
Посмотрите внимательно на пример 4.6. Вы, вероятно, обратите внимание, что я изменяю значение строки s до того, как добавляю ее в конец контейнера с помощью push_back. Логично ожидать такого вывода этого примера
three
three
three
Я поместил в вектор одну и ту же строку три раза, так что каждый раз, когда я переприсваиваю строку, разве не должны все элементы вектора указывать на одну и ту же строку? Нет. Это важный момент, касающийся контейнеров STL.
Контейнеры STL сохраняют копии объектов, помещаемых в них, а не сами объекты. Так что после помещения в контейнер всех трех строк в памяти остается четыре строки: три копии, созданные и хранящиеся в контейнере, и одна копия, которой присваиваются значения.
Ну и что? Было создано несколько новых копий: большое дело. Но это действительно большое дело, так как если используется большое количество строк, за каждую копию приходится платить процессорным временем, памятью или и тем и другим. Копирование элементов в контейнерах — это намеренное поведение STL, и все контейнеры организованы именно так.
Одним из решений (определенно delete указатели при его уничтожении. Память для указателей выделяет ваш код, так что он и должен ее очищать. Это относится и к ситуации, когда происходит полное удаление контейнера и когда удаляется только один его элемент.
В целях создания альтернативного решения давайте рассмотрим еще одну возможность. Рассмотрим шаблон класса list, определенный в , который является двусвязным списком (doubly linked list). Если планируется большое количество вставок и удалений элементов в середине последовательности или если требуется гарантировать, что итераторы, указывающие на элементы последовательности, не станут недействительными при ее изменении, используйте list. Пример 4.8 вместо vector для хранения нескольких строк типа string использует list. Также он для перебора этих строк и печати вместо оператора индекса, как это делается в случае с простыми массивами, использует for_each.
#include
#include
#include
#include
using namespace std;
void write(const string& s) {
cout << s << '\n';
}
int main() {
list
string s = "нож";
lst.push_front(s);
s = "вилка";
lst.push_back(s);
s = "ложка";
lst.push_back(s);
// У списка нет произвольного доступа, так что
// требуется использовать for_each()
for_each(lst.begin(), lst.end(), write);
}
Целью этого отступления от первоначальной проблемы (хранения строк в виде последовательностей) является краткое введение в последовательности STL. Здесь невозможно дать полноценное описание этого вопроса. За обзором STL обратитесь к главе 10 книги
4.4. Получение длины строки
Требуется узнать длину строки.
Используйте метод length класса string.
std::string s = "Raising Arizona";
int i = s.length();
Получение длины строки — это тривиальная задача, но она является хорошей возможностью обсудить схему размещения string (как узких, так и широких символов). string, в отличие от массивов строк, завершаемых нулем, в С являются динамическими и увеличиваются по мере надобности. Большая часть реализаций стандартной библиотеки начинают с относительно низкой емкости и увеличивают ее в два раза каждый раз, когда достигается предел. Знание того, как анализировать этот рост, если и не точного алгоритма, помогает диагностировать проблемы производительности, связанные со строками.