我正在跟踪一个错误,我遇到了非常奇怪的行为。我有一组指针,当我逐个擦除它们时,第一个擦除,但擦除另一个会给我带来段错误。 我用
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,但这是另一个故事)。
答案 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::less
。 std::less
接收对指针的引用,并将取消引用引用以获取指针值。
这样,std::set
内的无效内存只显示在std::less
,因为std::set
实际上没有触及无效内存,它给我们的无效内存地址可怜的家伙std::less
打开它并在其脸上得到 segfault 。
我的观点是,如果你创建一个使用副本而不是引用的比较器,当它试图复制指针值以给比较器时,损坏将显示在std::set
内。