如何从 std::vector 中删除数据

std::vector 由于内存的连续性带来了访问的高效率,但是,如果我们想要从 std::vector 中删除掉某些行的数据,应该怎么操作呢

首先,如果这个动作是一个高频频繁的动作,那从一开始就不应该使用 std::vector,而是应该用 std::list,但是,如果这个动作并不是很高频,且 std::vector 的连续高效性对我们很重要的情况下,那还是应该找方法去解决这个问题

一个思路是 std::vector 的 erase 方法,但是这个方法有两个坑:一是迭代器会乱动,会指向被删除的元素的下一个元素,也就有可能指向 end(),这个特性会导致如果是在 for 循环中遍历 iterator 去根据条件删除数据的时候,iterator 会很不可预期,一不小心就写出一个 coredump 来,另外一个坑,就是由于 std::vector 的内存连续性,那么每次 erase 的时候,他为了保证语义和满足约束,都需要把后续的元素逐个往前移动一位,这个在删除一个元素的时候还好,但是如果删除多个元素,那就是性能的瓶颈了,但是这个坑其实怪不得 erase,而是 std::vector 的本身特性约束导致的

那么考虑 c++11 的 std::move 语义呢,如果我们新建一个全新的 vector,然后把需要保留的元素使用 std::move 移动过去,是不是会好很多,可以避免了反复的内存拷贝和无谓的浪费,但是这里还有一个点,由于这个 vector 是传入的,且需要原位传出,那么就需要在最后的时候,把两个 vector 整体的 swap 一下,这个 swap 按理说也是可以用上 move 语义的,但是不确定的点在于,这样来回两次的 move,会不会出问题,因为按理来说,原来那个 vector 的元素被 move 之后,那个位置就应该不再访问,而是等着回收了

再继续查资料,查到一个 remove_if 的方式,看到这里, https://stackoverflow.com/ques…

v.erase(std::remove_if(
    v.begin(), v.end(),
    [](const int& x) { 
        return x > 10; // put your condition here
    }), v.end());

在这里,讨论了其实现方式, https://zh.cppreference.com/w/…

可能的实现
版本一
template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!(*i == value))
*first++ = std::move(*i);
return first;
}
版本二
template
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
first = std::find_if(first, last, p);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!p(*i))
*first++ = std::move(*i);
return first;
}

可以看到,其也是通过 move 语义,并且,是保持了原位容器的,这个应该是能满足需求的前提下保持一定的性能的

Leave a Reply

Your email address will not be published. Required fields are marked *