Склейка std::vector в C++: 3 лучших способа с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем склеивать векторы и почему это сложно?

Привет, коллега! Если ты когда-нибудь писал на C++ код, который обрабатывает списки данных, то наверняка сталкивался с задачей: «У меня есть два std::vector, как из них сделать один?» Этот вопрос — настоящий хит на Stack Overflow (тысячи просмотров!), и не зря. На первый взгляд кажется: ну что там сложного? vector1 + vector2, как в Python? А вот и нет. В C++ оператор + для векторов не определён. Приходится крутиться.

Сегодня мы разберём склейку двух std::vectors от и до. Я покажу тебе 3 проверенных способа, объясню, когда какой использовать, и предостерегу от граблей, на которые наступают даже опытные разработчики. Поехали!

Способ №1: insert() — классика жанра

Самый очевидный и часто используемый метод — метод insert(). Он позволяет вставить все элементы одного вектора в конец другого. Синтаксис простой и элегантный:

#include <iostream>
#include <vector>

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

    // Склеиваем: вставляем vec2 в конец vec1
    vec1.insert(vec1.end(), vec2.begin(), vec2.end());

    // Вывод: 1 2 3 4 5 6
    for (int v : vec1) std::cout << v << " ";
    return 0;
}

Как это работает: Мы говорим компилятору: «Начиная с позиции vec1.end() (то есть после последнего элемента первого вектора), скопируй всё, что лежит между vec2.begin() и vec2.end()». Вуаля — объединение векторов C++ выполнено.

Плюсы и минусы insert()

  • ✅ Плюсы: Простота, читаемость, работает с любыми типами.
  • ❌ Минусы: Если vec1 маленький, а vec2 — огромный, то при вставке может произойти несколько перераспределений памяти. Это снижает производительность при конкатенации векторов больших объёмов.

Совет: Если знаешь итоговый размер, заранее вызови vec1.reserve(vec1.size() + vec2.size()). Это избавит от лишних аллокаций.

Способ №2: std::merge() — для отсортированных данных

А что, если твои векторы уже отсортированы? Или тебе нужно не просто склеить, а получить единый отсортированный массив? Тут на сцену выходит алгоритм std::merge из заголовочного файла <algorithm>.

#include <iostream>
#include <vector>
#include <algorithm> // для std::merge

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

    // Зарезервируем место, чтобы не было лишних копирований
    result.reserve(vec1.size() + vec2.size());

    // Сливаем два отсортированных диапазона
    std::merge(vec1.begin(), vec1.end(),
               vec2.begin(), vec2.end(),
               std::back_inserter(result));

    // Вывод: 1 2 3 4 5 6
    for (int v : result) std::cout << v << " ";
    return 0;
}

Обрати внимание: std::merge требует, чтобы оба входных диапазона были отсортированы по возрастанию. Если это не так — результат будет непредсказуемым. Зато если данные упорядочены, этот метод — король производительности: сложность O(n + m).

Когда использовать std::merge?

  • Если ты объединяешь результаты двух SQL-запросов или логов, отсортированных по времени.
  • Когда нужен не просто concatenating two std::vectors, а именно сортированное слияние.
  • Если исходные векторы менять нельзя (мы создаём третий вектор result).

Способ №3: Move semantics — скорость для C++11 и новее

Допустим, тебе не нужны исходные векторы после склейки. Например, ты обработал данные и хочешь «переместить» элементы во второй вектор без копирования. Тут в игру вступает магия move-семантики, доступная начиная с C++11.

#include <iostream>
#include <vector>
#include <utility> // для std::move

int main() {
    std::vector<std::string> vec1 = {"Hello", "from"};
    std::vector<std::string> vec2 = {"C++", "World"};

    // Перемещаем элементы vec1 в конец vec2
    vec2.insert(vec2.end(),
                std::make_move_iterator(vec1.begin()),
                std::make_move_iterator(vec1.end()));

    // vec1 теперь в "valid but unspecified" состоянии (обычно пустой)
    // vec2 содержит: "C++ World Hello from"
    for (const auto& s : vec2) std::cout << s << " ";
    return 0;
}

В чём фишка? Вместо того чтобы копировать строки (а это выделение памяти, копирование символов, потом освобождение), мы просто «перекидываем» указатели на внутренние данные. Для тяжёлых объектов (строки, векторы, умные указатели) это даёт колоссальный прирост скорости.

Важно знать про move-семантику

  • После вызова std::make_move_iterator исходный вектор остаётся в корректном, но неопределённом состоянии. Его можно снова заполнить или уничтожить, но не стоит полагаться на его содержимое.
  • Этот метод идеален для склейки векторов C++ в высоконагруженных системах, где каждый микросекунда на счету.
  • Работает только с типами, которые поддерживают перемещение (а это практически все стандартные контейнеры и умные указатели).

Сравнение производительности: что выбрать?

Давай подведём небольшой итог. Представим, что у тебя есть два вектора по 10 000 элементов каждый. Что быстрее?

<

Блоги

Рекомендаций по книгам

Метод Скорость (условно) Когда использовать
insert() без reserve Средняя Быстрый прототип, маленькие данные
insert() с reserve