从std :: vector中删除原始指针

Jab*_*cky 20 c++ stdvector

我有以下模式:

  1. 我有一个std::vector包含对象的原始指针(我知道原始指针是"邪恶的",但它是需要维护的遗留软件).
  2. 现在对于向量中的每个元素,我需要做一个测试,如果测试是肯定的,用指针做一些事情,删除它,然后从向量中删除它:

伪代码:

for each pointer in vector
{
  if (SomeTest(pointer))
  {
     DoSomething(pointer)
     delete pointer
     remove pointer from vector
  }
}
Run Code Online (Sandbox Code Playgroud)

我无法想出一些干净利落的代码.

这个链接提供了不同的方法,但它们看起来或多或少都很麻烦.

我现在使用的繁琐解决方案:

for(auto &  p : v)
{
   if (SomeTest(p))
   {
       DoSomething(p);
       delete p;
       p = nullptr;
   }
}

v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
Run Code Online (Sandbox Code Playgroud)

YSC*_*YSC 28

通常答案是:了解你<algorithm>的(并且是对我自己的好提醒);)

std::partition是你在找什么:std::partition(begin, end, p)"动作"范围内的元素[ begin,end),这符合谓词p的范围的结束; 然后,您可以将它们视为批处理.

auto const to_be_removed = std::partition(begin(v), end(v), [](auto p){ /* predicate */ });
std::for_each(to_be_removed, end(v), [](auto p) {
    /* crunch */
    delete p;
});
v.erase(to_be_removed, end(v));
Run Code Online (Sandbox Code Playgroud)

完整的计划

#include <iostream>
#include <algorithm>
#include <vector>

int main()
{
    std::vector v = { new int{0}, new int{1}, new int{2} };

    // let's delete all even values
    auto const to_be_removed = std::partition(begin(v), end(v), [](auto p){ return *p % 2 != 0; });
    std::for_each(to_be_removed, end(v), [](auto p) {
        std::cout << "Deleting value " << *p << "...\n";
        delete p;
    });
    v.erase(to_be_removed, end(v));
}
Run Code Online (Sandbox Code Playgroud)

现场演示

走得更远

这种实现有两个主要缺点:来自向量的顺序不稳定(1),它可以被考虑到可重用函数(2)中.

  • (1)通过解决std::stable_partition.
  • (2)并不难:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
{
    auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
    std::for_each(to_be_removed, end, [d](auto p) { d(p) ; delete p; });
    return to_be_removed;
}

template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
{
    using std::begin, std::end;
    return c.erase(delete_if(begin(c), end(c), p, d), end(c));
}
Run Code Online (Sandbox Code Playgroud)

用法:

delete_if(v, SomeTest, DoSomething);
Run Code Online (Sandbox Code Playgroud)

现场演示


Gal*_*lik 13

您可以使用std::remove_if我不知道为什么您链接的文章std::remove_if 删除指针之前使用,因为这不起作用.您必须在删除之前删除指针:

std::vector<int*> v;

v.erase(std::remove_if(std::begin(v), std::end(v), [](int* p){

    // do your test and do not remove on failure
    if(!SomeTest(p))
        return false; // keep this one

    DoSomething(p);

    // Now we remove but be sure to delete here, before the
    // element is moved (and therefore invalidated)

    delete p;

    return true; // signal for removal

}), std::end(v));
Run Code Online (Sandbox Code Playgroud)

注意:为什么这是安全的.

删除指针不会修改指针本身,而是修改指向的对象.这意味着没有使用这种方法修改元素.

在该标准C++17 28.6.8 5保证了谓词将只是一次为每个元素调用.


Use*_*ess 6

最简单的解决方案 - 从链接文章开始 - 是采用该erase_if功能

template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
{
    c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
}
Run Code Online (Sandbox Code Playgroud)

然后用它来调用它

erase_if(v, [](T *pointer)
         {
           if (SomeTest(pointer))
           {
              DoSomething(pointer);
              delete pointer;
              return true; //remove pointer from vector
           }
           return false;
         });
Run Code Online (Sandbox Code Playgroud)

如果要将SomeTest/DoSomething部分与delete部分分开,您显然可以将谓词拆分为两个:

template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
{
    auto e = std::remove_if(std::begin(c), std::end(c),
        [&p](Container::value_type *pointer)
        {
          if (p(pointer)) {
            delete pointer;
            return true;
          }
          return false;
        });
    c.erase(e, std::end(c));
}
Run Code Online (Sandbox Code Playgroud)

既然你没有说过为什么你不喜欢erase_if你自己联系,我猜不出这是否有同样的问题.