Базовая инициализация вектора размером

Простейший способ инициализировать вектор конкретным размером на C++  —  использовать конструктор, которым принимается параметр размера:

#include <iostream>
#include <vector>

int main() {
std::vector<int> vec(5); // Создается вектор из пяти целых чисел

for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

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

Инициализация конкретным значением

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

#include <iostream>
#include <vector>

int main() {
std::vector<int> vec(5, 10); // Создается вектор из пяти целых чисел, Создается вектор из пяти целых чисел, всем им задается значение «10»

for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

Выводится: 10 10 10 10 10.

resize() для изменения размера вектора

Размер имеющегося вектора изменяется функцией resize():

#include <iostream>
#include <vector>

int main() {
std::vector<int> vec;
vec.resize(5); // Изменяется размер вектора, в нем становится пять элементов

for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

vec.resize(8, 100); // Размер увеличивается до восьми, новые элементы инициализируются значением «100»

for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

Выводится:

0 0 0 0 0
0 0 0 0 0 100 100 100

Инициализация диапазоном значений

С помощью функции iota библиотеки <numeric> вектор инициализируется диапазоном значений:

#include <iostream>
#include <vector>
#include <numeric>

int main() {
std::vector<int> vec(5);
std::iota(vec.begin(), vec.end(), 1); // Заполняется значениями «1, 2, 3, 4, 5»

for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

Выводится: 1 2 3 4 5.

Инициализация вектора векторов

Для многомерных векторов выполняется вложенная инициализация:

#include <iostream>
#include <vector>

int main() {
std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));

for (const auto& row : matrix) {
for (int num : row) {
std::cout << num << " ";
}
std::cout << std::endl;
}

return 0;
}

При этом создается матрица 3 х 4, инициализированная нулями.

std::fill для инициализации вектора

Функцией std::fill всем элементам вектора задается конкретное значение:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> vec(5);
std::fill(vec.begin(), vec.end(), 7);

for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;

return 0;
}

Выводится: 7 7 7 7 7.

Реальный сценарий: обработка изображений

Вот практический пример инициализации векторов размером в контексте обработки изображений:

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

class Image {
private:
std::vector<std::vector<int>> pixels;
int width;
int height;

public:
Image(int w, int h) : width(w), height(h) {
pixels.resize(height, std::vector<int>(width, 0));
}

void addNoise(int intensity) {
std::srand(std::time(nullptr));
for (auto& row : pixels) {
for (int& pixel : row) {
pixel += std::rand() % intensity;
if (pixel > 255) pixel = 255;
}
}
}

void applyBlur() {
std::vector<std::vector<int>> blurred(height, std::vector<int>(width, 0));
for (int y = 1; y < height - 1; ++y) {
for (int x = 1; x < width - 1; ++x) {
int sum = 0;
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
sum += pixels[y + dy][x + dx];
}
}
blurred[y][x] = sum / 9;
}
}
pixels = std::move(blurred);
}

void print() {
for (const auto& row : pixels) {
for (int pixel : row) {
std::cout << pixel << " ";
}
std::cout << std::endl;
}
}
};

int main() {
Image img(5, 5);
std::cout << "Original Image:" << std::endl;
img.print();

img.addNoise(50);
std::cout << "\nImage with Noise:" << std::endl;
img.print();

img.applyBlur();
std::cout << "\nBlurred Image:" << std::endl;
img.print();

return 0;
}

Здесь демонстрируется инициализация векторов размером в таких задачах обработки изображений, как добавление шума и применение фильтров размытия.

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

При работе с большими векторами способ инициализации сказывается на производительности. Сравним эти способы:

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

const int SIZE = 10000000;

void benchmarkInitialization(const std::string& method, void (*initFunc)()) {
auto start = std::chrono::high_resolution_clock::now();
initFunc();
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << method << " took " << diff.count() << " seconds" << std::endl;
}

void initWithSize() {
std::vector<int> vec(SIZE);
}

void initWithResize() {
std::vector<int> vec;
vec.resize(SIZE);
}

void initWithReserveAndPushBack() {
std::vector<int> vec;
vec.reserve(SIZE);
for (int i = 0; i < SIZE; ++i) {
vec.push_back(0);
}
}

int main() {
benchmarkInitialization("Initialize with size", initWithSize);
benchmarkInitialization("Initialize with resize", initWithResize);
benchmarkInitialization("Initialize with reserve and push_back", initWithReserveAndPushBack);

return 0;
}

В этом тесте сравнивается инициализация вектора заданным размером с использованием resize(), reserve() и push_back(). Запустите тест у себя в системе и сравните, какой способ эффективнее.

Инициализация векторов пользовательских типов

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

#include <iostream>
#include <vector>
#include <string>

class Person {
public:
std::string name;
int age;

Person() : name("Unknown"), age(0) {}
Person(const std::string& n, int a) : name(n), age(a) {}
};

int main() {
std::vector<Person> people(3); // Создаются три «Persons» со значениями по умолчанию

for (const auto& person : people) {
std::cout << person.name << ": " << person.age << std::endl;
}

std::vector<Person> team(2, Person("John Doe", 30)); // Два «Persons» с конкретными значениями

for (const auto& person : team) {
std::cout << person.name << ": " << person.age << std::endl;
}

return 0;
}

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

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

  1. Путаница между size() и capacity(). Не забывайте, что в первом возвращается количество элементов, а во втором  —  выделенное пространство. Для изменения количества элементов используйте resize(), для выделения пространства  —  reserve().
  2. Лишние перераспределения. Во избежание их, зная конечный размер вектора, инициализируйте его этим размером или используйте reserve().
  3. Отсутствие инициализации элементов. При увеличении размера вектора новые элементы инициализируются значением, а в случае с неклассовыми типами  —  нулем.
  4. Неэффективная вставка. Избегайте push_back() в цикле, если известен конечный размер. Инициализируйте вектор корректным размером и задавайте значения оператором индексирования [].
  5. Игнорирование возвращаемого значения push_back. Вставленный элемент не возвращается с помощью push_back(). Чтобы изменить элемент после вставки, используйте back() или оператор индексирования.

Заключение

В C++ векторы инициализируются размером по-разному, в зависимости от конкретных сценариев.

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

Выбирая способ инициализации, учитывайте конкретные требования своих задач. Работаете вы с известным, фиксированным размером? Нужно ли в целях производительности заранее выделять память? Имеются ли пользовательские типы, которым требуется особая инициализация?

Ответив на эти вопросы, вы выберете оптимальный способ.

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

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


Перевод статьи ryan: C++ Initialize Vector with Size: How to Guide

Предыдущая статьяЛоги контейнеров Kubernetes: реализация и управление
Следующая статьяПочему крупные проекты отказываются от TypeScript?