本文分析DEBUG配置下,VS 2010中std::vector和相關迭代器提供的安全檢查和記錄功能。有的功能非常耗時。幸好在RELEASE配置下,這些功能都被預編譯指令去掉了。當然這也說明,針對VC程序的效率測試必須採用RELEASE版本,否則測試結果很難說明問題。
std::vector和std::vector迭代器的類圖
其中,_Vector_iterator<_Myvec>是std::vector<_Ty,_Alloc>::begin()返回的類型,
_Vector_const_iterator<_Myvec>是相應的const iterator
迭代器的有效性檢查
在DEBUG配置下,_Vector_const_iterator負責檢查迭代器的有效性:
- 在迭代器遞增、遞減、解引用等操作的時候,檢查迭代器是否在vector的合法範圍內。
- 兩個迭代器比較大小的時候,驗證它們迭代器是同一個vector的迭代器。
比如以下取值函數,就是檢查指針是否越界。
reference operator*() const { // return designated object #if _ITERATOR_DEBUG_LEVEL == 2 // 這裏檢查指針沒有越界 if (this->_Getcont() == 0 || this->_Ptr == 0 || this->_Ptr < ((_Myvec *)this->_Getcont())->_Myfirst || ((_Myvec *)this->_Getcont())->_Mylast <= this->_Ptr) { // report error _DEBUG_ERROR("vector iterator not dereferencable"); _SCL_SECURE_OUT_OF_RANGE; } |
迭代器的單向鏈表
std::vector<_Ty,_Alloc>通過基類_Vector_val<_Ty,_Alloc>包含了一個_Container_proxy對象,_Container_proxy::_Myfirstiter指向了一個單向鏈表P的頭指針,P中包含一個vector對象的所有迭代器的指針。因此
- 新建一個迭代器,需要向P的頭部插入一個指針,這是在常數時間完成的。
- 銷燬一個迭代器,需要從P中刪除迭代器對應的指針。由於只保存了單向鏈表P的頭指針,這具有線性的時間複雜度(見下面的代碼)。如果一個容器的迭代器對象非常多,(比如hash_map的內部實現),則會非常耗時。
// 把迭代器指針從單向列表中刪除 void _Iterator_base12::_Orphan_me() { // cut ties with parent #if _ITERATOR_DEBUG_LEVEL == 2 if (_Myproxy != 0) { // 從鏈表的頭指針向後遍歷,找到當前元素的前一個元素 _Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter; while (*_Pnext != 0 && *_Pnext != this) _Pnext = &(*_Pnext)->_Mynextiter;
if (*_Pnext == 0) _DEBUG_ERROR("ITERATOR LIST CORRUPTED!"); *_Pnext = _Mynextiter; _Myproxy = 0; } #endif /* _ITERATOR_DEBUG_LEVEL == 2 */ }
|