首先要感谢下这个前辈:http://www.2cto.com/kf/201110/108902.html,是他的帖子给了我方向。
先上一段代码:
#include
#include
class Base
{
public:
std::vector vec;
int a;
};
int main()
{
Base base;
memset(&base, 0, sizeof(base));
base.vec.push_back(1);
base.vec.push_back(2);
base.vec.push_back(3);
base.vec.push_back(4);
std::vector::iterator iter;
for( iter = base.vec.begin(); iter != base.vec.end(); ++iter )
{
*iter = 10;
std::cout<<*iter<
不知道你能不能看出来这段代码是否有问题,有什么问题?
好吧,这就是今天要提的运行时崩溃错误“vector iterator incompatible”。
这个问题是一个朋友在弄他自己的服务器程序时遇见的,然后让我帮他解决。上面的代码是我将他的问题换了种比较简洁的方式来呈现。编译上述程序没有任何问题,但是
运行时总会在for( iter = base.vec.begin(); iter != base.vec.end(); ++iter ) 时崩掉。崩溃界面如下:
当时很疑惑,怎么会迭代器不匹配呢。明明就是同一个类型,用begin()赋值的时候也没错啊,调试看iter的数据也是正确的。然后就百度、google了一下。发现了文章最开
始的帖子,然后对照了他说的一些东西。
首先是类型不匹配,例如用int型的向量迭代器与char型的向量迭代器进行比对操作,其他编译器我不知道,我用的是VS2010,如果迭代器不匹配,编译根本通不过;
其次,如果是容器结构发生变化,比如erase某个iter之类的,报错也不是这个,而是“vecotr iterator not dereferencable”。意思是迭代器不可引用,已经失效。
既然上面两种都不是引发我们问题的原因,那么是什么导致的呢。在上面帖子最后,他还提到了跟踪到底层库是发现某个变量值为0,于是我也调试代码,跟踪到vector的
源码中,步骤如下:
1.进入vector函数 operator != :
bool operator!=(const _Myiter& _Right) const
{ // test for iterator inequality
return (!(*this == _Right));
}
2.进入重载的 operator == :
bool operator==(const _Myiter& _Right) const
{ // test for iterator equality
_Compat(_Right);
return (this->_Ptr == _Right._Ptr);
}
3.进入_Compa函数:
#if _ITERATOR_DEBUG_LEVEL == 2
void _Compat(const _Myiter& _Right) const
{ // test for compatible iterator pair
if (this->_Getcont() == 0
|| this->_Getcont() != _Right._Getcont())
{ // report error
_DEBUG_ERROR("vector iterators incompatible");
_SCL_SECURE_INVALID_ARGUMENT;
}
}
#elif
...
#endif
4._Getcont函数:
const _Container_base12 *_Getcont() const
{ // get owning container
return (_Myproxy == 0 ? 0 : _Myproxy->_Mycont);
}
发现果然“_Myproxy”指针的值是0。然后我就查看朋友的代码,发现他程序中确实有个地方使用了memset,而且传入的指针是一个野指针,导致了vector内部某些值被错误修改。
至此,问题解决。不过,我还是想粗浅的展示一些vector的细节,更详细的待以后再说吧。
打印上面代码中base的两个成员变量vec和a的地址,分别是0x0044F8FC,0X0044F910,这意味着vector一出来至少就占用了20个字节。在vector类中有如下定义:
pointer _Myfirst; // pointer to beginning of array
pointer _Mylast; // pointer to current end of sequence
pointer _Myend; // pointer to end of array
_Alty _Alval; // allocator object for values
同时std::vector<_Ty,_Alloc>通过基类_Vector_val<_Ty,_Alloc>包含了一个_Container_proxy对象,_Container_proxy::_Myfirstiter指向了一个单向链表P的头指针,P中包含
一个vector对象的所有迭代器的指针。
当我们使用memset初始化Base类时,其实也把所有的这些指针都置0了。所以,使用memset的时候还是需要很慎重,至少有stl容器的时候最好不要用这种方式去初始化
一块内存。
关于“vector iteraotr incompatible”就暂时讲到这吧,近期打算以侯捷老师的《STL源码剖析》为参考,通读下STL的源码,了解下容器内部构造以及设计理念。这对于以
后自己编写游戏引擎内存管理什么的或许有所帮助吧。