std :: set of shared_ptr的擦除导致SIGABRT

时间:2018-02-08 11:42:07

标签: c++ c++11 pointers shared-ptr stdset

我有std::set个共享指针到某种类型的对象(int这里只是例如)。我需要的是插入从原始指针构造的共享ptr。但是当我试图擦除一些set元素时(我只有一个原始指针),我应该构造shared_ptr并将其传递给erase方法(在我看来它是真的因为{{1比较运算符比较它们内部的原始指针。

代码段,导致SIGABRT:

shared_ptr

2 个答案:

答案 0 :(得分:4)

这不行:

sett.erase( shared_ptr<int>( rp ) );

这里,rp是一个指针,所以你构造一个匿名的临时shared_ptr,然后删除它指向的值和内存,然后你的匿名临时删除它。

您不能构造指向同一对象的两个不同的shared_ptr。如果你需要这些内容,你可以考虑使用enable_shared_from_this。或者更好的是,通过实现允许与原始指针进行比较的shared_ptr的比较函数,从容器中擦除而根本不构造std::set。有关详情,请参阅:What are transparent comparators?

答案 1 :(得分:3)

引自cppreference

  

使用所拥有的原始底层指针构造一个新的shared_ptr   另一个shared_ptr导致未定义的行为。

当你这样做时:

sett.emplace( rp );

由于implicit type conversion创建了shared_ptr,并且rp指定了内存位置的所有权。让我们称之为 sp1(1),其中(1)表示重新计数

当你这样称呼时: sett.erase( std::shared_ptr<int>( rp ) );

发生以下一系列事件:

  • shared_ptr拥有rp指向的内存。 shared_ptr认为内存的重新计数为1. sp2(1)
  • erase被调用。这会调用default comparator。这可能会导致删除第一个shared_ptr sp1

  • sp1 调用析构函数,引用计数将变为0.由于ref-count为0,因此释放内存。

  • 当erase返回 sp2 时(1)调用析构函数(因为这是为函数调用创建的临时函数)。对于由 sp2

  • 管理的内存,引用计数变为0
  • 析构函数不知道底层内存的命运已经被删除 sp1 调用内存再次封锁了内存。 Double delete disaster此时正好发生。

这就是为什么要么将所有内容保留为shared_ptr来创建内存,要么为指针类型编写自定义比较器。