我有一个疑点需要澄清。我知道std::vector
在erase
和std::remove
之间具有不同的行为。其中,erase
可以从向量中物理删除元素,减小向量大小,而std::remove
只是移动元素而不改变容量。
这只是为了效率考虑吗?使用erase
时,std::vector
中的所有元素都会向前移动一个位置,导致大量的复制操作;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
函数对给定的容器进行组织以删除不需要的对象。容器的erase
函数实际上处理了容器需要执行的“删除”操作。这就是它们分开的原因。std::remove
并不会这样做。从返回位置开始的末尾元素将保留在未指定的状态下。 - Benjamin Lindleyremove
基本上是将要删除的元素与不会被删除的元素进行交换,因此返回到end()
的位置都是将要被删除的元素(并且仍处于有效状态),但您是正确的 - 不能保证它们“匹配”已经交换出去的元素。我稍微澄清一下。 - Zac Howlandstd::remove
允许使用移动赋值,这将导致范围[result,end())仅成为已移动元素。那些元素的状态取决于相关类。 - Dave S是的,这就是要点。请注意,erase
还被其他标准容器支持,其性能特征也不同(例如list::erase是O(1)),而std::remove
是与容器无关的,适用于任何类型的前向迭代器(因此也适用于裸数组等)。
有点像。例如,remove 等算法是在迭代器上操作的(迭代器是表示集合中元素的抽象),它们不一定知道它们正在操作哪种类型的集合 - 因此无法调用集合成员来执行实际的删除操作。
这很好,因为它允许算法通用地处理任何容器,也可以处理整个集合的子集范围。
另外,正如您所说,出于性能考虑,如果您只需要访问逻辑结束位置以将其传递给另一个算法,则可能不必实际删除(和销毁)元素。
标准库算法操作的对象是序列。序列由一对迭代器定义;第一个指向序列中的第一个元素,第二个指向序列末尾的下一个位置。这就是全部内容;算法不关心序列来自哪里。
标准库容器保存数据值,并提供一对迭代器,用于指定序列以供算法使用。它们还提供成员函数,可以通过利用容器的内部数据结构更有效地执行与算法相同的操作。
std::remove
模板函数可以做到OP说的那样。 - sasha.sochkastd::remove
是一种将集合中的元素标记为已删除的算法。 - Zac Howland