std :: set :: erase(const key_type& key)如何导致段错误?

时间:2013-02-06 18:16:45

标签: c++ pointers stl segmentation-fault set

我正在跟踪一个错误,我遇到了非常奇怪的行为。我有一组指针,当我逐个擦除它们时,第一个擦除,但擦除另一个会给我带来段错误。 我用

   size_type erase( const key_type& key );

所以它不能是迭代器的东西。我的debuger告诉我在callstack中:

0 - std::less<cSubscriber *>::operator() //cSubscriber is an abstract base class and I have a set of cSubscriber *

1 - std::_Rb_tree<cSubscriber*, cSubscriber*, std::_Identity<cSubscriber*>, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::equal_range

2 -  std::_Rb_tree<cSubscriber*, cSubscriber*, std::_Identity<cSubscriber*>, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::erase

3 - std::set<cSubscriber*, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::erase

4 - cEventSystem::unsubscribe //my function, it is as follows in the class which has the set as its member

cEventSystem::unsubscribe(cSubscriber * ptr)
{
   set.erase(ptr);
}

在基本的cSubscriber抽象类中有虚析构函数:

  virtual ~cSubscriber()
  {
      eventSystem.unsubscribe(this);
  }

有什么想法吗?我不知道它怎么会导致段错误,当没有这样的元素时,擦除应该只返回0。或者在尝试从空容器中擦除某些东西时可能会崩溃? (在添加3个不同的指针后,我有另一个错误,集合的大小只有2,但这是另一个故事)。

1 个答案:

答案 0 :(得分:4)

如果您将无效地址传递给std::set<SOMETHING*>::erase(),则在尝试将传递的值与容器中的内容进行比较时会出现段错误。

例如:

struct IntPtrComparer {
    bool operator()(int* a, int* b) const {
        return *a < *b;
    }
};

std::set<int*,IntPtrComparer> a;
a.insert(new int);
a.erase(NULL);

更新

基于评论

由于您没有重新定义默认比较器,并且默认比较器不会取消引用您的指针,因此唯一的方法是std::set已损坏。

在内部,std::set实现为二叉树。这意味着它在查找值和擦除值方面有很多指针。如果std::set已损坏,则其中一些指针将指向无效的内存地址。此无效内存地址将用于将比较值的引用(指针cSubscriber* & const的引用)传递给std::lessstd::less接收对指针的引用,并将取消引用引用以获取指针值。

这样,std::set内的无效内存只显示在std::less,因为std::set实际上没有触及无效内存,它给我们的无效内存地址可怜的家伙std::less打开它并在其脸上得到 segfault

我的观点是,如果你创建一个使用副本而不是引用的比较器,当它试图复制指针值以给比较器时,损坏将显示在std::set内。