如何在C++中重新创建向量而不进行不必要的复制

huangapple go评论59阅读模式
英文:

How to recreate a vector without unnecessary copies C++

问题

以下是您要翻译的内容:

"我有一个包含 n 个复杂对象的向量。我想执行大量的擦除操作,使用 [.erase()](https://cplusplus.com/reference/vector/vector/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];  // Obj 的深度拷贝
      newId++;
   }
}

然后释放 oldV 的数组(.data()),并用 newVptr 替换内部指针。如何实现?

我不能只丢弃 oldV 并继续使用 newV,因为它是类的属性。"

英文:

I have a vector containing n complex objects. I want to perform a significant number of erasure, which would be extremely inefficient using .erase(). The way I thought I would do it is by creating an empty vector on the heap, filling it with copies of objects that are to be kept, and redirecting the original vector towards the new one, without a copy. And of course free the old .data().

Something like this:

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

then free oldV's array (.data()) and replace the internal pointer with newVptr. How ?

I cant just discard oldV and keep going with newV, as it is an attribute of a class.

答案1

得分: 3

没有必要动态分配std::vector,因为它们在内部动态分配。

如果逐个删除,那将是低效的,是的。但通常的做法是使用std::remove*算法之一将要保留的所有元素移到vector的前面,然后调用erase。

例如:

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

或在C++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相当于您的指针交换想法。

英文:

There's almost never a need to allocate a std::vector dynamically, as they allocate dynamically internally.

If you erase one-by-one, then that will be inefficient, yes. But the usual way to do this is using one of the std::remove* algorithms to move all the elements you want to keep to the front of the vector, and then call erase on the end.

For example:

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

Or in C++20:

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

If you really want to copy the kept elements instead you could do:

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

That std::move at the end would do the equivalent of your pointer swap idea.

huangapple
  • 本文由 发表于 2023年4月13日 23:17:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76007147.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定