【问题标题】:Use std::remove_if on a std::vector filled with pointers在填充有指针的 std::vector 上使用 std::remove_if
【发布时间】:2016-03-15 11:33:18
【问题描述】:

我对@9​​87654321@ 函数有一点疑问。我的程序某处出现内存泄漏,我怀疑 erase 函数会以某种方式被破坏。

其实我的代码里有这个

std::vector<Object*> v; // Just the constructor to show you
v.erase(std::remove_if(begin(v), end(v), [Foo f](Object *o){
    return o->containsFoo(f);
}), end(v));

但经过一番研究,这是否比以前更好?

v.erase(std::remove_if(begin(v), end(v), [Foo f](Object *o){
   if(o->containsFoo(f)) {
     delete o;
     return true; 
   }
   return false;
}), end(v));

或者我应该使用其他东西吗?

【问题讨论】:

  • std::vector&lt;std::unique_ptr&lt;Object&gt;&gt;
  • @T.C.我应该使用 unique_ptr 而不是 Type* 指针吗?
  • @T.C.这不是评论——这就是答案。把它放在一个答案中,我可以投票,OP 可以接受它。
  • @Bl4ckb0ne 是的。 std::unique_ptr 将在其作用域的末尾自动删除指针,就像从容器中移除时一样,并且没有处理或运行时开销。
  • “我的程序某处有内存泄漏...” - Valgrind 没有告诉你在哪里?你应该证明你已经为解决你的问题做出了一些的努力!

标签: c++ pointers c++11 vector


【解决方案1】:

你真的应该使用智能指针而不是裸Object* - 要么

std::vector<std::unique_ptr<int>>

std::vector<std::shared_ptr<int>>

选择合适的。如果您使用裸 C 风格的指针,很容易错过关键的 delete(或两次 delete)。

尽管如此,很容易看出一种方法会泄漏,而另一种则不会:

#include <algorithm>
#include <vector>

int main(int argc, char **)
{
    std::vector<int*> v{ new int(1), new int(-1) };

    if (argc < 2) {
        // First version
        v.erase(std::remove_if(begin(v), end(v),
                               [](int *o){
                                   return *o < 0;
                               }),
                end(v));
    } else {
        // Second version
        v.erase(std::remove_if(begin(v), end(v),
                               [](int *o){
                                   if (*o < 0) {
                                       delete o;
                                       return true;
                                   }
                                   return false;
                               }),
                end(v));
    }

    // normal cleanup
    for (int *p: v)
        delete p;

}

我在没有参数的情况下运行它(调用第一个版本),然后使用参数(调用第二个版本)。看看会发生什么:

g++ -std=c++11 -g -Wall -Wextra 34191606.cpp -o 34191606

valgrind --leak-check=full ./34191606
==16894== HEAP SUMMARY:
==16894==     in use at exit: 72,708 bytes in 2 blocks
==16894==   total heap usage: 4 allocs, 2 frees, 72,728 bytes allocated
==16894== 
==16894== 4 bytes in 1 blocks are definitely lost in loss record 1 of 2
==16894==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16894==    by 0x400881: main (34191606.cpp:6)
==16894== 
==16894== LEAK SUMMARY:
==16894==    definitely lost: 4 bytes in 1 blocks
==16894==    indirectly lost: 0 bytes in 0 blocks
==16894==      possibly lost: 0 bytes in 0 blocks
==16894==    still reachable: 72,704 bytes in 1 blocks
==16894==         suppressed: 0 bytes in 0 blocks
==16894== Reachable blocks (those to which a pointer was found) are not shown.
==16894== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

valgrind --leak-check=full ./34191606 -
==16895== HEAP SUMMARY:
==16895==     in use at exit: 72,704 bytes in 1 blocks
==16895==   total heap usage: 4 allocs, 3 frees, 72,728 bytes allocated
==16895== 
==16895== LEAK SUMMARY:
==16895==    definitely lost: 0 bytes in 0 blocks
==16895==    indirectly lost: 0 bytes in 0 blocks
==16895==      possibly lost: 0 bytes in 0 blocks
==16895==    still reachable: 72,704 bytes in 1 blocks
==16895==         suppressed: 0 bytes in 0 blocks
==16895== Reachable blocks (those to which a pointer was found) are not shown.
==16895== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

请注意,在第一个版本中,您永远不会删除从向量中删除其指针的对象,并且报告为泄漏。在第二个版本中,没有内存泄漏。

【讨论】:

  • Ty,我切换到了 shared_ptr,但我的算法仍然遇到了一些问题。结果与没有指针的版本相比是不一样的。
  • 这听起来像是一个新问题;如果您可以将该问题提炼为MCVE,那么请ask it separately
【解决方案2】:

lambda 更好,但如果我是你(并且由于某种原因你不准备使用std::unique_ptr),我会这样做:

const auto predicate = [Foo f](Object *o){return !o->containsFoo(f);};
const auto new_end = std::partition(begin(v), end(v), predicate);
std::for_each(new_end, end(v), [](Object *o){delete o;});
v.erase(new_end,end(v));

换句话说:

  1. 使用 remove_if partition 将不需要的指针移动到末尾
  2. 使用for_each 删除所有不需要的对象
  3. 使用erase 删除(现在无效的)不需要的指针。

关键是,在移动事物时删除指针很麻烦。

【讨论】:

  • 如果你想走这条路,你需要partition,而不是remove_if
  • 编辑修复。并澄清为什么
  • 强调他确实需要使用智能指针,你得到我的支持
  • 我认为[](Object *o){delete o;} 可以拼写为std::default_delete&lt;Object&gt;()(实际上,这并没有更短,不是吗?但它不再是重新发明轮子)。
  • 哦。我不知道 std::default_delete。那是闪亮的。它不再是发明轮子,而是有了名字。此外,在实际代码中,我可能会将 foreach lambda 拆分为多行。
猜你喜欢
  • 2013-06-11
  • 2021-08-08
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多