标准::删除和擦除矢量之间的区别
Difference between std::remove and erase for vector?
我有一个疑问,我想在脑海中澄清一下。我知道erase
和std::remove
之间的std::vector
行为不同,其中第一个从矢量中物理删除一个元素,减小大小,另一个只是移动一个元素,保持容量不变。
这只是出于效率原因吗?通过使用erase
,std::vector
中的所有元素将移动 1,从而导致大量副本; std::remove
只执行"逻辑"删除,并通过移动内容来保持矢量不变。如果物体很重,这种差异可能很重要,对吧?
这只是出于效率原因吗?通过使用擦除,std::vector 中的所有元素将移动 1,从而导致大量副本;std::remove 只执行"逻辑"删除,并通过移动内容来保持向量不变。如果物体很重,那么差异可能很重要,对吧?
使用这个成语的原因正是如此。在性能方面有好处,但在单次擦除的情况下则不然。重要的是您是否需要从矢量中删除多个元素。在这种情况下,std::remove
将只将每个未删除的元素复制到其最终位置一次,而vector::erase
方法会将所有元素从该位置多次移动到末尾。考虑:
std::vector<int> v{ 1, 2, 3, 4, 5 };
// remove all elements < 5
如果您逐个删除元素的向量,则会删除 1,从而导致剩余元素的副本被移动 (4(。然后,您将删除 2 并将所有剩余元素移动一 (3(...如果您看到模式,这是一种O(N^2)
算法。
在std::remove
的情况下,算法维护读写磁头,并迭代容器。对于前 4 个元素,将移动读取头并测试元素,但不复制任何元素。仅对于第五个元素,对象才会从最后一个位置复制到第一个位置,并且算法将以单个副本完成,并将迭代器返回到第二个位置。这是一种O(N)
算法。稍后与范围std::vector::erase
将导致所有剩余元素的破坏并调整容器的大小。
正如其他人所提到的,在标准库中,算法应用于迭代器,并且缺乏对正在迭代的序列的了解。这种设计比算法识别容器的其他方法更灵活,因为算法的单个实现可以与符合迭代器要求的任何序列一起使用。例如,考虑std::remove_copy_if
,即使没有容器也可以通过使用生成/接受序列的迭代器来使用它:
std::remove_copy_if(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::ostream_iterator<int>(std::cout, " "),
[](int x) { return !(x%2); } // is even
);
该单行代码将从标准输入中过滤掉所有偶数并将其转储到标准输出中,而无需将所有数字加载到容器中的内存中。这是拆分的优点,缺点是算法不能修改容器本身,只能修改迭代器引用的值。
std::remove
是来自STL的算法,与容器无关。它需要一些概念,确实如此,但它也被设计为也适用于大小是静态的 C 数组。
std::remove
只是返回一个新的end()
迭代器,指向最后一个未删除元素之后的迭代器(从返回值到end()
的项目数将与要删除的项目数匹配,但不能保证它们的值与您删除的值相同 - 它们处于有效但未指定的状态(。 这样做是为了它可以适用于多种容器类型(基本上是ForwardIterator
可以循环访问的任何容器类型(。
std::vector::erase
实际上在调整大小后设置了新的end()
迭代器。 这是因为vector
的方法实际上知道如何调整它的迭代器(std::list::erase
、std::deque::erase
等也可以这样做(。
remove
组织给定的容器以删除不需要的对象。 容器的擦除功能实际上以容器需要的方式处理"删除"。 这就是为什么它们是分开的。
我认为这与需要直接访问矢量本身才能调整其大小有关。 std::remove 只能访问迭代器,因此它无法告诉向量"嘿,你现在的元素更少了"。
参见 yves Baumes 的答案,了解为什么 std::remove 是这样设计的。
是的,这就是它的要点。请注意,erase
也受到其他标准容器的支持,其中其性能特征不同(例如 list::erase 是 O(1((,而 std::remove
与容器无关,并且适用于任何类型的前向迭代器(因此它也适用于例如裸数组(。
有点。 诸如删除之类的算法在迭代器(迭代器是表示集合中的元素的抽象(上工作,这些算法不一定知道它们正在操作哪种类型的集合 - 因此无法调用集合上的成员来执行实际删除。
这很好,因为它允许算法通常适用于任何容器以及作为整个集合子集的范围。
此外,正如您所说,为了性能 - 如果您只需要访问逻辑结束位置以传递给另一种算法,则可能没有必要实际删除(和销毁(元素。
标准库算法对序列进行操作。序列由一对迭代器定义;第一个指向序列中的第一个元素,第二个指向序列的末尾。就这样;算法不在乎序列来自哪里。
标准库容器保存数据值,并提供一对迭代器,用于指定供算法使用的序列。它们还提供成员函数,这些函数可以通过利用容器的内部数据结构更有效地执行与算法相同的操作。
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 在 const 函数中通过引用和指针返回之间的区别
- 我想知道长双倍和双倍之间的区别
- 这 4 个 lambda 表达式之间有什么区别?
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- (double) 和 double() 之间的区别
- & 和 * 之间的区别
- std::is_convertible 和 std::convertible_to 之间的区别(在实践中)?
- 析构函数和'delete'之间的区别
- 在 typedef 内部使用 const 关键字和在 typedef 外部使用 const 关键字之间有区别吗?
- 从预序遍历构造 bst 的 c++ 和 python 解决方案之间的区别
- vector.back() 和 vector[vector.size() - 1] 之间的区别?
- 空指针常量 (nullptr)、空指针值和空成员指针值之间有什么区别?
- 函数签名与调用的函数不匹配,常量字符[]和字符*之间的区别?
- OpenMP #pragma omp for v/s #pragma omp parallel for 之间的区别?
- S() 与 S{} 之间的区别?