我有std::set
个共享指针到某种类型的对象(int
这里只是例如)。我需要的是插入从原始指针构造的共享ptr。但是当我试图擦除一些set元素时(我只有一个原始指针),我应该构造shared_ptr
并将其传递给erase方法(在我看来它是真的因为{{1比较运算符比较它们内部的原始指针。
代码段,导致SIGABRT:
shared_ptr
答案 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)
使用所拥有的原始底层指针构造一个新的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
析构函数不知道底层内存的命运已经被删除 sp1 调用内存再次封锁了内存。 Double delete disaster此时正好发生。
这就是为什么要么将所有内容保留为shared_ptr
来创建内存,要么为指针类型编写自定义比较器。