Ответ
Основная проблема — множественные реаллокации, которые приводят к падению производительности.
Механизм работы std::vector:
- Вектор хранит элементы в непрерывном блоке памяти.
- У него есть два размера:
size()(количество элементов) иcapacity()(размер выделенного блока памяти). - При
push_back(), еслиsize() == capacity(), происходит реаллокация: выделяется новый, больший блок памяти (обычно в 1.5 или 2 раза больше), все существующие элементы копируются или перемещаются в него, старый блок освобождается.
Пример с проблемой:
std::vector<int> vec; // capacity = 0
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // Реаллокации произойдут при i = 0, 1, 2, 4, 8, 16, 32, 64...
}
// Каждая реаллокация — это O(N) операций копирования/перемещения.
Решение — метод reserve():
std::vector<int> vec;
vec.reserve(100); // Однократное выделение памяти под 100 элементов
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // Ни одной реаллокации. Вставка за O(1).
}
Дополнительные соображения:
- Использование
emplace_back()вместоpush_back()позволяет конструировать объекты прямо в памяти вектора, избегая лишних копирований. - Если конечный размер известен, можно использовать конструктор или
assign().// Альтернатива: инициализация из итераторов или списка std::vector<int> vec(100); // 100 элементов, инициализированных нулями // или std::vector<int> vec; vec.assign(100, 0); // 100 нулей
Ответ 18+ 🔞
А, слушай, вот это реально больная тема, которая у новичков постоянно вылезает. Представь себе, что ты пытаешься впихнуть диван в квартиру, а дверной проём — узкий. Ты его туда, блядь, засовываешь, потом понимаешь — не лезет. Вытаскиваешь, ломаешь стену, делаешь проём шире, затаскиваешь диван. Потом привозят кресло — и опять та же история! Это и есть множественные реаллокации, ёпта. Производительность на них просто накрывается медным тазом.
Как этот std::vector изнутри устроен, блядь:
- Он, хитрая жопа, хранит всё в одной куче памяти, подряд.
- У него есть два параметра:
size()— сколько элементов уже внутри лежит, иcapacity()— а сколько вообще влезет в тот кусок памяти, который он уже отгрыз у системы. - Вот ты делаешь
push_back(). Если места (capacity) ещё хватает — всё ок, пихаем. А если нет? Ёперный театр! Начинается реаллокация: он бежит, ищет новый, больший кусок памяти (обычно в 1.5 или 2 раза больше), потом тупо копирует или перемещает туда все старые элементы, а старый кусок выкидывает. И так каждый раз, когда упрётся.
Смотри, как это выглядит в коде, просто пиздец:
std::vector<int> vec; // capacity = 0, места нет нихуя
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // Реаллокации будут при i = 0, 1, 2, 4, 8, 16, 32, 64...
}
// Каждая такая операция — это O(N), то есть чем больше накопилось, тем дольше всё копировать. Ужас, да?
Решение? Да элементарно, Ватсон! Метод reserve():
std::vector<int> vec;
vec.reserve(100); // Всё, блядь! Одним махом говорим системе: "Дай мне памяти на сотню интов, не ссы!"
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // И тут уже нихуя не реаллоцируется! Вставка за O(1), красота.
}
Вот и вся магия. Сразу видно, кто в теме писал код, а кто нет. Доверия ебать ноль к тому, кто reserve не использует, когда размер известен.
Ещё пара советов, чтобы вообще охуенно было:
- Вместо
push_back()можноemplace_back()юзать. Он объект прямо на месте, в памяти вектора, конструирует, минуя лишние копирования. Умно, блядь. - Если уж совсем точно знаешь, что будет, можно сразу конструктором или
assign().// Вариант раз: сразу сто нулей std::vector<int> vec(100); // Вариант два: команда "сделай мне сто нулей" std::vector<int> vec; vec.assign(100, 0);
Короче, суть в чём: не заставляй вектор каждый раз переезжать на новую квартиру. Скажи ему сразу, сколько тебе нужно комнат, и живи спокойно. Волнение ебать отпадает сразу.