STL map 內存改變,迭代器失效,_Isnil(_Ptr)和紅黑樹


STL map 內存改變,迭代器失效,_Isnil(_Ptr)和紅黑樹

最近在做項目時發現一個crash的問題,當時得到的dmp文件顯示crash在一個以map為循環變量的循環中,crash位置在如下的代碼中標出。

 

         void _Inc()
            {     //  move to node with next larger value

  #if _HAS_ITERATOR_DEBUGGING
             if ( this->_Mycont ==  0
                || _Ptr ==  0
                || _Isnil(_Ptr))
                {
                _DEBUG_ERROR( " map/set iterator not incrementable ");
                _SCL_SECURE_OUT_OF_RANGE;
                }
  #else
            _SCL_SECURE_VALIDATE( this->_Has_container());
             if (_Isnil(_Ptr))---------------------------------------->Why crash here?
                {
                _SCL_SECURE_OUT_OF_RANGE;
                 //  end() shouldn't be incremented, don't move if _SCL_SECURE is not turned on
                }
  #endif /* _HAS_ITERATOR_DEBUGGING */

             else  if (!_Isnil(_Right(_Ptr)))
                _Ptr = _Min(_Right(_Ptr));     //  ==> smallest of right subtree
             else
                {     //  climb looking for right subtree
                _Nodeptr _Pnode;
                 while (!_Isnil(_Pnode = _Parent(_Ptr))
                    && _Ptr == _Right(_Pnode))
                    _Ptr = _Pnode;     //  ==> parent while right subtree
                _Ptr = _Pnode;     //  ==> parent (head if end())
                }
            }

 

這是C++ 中紅黑樹迭代器的標准實現,那從這個棧幀能說明我們的代碼哪出問題了么?在閱讀紅黑樹的實現代碼中有一條語句困擾了我大約半個小時的時間,這條語句就是:

_Isnil(_Ptr)

標准實現中到處都是這條語句,2年前算法導論系統的學習過一遍,但是由於長時間沒有相關的功能需要用到這么深入的知識,有些具體的問題已經記得不是很清楚,於是為了弄對這條語句以及對紅黑樹有透測的理解,再一次對紅黑樹知識進行了系統的學習,並翻閱了一些資料,為了使本文自成體系,下面對基礎知識進行一些說明。

紅黑樹定義

紅黑樹是一種自平衡二叉查找樹,是在計算機科學中用到的一種數據結構,典型的用途是實現關聯數組,如STL map set,和普通二叉樹相比它的實現上稍微有些復雜,但它的操作有着良好的最壞情況運行時間,並且在實踐中是高效的: 它可以在O(log n)時間內做查找,插入和刪除,這里的n是樹中元素的數目。

紅黑樹性質

紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色為紅色或黑色。在二叉查找樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求:

性質1. 節點是紅色或黑色。

性質2. 根是黑色。

性質3. 所有葉子都是黑色(葉子是NIL節點)。

性質4. 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)

性質5. 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

算法導論原版定義

A red-black tree is a binary search tree with one extra bit of storage per node: its color, which can be either RED or BLACK. By constraining the node colors on any simple path from the root to a leaf, red-black trees ensure that no such path is more than twice as long as any other, so that the tree is approximately balanced. Each node of the tree now contains the attributes color, key, left, right, and p. If a child or the parent of a node does not exist, the corresponding pointer attribute of the node contains the value NIL. We shall regard these NILs as being pointers to leaves (external nodes) of the binary search tree and the normal, key-bearing nodes as being internal nodes of the tree. A red-black tree is a binary tree that satisfies the following red-black properties:

1. Every node is either red or black.

2. The root is black.

3. Every leaf (NIL) is black.

4. If a node is red, then both its children are black.

5. For each node, all simple paths from the node to descendant leaves contain the same number of black nodes.

典型的紅黑樹如下圖所示

 

紅黑樹的典型操作

插入/刪除/左旋/右旋/查找/遍歷再次不做贅述,有興趣的讀者可以參考STL源碼實現。

總結

再來看看我們的問題,請看如下的迭代器實現,迭代器++操作符會調用_Inc(),由於多線程,map被破壞,迭代器失效導致循環無法結束並crash。那么為什么會crash呢?原來紅黑樹的標准實現中真正的葉子節點都是NIL(哨兵)節點,並且規定葉子節點已經到達了紅黑樹的邊界,所以不能在++,如果在++系統就會 crash

const_iterator& operator++()

{      // preincrement

       _Inc();

return (*this);

}

那么這條語句:_Isnil(_Ptr) 到底是什么意思呢?

答案:就是說當前操作的迭代器是否已經指向了黑色的葉子節點,這個節點其實是哨兵(NIL)節點,指向了這個哨兵節點以后的迭代器是不能++的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM