C++ <vector> функция reserve

класс контейнера C++ <vector>


reserveиспользуется в vector дляПредварительного выделения памяти— важная функция, которая может значительно повысить производительность добавления элементов.

reserveявляется функцией-членом класса контейнера, используемой длярезервирования места для хранения как минимум n элементовЭто не изменяет размер контейнера, но предварительно выделяет достаточно памяти, чтобы избежать частого перераспределения при последующем добавлении элементов.

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

Определения слов: reserveозначает "reserve", то есть предварительное резервирование достаточного пространства.


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

reserve— это функция-член класса контейнера, и необходимо указать количество элементов для резервирования.

Формат синтаксиса

void reserve(size_type n);

Описание параметра

  • Параметр: n
    • Тип:size_type(беззнаковый целочисленный тип, обычноsize_t)
    • Описание: минимальное количество элементов, которое вы хотите зарезервировать. Контейнер выделит как минимум достаточно памяти для хранения n элементов.

Описание функции

  • Возвращаемое значение: void(Нет возвращаемого значения).
  • ДействиеКонтейнер перераспределит память так, чтобы его вместимость была не меньше n. Контейнераsize()не изменится и останется равным исходному количеству элементов.
  • ПримечаниеЕсли n меньше текущей вместимости,reserveон ничего не сделает. Если n больше текущей вместимости, память будет перераспределена.

Пример

Давайте тщательно освоим [это] на серии примеров.reserveИспользование.

Пример 1: базовое использование — предварительное выделение памяти

Пример

#include <iostream>
#include <vector>

int main() {
    // Создать пустой вектор
    std::vector<int> numbers;

    std::cout << "Начальное состояние - размер: " << numbers.size()
              << ", вместимость: " << numbers.capacity() << std::endl;

    // Зарезервировать место для 100 элементов
    numbers.reserve(100);

    std::cout << "После reserve(100) — размер: " << numbers.size()
              << ", вместимость: " << numbers.capacity() << std::endl;

    // Добавить элементы
    for(int i = 0; i < 10; ++i) {
        numbers.push_back(i);
    }

    std::cout << "После добавления 10 элементов — размер: " << numbers.size()
              << ", вместимость: " << numbers.capacity() << std::endl;

    return 0;
}

Ожидаемый вывод:

初始状态 - size: 0, capacity: 0
reserve(100) 后 - size: 0, capacity: 100
添加 10 个元素后 - size: 10, capacity: 100

Анализ кода:

  1. reserve(100)Было предварительно выделено пространство памяти, способное вместить 100 элементов.
  2. size()по-прежнему равно 0, потому что элементы ещё не были добавлены.
  3. После добавления 10 элементов ёмкость по-прежнему равна 100, и перераспределения памяти не произошло.

Пример 2: Использование reserve для оптимизации добавления больших объёмов данных

В сценариях, где необходимо добавить большое количество элементов, заблаговременное использование [reserve]reserveможет значительно повысить производительность.

Пример

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

int main() {
    const int N = 100000;

    // Без использования reserve
    std::vector<int> v1;
    auto start1 = std::chrono::high_resolution_clock::now();
    for(int i = 0; i < N; ++i) {
        v1.push_back(i);
    }
    auto end1 = std::chrono::high_resolution_clock::now();
    auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(end1 - start1);

    // С использованием reserve
    std::vector<int> v2;
    v2.reserve(N);
    auto start2 = std::chrono::high_resolution_clock::now();
    for(int i = 0; i < N; ++i) {
        v2.push_back(i);
    }
    auto end2 = std::chrono::high_resolution_clock::now();
    auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(end2 - start2);

    std::cout << "Затраченное время без reserve: " << duration1.count() << " микросекунд" << std::endl;
    std::cout << "Время выполнения с reserve: " << duration2.count() << " микросекунд" << std::endl;
    std::cout << "Прирост производительности: " << (double)duration1.count() / duration2.count() << "x" << std::endl;

    return 0;
}

Анализ кода:

  • При добавлении 100,000 элементов, использование [reserve] заранееreserveможет значительно сократить количество перераспределений памяти.
  • Это может обеспечить заметное повышение производительности при обработке больших объёмов данных.

Пример 3: Правильный способ использования reserve

Чтобы полностью использоватьreserveвам следует заранее оценить требуемую ёмкость.

Пример

#include <iostream>
#include <vector>

int main() {
    // Предположим, мы хотим хранить данные для 1000 пользователей
    struct User {
        std::string name;
        int age;
    };

    std::vector<User> users;
    users.reserve(1000); // предварительное выделение памяти

    // Имитация загрузки данных пользователя
    for(int i = 0; i < 1000; ++i) {
        User u;
        u.name = "Пользователь" + std::to_string(i);
        u.age = 20 + (i % 50);
        users.push_back(u);
    }

    std::cout << "Количество пользователей: " << users.size() << std::endl;
    std::cout << "Вместимость: " << users.capacity() << std::endl;

    return 0;
}

Анализ кода:

  • Для загрузки данных известного размера предварительное использование [reserve]reserveявляется лучшей практикой.
  • Это позволяет избежать многократного перераспределения памяти и повышает эффективность.

Пример 4: Как избежать ненужного reserve

reserveЕго следует использовать в соответствии с реальными потребностями; избыточное выделение приведёт к нерациональному расходу памяти.

Пример

#include <iostream>
<vector>

int main() {
    // Неправильный подход: избыточное предварительное выделение
    std::vector<int> v1;
    v1.reserve(1000000); // Нужно всего 10 элементов, но мы предварительно выделили место для 1 миллиона!

    for(int i = 0; i < 10; ++i) {
        v1.push_back(i);
    }

    std::cout << "Нужно всего 10 элементов - размер: " << v1.size()
              << ", ёмкость: " << v1.capacity() << std::endl;

    // Правильный подход: предварительно выделять по мере необходимости
    std::vector<int> v2;
    v2.reserve(10);

    for(int i = 0; i < 10; ++i) {
        v2.push_back(i);
    }

    std::cout << Нужно 10 элементов - размер: << v2.size()
              << ", ёмкость: " << v2.capacity() << std::endl;

    return 0;
}

Анализ кода:

  • Чрезмерное предварительное выделение расходует память впустую.
  • следует задавать исходя из фактического количества необходимых элементов.reserveпараметра.

Пример 5: reserve и проверка capacity

Можно проверить, достаточно ли capacity, перед добавлением элементов.

Пример

#include <iostream>
#include <vector>

void addElements(std::vector<int>& v, int count) {
    // Если текущей ёмкости недостаточно, зарезервируйте достаточно места
    if(v.capacity() < v.size() + count) {
        v.reserve(v.size() + count);
    }

    // Добавить элементы
    for(int i = 0; i < count; ++i) {
        v.push_back(v.size());
    }
}

int main() {
    std::vector<int> data;

    std::cout << "начальный - размер: " << data.size()
              << ", ёмкость: " << data.capacity() << std::endl;

    addElements(data, 50);
    std::cout << После добавления 50 - размер: << data.size()
              << ", ёмкость: " << data.capacity() << std::endl;

    addElements(data, 50);
    std::cout << После добавления ещё 50 - размер: << data.size()
              << ", ёмкость: " << data.capacity() << std::endl;

    return 0;
}

Анализ кода:

  • Вы можете динамически проверять и резервировать ёмкость внутри функции.
  • Это гарантирует, что при добавлении элементов не происходит перераспределения памяти.

класс контейнера C++ <vector>

Другие расширения