Векторы — это универсальные и популярнейшие контейнеры на 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++. С использованием этих концепций — от базовых запросов размера до оптимизации производительности — пишется более эффективный, надежный код.
Читайте также:
- C++: практическое руководство по rotate
- C++: подробное руководство по вложенным операторам If-Else
- C++: подробное руководство по cортированным векторам
Читайте нас в Telegram, VK и Дзен
Перевод статьи ryan: Vector Size in C++: A Comprehensive Guide





