У меня есть вектор, содержащий 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, так как это атрибут класса.
Не используйте new
для создания вектора. Вектор уже управляет массивом, выделенным в куче, внутри. Просто создайте второй вектор, вставьте все, что вам нужно вставить, а затем переместите новый в старый.
Честно говоря, вы переусердствуете с этим. Как уже упоминалось, вектор имеет семантику перемещения. Ваша попытка, во всяком случае, будет иметь либо очень минимальные, либо даже пагубные последствия для оптимизации.
Кроме того, вы не можете использовать vector::operator[]
для назначения несуществующим объектам, которые просто предварительно распределены с помощью vector::reserve()
. Вам нужно использовать vector::resize()
(или конструктор vector
) вместо того, чтобы фактически создавать объекты, прежде чем вы сможете их назначить. В противном случае используйте vector::push_back()
или vector::emplace_back()
вместо vector::operator[]
.
Почти никогда нет необходимости динамически распределять 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 Yeb02 В том-то и дело, что вы перемещаете его в исходную переменную-член класса, прежде чем он будет уничтожен. Так что нет, вам не нужно new
там.
std::vector
поддерживает семантику перемещения. Смотрите конструктор 8 здесь.