Как пересоздать вектор без лишних копий C++

У меня есть вектор, содержащий n сложных объектов. Я хочу выполнить значительное количество стираний, что было бы крайне неэффективно при использовании .erase(). Я думал, что сделаю это, создав пустой вектор в куче, заполнив его копиями объектов, которые должны быть сохранены, и перенаправив исходный вектор на новый без копии. И, конечно же, освободите старый .data().

Что-то вроде этого:

vector<Obj>* newVptr = new vector<Obj>;
newVptr->reserve(newSize);
int newId = 0;
for (int i = 0; i < oldSize; i++) {
   if (isKept(oldV[i])) {
      (*newVptr)[newId] = oldV[i];  // deep copy of Obj
      newId++;
   }
}

затем освободите массив oldV (.data()) и замените внутренний указатель на newVptr. Как ?

Я не могу просто отказаться от oldV и продолжить работу с newV, так как это атрибут класса.

std::vector поддерживает семантику перемещения. Смотрите конструктор 8 здесь.
πάντα ῥεῖ 13.04.2023 17:25

Не используйте new для создания вектора. Вектор уже управляет массивом, выделенным в куче, внутри. Просто создайте второй вектор, вставьте все, что вам нужно вставить, а затем переместите новый в старый.

guard3 13.04.2023 17:26

Честно говоря, вы переусердствуете с этим. Как уже упоминалось, вектор имеет семантику перемещения. Ваша попытка, во всяком случае, будет иметь либо очень минимальные, либо даже пагубные последствия для оптимизации.

PaulMcKenzie 13.04.2023 17:27

Кроме того, вы не можете использовать vector::operator[] для назначения несуществующим объектам, которые просто предварительно распределены с помощью vector::reserve(). Вам нужно использовать vector::resize() (или конструктор vector) вместо того, чтобы фактически создавать объекты, прежде чем вы сможете их назначить. В противном случае используйте vector::push_back() или vector::emplace_back() вместо vector::operator[].

Remy Lebeau 13.04.2023 20:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
91
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почти никогда нет необходимости динамически распределять std::vector, поскольку они динамически распределяются внутри.

Если стирать по одному, то это будет неэффективно, да. Но обычный способ сделать это — использовать один из алгоритмов std::remove*, чтобы переместить все элементы, которые вы хотите сохранить, в начало vector, а затем вызвать стирание в конце.

Например:

oldV.erase(std::remove_if (oldV.begin(), oldV.end(), std::not_fn(isKept)),
           oldV.end());

Или в С++ 20:

std::erase_if (oldV, std::not_fn(isKept));

Если вы действительно хотите скопировать сохраненные элементы, вы можете сделать:

std::vector<Obj> newV;
newV.reserve(oldV.size());
std::copy_if (oldV.begin(), oldV.end(), std::back_inserter(newV), isKept);
oldV = std::move(newV);

Этот std::move в конце будет эквивалентен вашей идее замены указателя.

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

Yeb02 13.04.2023 17:49

@Yeb02 Yeb02 В том-то и дело, что вы перемещаете его в исходную переменную-член класса, прежде чем он будет уничтожен. Так что нет, вам не нужно new там.

πάντα ῥεῖ 13.04.2023 18:12

Другие вопросы по теме