STL vector容器需要警惕的一些坑

从迭代器中取值切记需要判断是否为空

例如:

1 vector<int> vtTest;
2 vtTest.clear();
3 if (vtTest.empty()){
4    return 0;
5 }
6     
7 int *pTest = &vtTest[0];

如果没有忘了判断则会出现这样的异常_DEBUG_ERROR("vector subscript out of range");

其实这条异常是Visual C++专有,在g++当中并不会出现,所取得的数值是0。可问题是你怎么区分里面所存的数据本身是0还是vector为空呢?

带有容器的结构体不要使用memset清0

例如以下代码:

 1 struct _Test{
 2     int i;
 3     vector<int> vtTest;
 4 };
 5 
 6 _Test test;
 7 memset(&test,0,sizeof(test));
 8 test.vtTest.push_back(0);
 9 test.vtTest.push_back(0);
10 test.vtTest.push_back(0);

其实这部分代码完全可以正常运行,但是如果加上以下代码就一样了

1 vector<int>::iterator it = test.vtTest.begin();
2 it++;

问题就出在it++这条语句之上,此时会抛出vector iterator not incrementable

因为在自加的操作中,有这么一条判断

1 if (this->_Getcont() == 0
2             || this->_Ptr == 0
3             || ((_Myvec *)this->_Getcont())->_Mylast <= this->_Ptr)
4             {    // report error
5             _DEBUG_ERROR("vector iterator not incrementable");
6             _SCL_SECURE_OUT_OF_RANGE;
7             }

在this->_Getcont()函数内部,其实现是这样的;

return (_Myproxy == 0 ? 0 : _Myproxy->_Mycont);

一旦调用上面的memset(&test,0,sizeof(test));之后,vector的_Myproxy数据结构也被清0了,此时_Getcont()函数的返回值就是0,那么程序就会执行到_DEBUG_ERROR("vector iterator not incrementable");经过分析发现,_Myproxy变量是vector用来寻找相邻的数值,而我们的清0操作导致这个链条断裂了,破坏了vector的数据结构从而导致异常。但是g++编译器并没有以上问题,因为VS改写了STL代码。

其实memset清0操作对于结构体或者类都必须慎重,因为很容易破坏自身的数据结构,最典型的就是带有虚函数的类,一旦清0连虚表都给破坏掉了。

vector内存控制
vector每次调用push_back的时候,如果之前内存够用就直接插入,如果不够用了就重新申请一块更大的内存(g++是在原基础上增加一倍长度,VS是增加百分之五十),然后将数据拷贝到新内存当中,释放原内存,再插入新数据。

那么有两个问题,第一,如果能预测到数据量是固定数字,一定要首先预备一块内存,然后插入数据,这样第一避免多次重复申请释放,拷贝等耗资源和时间的无用操作,也可以避免多出一块并不会使用的内存。

其次如果从vector中删除元素,由于vector内存只增不减,当你申请一万个元素空间,删除了9999个,但是其占用仍然是10000个元素空间,如果条件允许,其实可以考虑增加策略来避免内存浪费,因为这些内存只有在析构的时候才会彻底释放掉。如果是指针则必须要手动析构,先遍历逐一delete,然后clear。

由于vector是申请的一块内存,所以如果要从首部删除元素会导致后面的所有元素向前移动一个单位,如果一直这么操作其占用可想而知。。。

你可能感兴趣的:(STL vector容器需要警惕的一些坑)