Базовый вывод вектора циклом for с диапазоном

Вывести вектор на C++ проще всего циклом for с диапазоном. Это чистый, удобный для восприятия, рабочий способ для векторов любого выводимого типа:

#include <iostream>
#include <vector>

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

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

return 0;
}

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

Функцией const auto& не создается лишних копий элементов. Для больших объектов  —  в самый раз.

Итераторы для большего контроля

Больший контроль за процессом вывода дается итераторами:

#include <iostream>
#include <vector>

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

for (auto it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it;
if (std::next(it) != numbers.end()) {
std::cout << ", ";
}
}
std::cout << std::endl;

return 0;
}

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

С итераторами доступно сложное форматирование, например добавление запятых между элементами, но не после последнего.

Использование std::copy вместе с std::ostream_iterator

Для более функционального подхода к программированию сочетаются std::copy и std::ostream_iterator:

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

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

std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;

return 0;
}

Это лаконичный и элегантный способ, нюанс не для начинающих.

Вывод векторов пользовательских объектов

Работая с векторами пользовательских объектов, необходимо перегрузить оператор <<:

#include <iostream>
#include <vector>

struct Point {
int x, y;

friend std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << "(" << p.x << ", " << p.y << ")";
}
};

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

for (const auto& point : points) {
std::cout << point << " ";
}
std::cout << std::endl;

return 0;
}

В этом коде выводится: (1, 2) (3, 4) (5, 6).

С перегруженным оператором << пользовательские объекты выводятся точно так же, как встроенные типы.

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

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

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

template<typename T>
void printVector(const std::vector<T>& vec, const std::string& separator = " ") {
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i];
if (i < vec.size() - 1) {
std::cout << separator;
}
}
std::cout << std::endl;
}

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<std::string> words = {"Hello", "World", "C++"};

printVector(numbers); // Выводится: «1 2 3 4 5»
printVector(words, ", "); // Выводится: «Hello, World, C++»

return 0;
}

Этой функцией выводятся векторы разных типов с пользовательскими разделителями.

Реальный сценарий: отчет об анализе данных

Рассмотрим практическое применение вывода вектора:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iomanip>

struct SalesData {
std::string product;
double revenue;

friend std::ostream& operator<<(std::ostream& os, const SalesData& data) {
return os << data.product << ": $" << std::fixed << std::setprecision(2) << data.revenue;
}
};

void printSalesReport(const std::vector<SalesData>& sales) {
std::cout << "Sales Report:\n";
for (const auto& sale : sales) {
std::cout << sale << std::endl;
}

double totalRevenue = std::accumulate(sales.begin(), sales.end(), 0.0,
[](double sum, const SalesData& data) { return sum + data.revenue; });

std::cout << "\nTotal Revenue: $" << std::fixed << std::setprecision(2) << totalRevenue << std::endl;

auto maxSale = std::max_element(sales.begin(), sales.end(),
[](const SalesData& a, const SalesData& b) { return a.revenue < b.revenue; });

std::cout << "Best Selling Product: " << maxSale->product << std::endl;
}

int main() {
std::vector<SalesData> sales = {
{"Widget A", 1000.50},
{"Gadget B", 750.75},
{"Gizmo C", 1250.25}
};

printSalesReport(sales);

return 0;
}

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

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

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

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iomanip>

struct SalesData {
std::string product;
double revenue;

friend std::ostream& operator<<(std::ostream& os, const SalesData& data) {
return os << data.product << ": $" << std::fixed << std::setprecision(2) << data.revenue;
}
};

void printSalesReport(const std::vector<SalesData>& sales) {
std::cout << "Sales Report:\n";
for (const auto& sale : sales) {
std::cout << sale << std::endl;
}

double totalRevenue = std::accumulate(sales.begin(), sales.end(), 0.0,
[](double sum, const SalesData& data) { return sum + data.revenue; });

std::cout << "\nTotal Revenue: $" << std::fixed << std::setprecision(2) << totalRevenue << std::endl;

auto maxSale = std::max_element(sales.begin(), sales.end(),
[](const SalesData& a, const SalesData& b) { return a.revenue < b.revenue; });

std::cout << "Best Selling Product: " << maxSale->product << std::endl;
}

int main() {
std::vector<SalesData> sales = {
{"Widget A", 1000.50},
{"Gadget B", 750.75},
{"Gizmo C", 1250.25}
};

printSalesReport(sales);

return 0;
}

По этому тесту производительности, эффективность цикла for с диапазоном и итератора обычно одинаковая, а сочетание std::copy и std::ostream_iterator чуть медленнее  —  из-за дополнительной абстракции.

Пустые векторы

При выводе векторов важно корректно обрабатывать случай пустых векторов:

#include <iostream>
#include <vector>

template<typename T>
void printVectorSafely(const std::vector<T>& vec) {
if (vec.empty()) {
std::cout << "The vector is empty." << std::endl;
return;
}

for (const auto& elem : vec) {
std::cout << elem << " ";
}
std::cout << std::endl;
}

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

printVectorSafely(numbers); // Выводится: «1 2 3»
printVectorSafely(emptyVec); // Выводится: «Вектор пуст»

return 0;
}

При работе с пустыми векторами этим подходом предотвращается неожиданное поведение.

Вывод многомерных векторов

С многомерными векторами работают вложенными циклами:

#include <iostream>
#include <vector>

void print2DVector(const std::vector<std::vector<int>>& vec) {
for (const auto& row : vec) {
for (const auto& elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
}

int main() {
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

print2DVector(matrix);

return 0;
}

Этой функцией выводится двумерный вектор в виде матрицы.

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

  1. Невключение необходимых заголовков. При работе с векторами и выводом всегда включайте <vector> и <iostream>.
  2. Не обрабатываются пустые векторы. До вывода содержимого вектора всегда проверяйте, не пуст ли он.
  3. Неэффективная конкатенация строк. Чтобы повысить производительность, при построении строкового представления вектора выбирайте std::ostringstream, а не повторную конкатенацию строк.
  4. Игнорирование точности чисел с плавающей точкой. При выводе векторов чисел с плавающей точкой задавайте точность:
std::cout << std::fixed << std::setprecision(2);

5. Не учитывается потокобезопасность. Выводя векторы в многопоточной среде, помните о потенциальных состояниях гонки при записи в std::cout.

Заключение

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

В сценариях посложнее элегантные решения находятся с помощью std::copy и std::ostream_iterator.

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

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

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

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


Перевод статьи ryan: How to Print Vectors in C++

Предыдущая статьяРазработка отказоустойчивых микросервисов с шаблонами «Повтор» и «Выключатель»
Следующая статья20 уникальных сценариев использования GPT-4o