std::vector
Размер std::array входит в его тип: std::array<int, 5> хранит ровно пять чисел. Но часто количество значений становится известно только во время работы программы. Например, программа, которая отбирает числа по условию, заранее не знает, сколько их наберётся. Такие данные хранят в std::vector:
#include <iostream>
#include <vector>
int main() {
std::vector<int> found;
for (int i = 1; i <= 10; ++i) {
if (i % 3 == 0) {
found.push_back(i);
}
}
std::cout << found.size() << '\n'; // 3
for (int x : found) {
std::cout << x << ' '; // 3 6 9
}
}Контейнер (container) — объект, который хранит набор элементов одного типа. std::vector<int> — контейнер чисел типа int: тип элементов пишут в угловых скобках. Новый вектор пуст, push_back добавляет элемент в конец, а size() возвращает число элементов. Цикл по диапазону обходит элементы в порядке добавления.
Доступ по индексу
Вектор можно сразу заполнить значениями в фигурных скобках. Как и у массива, v[0] — первый элемент, а v[v.size() - 1] — последний. Обращение по индексу за пределами вектора — неопределённое поведение (undefined behavior).
std::vector<int> v{4, 8, 15};
for (std::size_t i = 0; i < v.size(); ++i) {
v[i] = v[i] * 2;
}
std::cout << v[0] << ' ' << v[2] << '\n'; // 8 30Как и у строки, size() возвращает std::size_t, поэтому счётчик для индексов объявляют того же типа. v.empty() возвращает true, если в векторе нет элементов. v.back() возвращает последний элемент, а v.pop_back() удаляет его. Элементом вектора может быть и другой вектор: std::vector<std::vector<int>> хранит таблицу по строкам.
Размер и ёмкость
Элементы вектора лежат в памяти подряд, одним блоком, и в блоке может остаться свободное место. Сколько его, стандарт не задаёт: у вектора v{1, 2, 3} его может не быть совсем. Ёмкость (capacity) — сколько элементов поместится в блок, её возвращает capacity(). Пока size() меньше capacity(), push_back кладёт элемент в свободное место.
Когда места нет, push_back выделяет блок побольше, переносит в него элементы и освобождает старый. Это перевыделение памяти (reallocation). Насколько вырастет ёмкость, решает стандартная библиотека, и программа не должна на это рассчитывать.
reserve и resize
Если число элементов известно заранее, место можно выделить сразу. После reserve(n) ёмкость не меньше n, но элементов не прибавляется: size() остаётся прежним. Если n не больше текущей ёмкости, вызов ничего не делает. resize(n) меняет сам размер: недостающие элементы получают значение 0, как int x{}, а лишние удаляются.
std::vector<int> a;
a.reserve(100);
std::cout << a.size() << ' ' << (a.capacity() >= 100) << '\n'; // 0 1
std::vector<int> b{7};
b.resize(3);
std::cout << b.size() << ' ' << b[0] << ' ' << b[2] << '\n'; // 3 7 0Частая ошибка
После reserve элементы записывают по индексу:
std::vector<int> v;
v.reserve(3);
v[0] = 7; // ✗Вызов reserve выделил память, но элементов не создал: v.size() по-прежнему равен 0, и v[0] обращается за пределы вектора. Это неопределённое поведение, хотя программа компилируется без предупреждений. Добавляйте элементы через push_back или задайте размер через resize(3).