您的位置:首页 > 其它

Key的小于操作符号重载不正确导致map占用CPU占用100%的分析

2007-04-01 23:26 465 查看
 

一、问题描述。
map的key的小于操作符重载的实现不正确,导致在删除一个元素时候,CPU占用100%。
有问题的代码如下:




using namespace std;


class CUser




...{


public:




    CUser(int iID,int iKey):miID(iID),miKey(iKey)...{};




    ~CUser()...{};


public:


    int miID;


    int miKey;


    //小于操作符号重载有问题的实现


    bool operator<(const CUser &rhs) const




    ...{


       if(miID < rhs.miID)




       ...{


           return true;


       }


       if(miKey < rhs.miKey)




       ...{


           return true;


       }   


       return false;


    };


    //小于操作符号重载正确的实现




    /**//*


    bool operator<(const CUser &rhs) const


    {


       if(miID != rhs.miID)


       {


           return miID < rhs.miID;


       }


       return miKey < rhs.miKey;


    };


    */


};


typedef map<CUser,string> CUserMap;


typedef CUserMap::iterator CUMIterator;


int main(int argc, char* argv[])




...{


 


    CUserMap cu;


    cu[CUser(1,3)] = "1--3";//Node A


    cu[CUser(3,1)] = "3--1";//Node B


    //该map的结构为Head(end),A,B


    cu.erase(CUser(3,1));//此处程序挂在这儿,CPU占用100%


    cout << "hello"<<endl;


    return 0;


}



 
二、原因分析
 
1、 这是插入A、B节点后map cu的树结构

2、 STL源码分析
[align=left](1)map的删除函数[/align]


size_type erase(const key_type& _Keyval)// _Keyval为B(3,1)




       ...{   // erase and count all that match _Keyval


       // equal_range返回_Keyval可以插入的第一个位置和最后一个位置


//[_Where.first, _Where.second),也就是元素=_Keyval的元素区间,


//此处正确的结果应该是[B,Head(end))


//但此次结果却为[B,A),导致执行_Distance时候,出现死循环


       _Pairii _Where = equal_range(_Keyval);


       size_type _Num = 0;


       //_Distance计算_Where.second到_Where.first的距离,


//是通过++计算的


       _Distance(_Where.first, _Where.second, _Num);


       erase(_Where.first, _Where.second);


       return (_Num);


       }



 
[align=left](2)[/align]
 


template<class _BidIt,


    class _Diff> inline


       void _Distance2(_BidIt _First, _BidIt _Last, _Diff& _Off,


           bidirectional_iterator_tag)




    ...{   // add to _Off distance between bidirectional iterators (redundant)


    // B++ == Head, Head ++ == Head


    for (; _First != _Last; ++_First)


       ++_Off;


    }



[align=left] (3)获取_Where.second的结果[/align]


_Nodeptr _Ubound(const key_type& _Keyval) const




       ...{   // find leftmost node greater than _Keyval


       _Nodeptr _Pnode = _Root();


       _Nodeptr _Wherenode = _Myhead; // end() if search fails


       // _Keyval= B(3,1), _Pnode = A(1,3)


       while (!_Isnil(_Pnode))


           //此处comp调用了Cuser的<,B(3,1) < A(1,3) 应该是false,但是这里却返回true


           if (this->comp(_Keyval, _Key(_Pnode)))




              ...{   // _Pnode greater than _Keyval, remember it


              _Wherenode = _Pnode;


              _Pnode = _Left(_Pnode); // descend left subtree


              }


           else


              _Pnode = _Right(_Pnode); // descend right subtree


 


       return (_Wherenode); // return best remembered candidate


       }



   
[align=left] [/align]
[align=left]3、 根本原因[/align]
[align=left]在插入的时候,首先插入的是A(1,3),在插入B时候,使用的比较操作是comp(A, B),但在删除的时候,用到是comp(B,A). [/align]
[align=left]comp(A, B) => A(1,3) < B(3,1) 结果为true[/align]
[align=left]comp(B, A) => B(3,1) < A(1,3) 结果为true[/align]
[align=left]comp(A, B)  ==  !( comp(B, A)),[/align]
[align=left]即Cuser的< 不符合小于的定义[/align]
 
三、对策
[align=left]1、在重载操作符时候,要使用其基本语意进行验证,看是否自相矛盾,例如在定义<时候,可以判断(A<B) != (B<A)是否为真。[/align]
[align=left]2、操作符<的定义一般都是类似如下定义[/align]
          


    bool operator<(const CUser &rhs) const




    ...{


        if(miID != rhs.miID)




        ...{


            return miID < rhs.miID;


        }


        return miKey < rhs.miKey;


    };    



[align=left] [/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息