Векторы  —  это универсальные и популярнейшие контейнеры на C++. Умение получить и использовать размер вектора важно для эффективного программирования.

Изучим различные методы получения размера вектора с их нюансами и практическим применением.

Основные методы получения размера вектора

Начнем с простейших способов определения размера.

Функция size()

Вот как ею получается количество элементов вектора:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::cout << "The size of the vector is: " << numbers.size() << std::endl;
return 0;
}

Этим кодом выводится: The size of the vector is: 5, то есть размер вектора  —  5.

Функцией size() возвращается актуальное количество элементов вектора. Это операция константного времени, то есть очень быстрая при любом размере вектора.

empty()

Для простой проверки, содержатся ли в векторе элементы, пригоднее функция empty():

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers;
if (numbers.empty()) {
std::cout << "The vector is empty" << std::endl;
} else {
std::cout << "The vector is not empty" << std::endl;
}
return 0;
}

Этим кодом выводится: The vector is empty, то есть вектор пуст.

Функция empty() обычно эффективнее проверки того, не равна ли size() нулю  —  особенно для сложных контейнерных типов.

Продвинутые функции, связанные с размером вектора

Помимо size() и empty(), размер вектора в конкретных сценариях определяется и другими функциями.

max_size()

Функцией max_size() возвращается максимальное количество элементов вектора:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers;
std::cout << "Maximum size of the vector: " << numbers.max_size() << std::endl;
return 0;
}

Здесь выводится очень большое число  —  как правило, это максимальное значение size_t в системе.

capacity()

Функцией capacity() возвращается актуальное количество элементов вектора, для которого не требуется выделения дополнительной памяти:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers;
std::cout << "Initial capacity: " << numbers.capacity() << std::endl;

numbers.push_back(1);
std::cout << "Capacity after adding one element: " << numbers.capacity() << std::endl;

numbers.reserve(10);
std::cout << "Capacity after reserving space for 10 elements: " << numbers.capacity() << std::endl;

return 0;
}

В этом коде демонстрируется, как изменяется такой размер при добавлении элементов или резервировании места.

Практические применения размера вектора

Рассмотрим реальные сценарии, в которых получение размера вектора приходится кстати.

Прохождение вектора

Знание размера вектора важно при итеративном обходе его элементов:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

for (size_t i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;

return 0;
}

Этим кодом выводится: 1 2 3 4 5.

Обратите внимание на использование size_t для переменной цикла. Этот возвращаемый функцией size() тип достаточно большой, чтобы им гарантированно представить размер любого объекта в памяти.

Изменение размера вектора

Размер вектора изменяется при помощи текущего размера:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::cout << "Original size: " << numbers.size() << std::endl;

numbers.resize(numbers.size() * 2);
std::cout << "New size after doubling: " << numbers.size() << std::endl;

return 0;
}

Этим кодом размер вектора удваивается, причем новые элементы заполняются значением по умолчанию  —  для целых чисел это 0.

Реализация динамического стека

Размер вектора важен при реализации таких структур данных, как стек:

#include <iostream>
#include <vector>
#include <stdexcept>

template <typename T>
class Stack {
private:
std::vector<T> elements;

public:
void push(const T& value) {
elements.push_back(value);
}

T pop() {
if (elements.empty()) {
throw std::out_of_range("Stack is empty");
}
T top = elements.back();
elements.pop_back();
return top;
}

size_t size() const {
return elements.size();
}

bool empty() const {
return elements.empty();
}
};

int main() {
Stack<int> stack;

stack.push(1);
stack.push(2);
stack.push(3);

std::cout << "Stack size: " << stack.size() << std::endl;

while (!stack.empty()) {
std::cout << "Popped: " << stack.pop() << std::endl;
}

return 0;
}

В этой реализации состояние стека управляется векторными функциями size() и empty().

Производительность

Манипулирование размерами векторов сказывается на производительности. Рассмотрим соответствующие сценарии.

Резервирование места, когда размеры известны

Если конечный размер вектора известен, производительность повышается заблаговременным резервированием места:

#include <iostream>
#include <vector>
#include <chrono>

int main() {
const int NUM_ELEMENTS = 1000000;

auto start = std::chrono::high_resolution_clock::now();
std::vector<int> v1;
for (int i = 0; i < NUM_ELEMENTS; ++i) {
v1.push_back(i);
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to fill vector without reserve: " << diff.count() << " s\n";

start = std::chrono::high_resolution_clock::now();
std::vector<int> v2;
v2.reserve(NUM_ELEMENTS);
for (int i = 0; i < NUM_ELEMENTS; ++i) {
v2.push_back(i);
}
end = std::chrono::high_resolution_clock::now();
diff = end - start;
std::cout << "Time to fill vector with reserve: " << diff.count() << " s\n";

return 0;
}

Этим кодом демонстрируются, как сказывается на производительности резервирование места, когда известен конечный размер вектора.

Сокращение выделенной памяти для соответствия размеру

Чтобы высвободить память после удаления элементов из вектора, применяется shrink_to_fit():

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers(100, 1); // Вектор, где 100 элементов
std::cout << "Size: " << numbers.size() << ", Capacity: " << numbers.capacity() << std::endl;

numbers.resize(10); // Размер уменьшается до 10
std::cout << "After resize - Size: " << numbers.size() << ", Capacity: " << numbers.capacity() << std::endl;

numbers.shrink_to_fit(); // До нового размера уменьшается и количество элементов, для которого не требуется выделения дополнительной памяти
std::cout << "After shrink_to_fit - Size: " << numbers.size() << ", Capacity: " << numbers.capacity() << std::endl;

return 0;
}

Так сокращается использование памяти по завершении обработки большого вектора.

Типичные ошибки и как их избежать

При работе с размерами векторов важно избежать типичных ошибок.

Путаница с размером и выделенной под него памятью

Не забываем, что size() и capacity()  —  не одно и то же:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers;
numbers.reserve(10);

std::cout << "Size: " << numbers.size() << std::endl;
std::cout << "Capacity: " << numbers.capacity() << std::endl;

return 0;
}

Здесь выводится: Size: 0 Capacity: 10, то есть размер  —  0, выделенная память  —  10.

reserve() сказывается не на размере, а на выделенной памяти.

Ошибки смещения на единицу

При использовании size() в циклах будьте осторожны с ошибками смещения на единицу:

#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

// Не корректно: получится доступ к элементу за пределами границ
for (size_t i = 0; i <= numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;

// Корректно
for (size_t i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;

return 0;
}

При прохождении вплоть до size() всегда вместо <= используйте <.

Заключение

Умение получить и использовать размер вектора важно для эффективного программирования на C++. С использованием этих концепций  —  от базовых запросов размера до оптимизации производительности  —  пишется более эффективный, надежный код.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи ryan: Vector Size in C++: A Comprehensive Guide

Предыдущая статья7 лучших ресурсов для iOS-разработчиков в 2025 году
Следующая статьяКэширование трендовых новостей в приложении TrendNow с помощью OkHttp Cache. Часть 6