Выбрать главу

Оператор [] полезен при выполнении вычислений, для которых нужно очень быстро обращаться к проиндексированным элементам вектора. В любой другой ситуации функция 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, решающей задачу, которой посвящен этот раздел. Для каждой новой строки эта функция определяет позицию в отсортированном векторе (куда нужно произвести вставку), позволяющую сохранить порядок строк в векторе. Однако мы предполагаем, что вектор был отсортирован заранее. Иначе этот подход не сработает.