Оператор [] полезен при выполнении вычислений, для которых нужно очень быстро обращаться к проиндексированным элементам вектора. В любой другой ситуации функция at помогает определять ошибки, при этом вы почти не теряете в производительности.
Широко практикуется использование функции
at по умолчанию. Если полученный код слишком медленный, но при этом безошибочный, то вместо данной функции можно задействовать оператор [] в тех местах, где важна высокая производительность.
Дополнительная информация
Конечно, можно обработать ситуацию выполнения доступа к элементу, лежащему за пределами вектора, вместо того чтобы прерывать работу программы. Для ее обработки нужно перехватить исключение, которое в нашем случае будет сгенерировано функцией at. Сделать это нетрудно. Мы окружим вызов функции at блоком try и определим код обработки ошибки в блоке catch:
try {
std::cout << "Out of range element value: "
<< v.at(container_size + 10) << '\n';
} catch (const std::out_of_range &e) {
std::cout << "Ooops, out of range access detected: "
<< e.what() << '\n';
}
Кстати говоря, контейнер
std::array также предоставляет функцию at.
Сохраняем сортировку экземпляров класса std::vector
Массивы и векторы не сортируются самостоятельно. Если нам потребуется такая возможность, мы всегда можем воспользоваться структурами данных, которые предоставляют ее автоматически. Контейнер std::vector идеально подходит для нашего случая, ведь добавлять в него новые элементы в порядке сортировки несложно и удобно.
Как это делается
В этом примере мы заполним контейнер std::vector случайными словами, отсортируем их, а затем вставим дополнительные слова с учетом сортировки.
1. Сначала включим все необходимые заголовочные файлы:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <cassert>
2. Кроме того, объявим пространство имен std, чтобы не писать префиксы std:::
using namespace std;
3. Далее напишем небольшую функцию main, в которой вектор заполняется случайными строками:
int main()
{
vector<string> v {"some", "random", "words",
"without", "order", "aaa",
"yyy"};
4. Затем отсортируем вектор. Для этого воспользуемся некоторыми утверждениями и функцией is_sorted из STL, показывающей, что изначально вектор не был отсортирован, а теперь все его элементы расположены по порядку:
assert(false == is_sorted(begin(v), end(v)));
sort(begin(v), end(v));
assert(true == is_sorted(begin(v), end(v)));
5. Наконец, добавим случайные слова в отсортированный вектор с помощью новой функции insert_sorted, которую будем реализовывать далее. Эти слова сразу нужно помещать в правильную позицию, поэтому вектор останется отсортированным:
insert_sorted(v, "foobar");
insert_sorted(v, "zzz");
6. Теперь реализуем функцию insert_sorted и расположим ее перед функцией main:
void insert_sorted(vector<string> &v, const string &word)
{
const auto insert_pos (lower_bound(begin(v), end(v), word));
v.insert(insert_pos, word);
}
7. Теперь вернемся в функцию main — туда, где мы остановились, — и продолжим работу, выведя содержимое вектора и увидев, что процедура вставки отработала:
for (const auto &w : v) {
cout << w << " ";
}
cout << '\n';
}
8. Компиляция и запуск программы дадут следующий результат:
aaa foobar order random some without words yyy zzz
Как это работает
Вся программа построена вокруг функции insert_sorted, решающей задачу, которой посвящен этот раздел. Для каждой новой строки эта функция определяет позицию в отсортированном векторе (куда нужно произвести вставку), позволяющую сохранить порядок строк в векторе. Однако мы предполагаем, что вектор был отсортирован заранее. Иначе этот подход не сработает.