1. 程式人生 > >c++ map 使用自定義結構做關鍵字

c++ map 使用自定義結構做關鍵字

map在STL中的定義

template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>

 第一個引數Key是關鍵字型別

第二個引數T是值型別

第三個引數Compare是比較函式(仿函式)

第四個引數是記憶體配置物件

map內部儲存機制實際是以紅黑樹為基礎,紅黑樹在插入節點時,必須依照大小比對之後在一個合適的位置上執行插入動作。所以作為關鍵字,起碼必須有“<”這個比較操作符。我們知道,int,float,enum,size_t等等簡單關鍵字,都有內建的比較函式,與map搭配無論是插入還是查詢,都沒什麼問題。但是作為複雜資料型別,如果沒有明確定義“<”比較操作符,就不能與map直接搭配使用,除非我們自己定義第三個引數。

在選擇map的關鍵字時,注意以下兩點,同時這兩點也是改錯的方法:

a) 關鍵字明確定義“<”比較操作符

b) 沒有“<”比較操作符,自定義仿函式替代第三個引數Compare,該仿函式實現“()”操作符,提供比較功能。插入時各節點順序以該仿函式為綱。

以std::pair為關鍵字摻入map
下面我們先寫一個有錯誤的函式,在分析錯誤原因之後,逐步進行修正。

#include <map>
int main()
{
       std::map<std::pair<int, int>, int> res;
       res.insert(std::make_pair(12,33), 33);
}

 這個程式一定失敗,如果非要如此使用,上述a方法顯然不適合,std::pair是已定義好的結構體不可修改。只能使用b方法了,定義一個比較類改造如下:

    #include <map>  
      
    struct comp  
      
    {  
      
           typedef std::pair<int, int> value_type;  
      
           bool operator () (const value_type & ls, const value_type &rs)  
      
           {  
      
                  return ls.first < rs.first || (ls.first == rs.first && ls.second < rs.second);  
      
           }  
      
    };  
      
    int main()  
      
    {  
      
           std::map<std::pair<int, int>, int, comp> res;  
      
           res.insert(std::make_pair(std::make_pair(12,33), 33));  
      
           res.insert(std::make_pair(std::make_pair(121,331), 331));  
      
           res.insert(std::make_pair(std::make_pair(122,332), 332));  
      
            
      
           std::map<std::pair<int, int>, int, comp>::iterator it = res.find(std::make_pair(121,331));  
      
           if (it == res.end())  
      
                  printf("NULL"n");  
      
           else  
      
                  printf("%d %d %d "n", it->first.first, it->first.second, it->second);  
      
        return 0;  
      
    }  

以結構體或類為關鍵字插入map

    #include <map>  
      
    struct st  
      
    {  
      
           int a, b;  
      
           st():a(0), b(0){}  
      
           st(int x, int y):a(x), b(y){}  
      
    };  
      
    int main()  
      
    {  
      
           std::map<struct st, int> res;  
      
           res.insert(std::make_pair(st(1,2), 12));  
      
           res.insert(std::make_pair(st(30,4), 34));  
      
           res.insert(std::make_pair(st(5,6), 56));  
      
            
      
           std::map<struct st, int>::iterator it = res.find(st(30,4));  
      
           if (it == res.end())  
      
                  printf("NULL"n");  
      
           else  
      
                  printf("first:%d second:%d %d"n", it->first.a, it->first.b, it->second);  
      
           return 0;  
      
    }  

編譯這個程式也是錯誤的,錯誤意思大概也是沒有定義“<”比較函式。因為struct st是我們自己定義的結構體,所以修改這個程式可以使用上面ab兩種方法。我們先談第一種,第一次修改時我也搞錯了,我是這樣定義比較函式的。

    struct st  
      
    {  
      
           int a, b;  
      
           st():a(0), b(0){}  
      
           st(int x, int y):a(x), b(y){}  
      
    bool operator < (const struct st &rs) {return (this->a < rs.a || (this->a == rs.a && this->b < rs.b));}  
      
    };  

按照這個改動再次編譯程式還是錯誤,有個如下這樣的提示:

/usr/include/c++/3.2.3/bits/stl_function.h:197: passing `const st' as `this' argument of `bool st::operator<(const st&)' discards qualifiers

   為什麼會出現這個 問題呢?我們深入STL的原始碼看下。既然說是/usr/include/c++/3.2.3/bits/stl_function.h的197行出了問題,且看這行是什麼。

// One of the @link s20_3_3_comparisons comparison [email protected].

    template <class _Tp>

    struct less : public binary_function<_Tp,_Tp,bool>

    {

         bool operator()(const _Tp& __x, const _Tp& __y) const { return __x < __y; }

     };

struct st中的“<”在編譯後真正是什麼樣子呢?大概是bool operator < (struct st &ls, const struct st &rs)。在less呼叫這個比較符時,它都是以const方式傳入,不可能再以非const方式呼叫,故出錯。修正如下

struct st

{

       int a, b;

       st():a(0), b(0){}

       st(int x, int y):a(x), b(y){}

       friend bool operator < (const struct st &ls, const struct st &rs);

};

inline bool operator < (const struct st &ls, const struct st &rs)

{return (ls.a < rs.a || (ls.a == rs.a && ls.b < rs.b));}

 以友聯函式代替函式內部定義的比較操作符,STL內部也多是以這種方式定義的。如果我非要以內部定義的方式呢?可以使用b方法,我們自定義一個比較仿函式,替代預設的less。

插入函式返回值
       在map容器中插入資料有很多函式可用,這裡只討論最普通的insert操作,在STL中它是這樣定義的。

       pair<iterator, bool> insert(const value_type& x);

       map容器不允許鍵值重複,在執行插入操作後,可以憑藉該返回值獲取操作結果。返回值是一個迭代器和布林值的鍵值對,迭代器指向map中具有該值的元素,布林值表示是否插入成功。如果布林值為true,表示插入成功,則迭代器為新插入值在map中的位置;布林值為false,表示插入失敗(已經存在該值),迭代器為原有值在map中的位置。

    #include <map>  
    #include <iostream>  
    using namespace std;  
      
    class Key  
    {  
    public:  
        Key();  
        Key(int v);  
        int _key;  
        ~Key();  
        /*過載<作為成員函式不行,兩個運算元都要求是const*/  
        //bool operator <(const Key& key);  
    };  
    bool operator <(const Key &key1,const Key &key2)  
    {  
        if(key1._key<key2._key)  
            return true;  
        else  
            return false;  
    }  
    Key::Key()  
    {  
    }  
    Key::Key(int v)  
    {  
        _key=v;  
    }  
    Key::~Key()  
    {  
    }  
      
    void main()  
    {  
          
        map<Key,int> ClassMap;  
        Key one(1);  
        ClassMap.insert(make_pair(one,1));  
        Key two(2);  
        ClassMap.insert(make_pair(two,2));  
        Key three(3);  
        ClassMap.insert(make_pair(three,3));  
        map<Key,int>::iterator itor=ClassMap.begin();  
        while(itor!=ClassMap.end())  
        {  
            cout<<itor->first._key<<" ~~ "<<itor->second<<endl;  
            ++itor;  
        }  
    }  



糾正上例中的一個錯誤,過載<的時候,也可以這麼做:

#include <map>  
    #include <iostream>  
    using namespace std;  
      
    class Key  
    {  
    public:  
        Key();  
        Key(int v);  
        int _key;  
        ~Key();  
       
        bool operator <(const Key& key) const;  
    };  
    bool Key::operator <(const Key &key)  const
    {  
        if(this->_key<key._key)  
            return true;  
        else  
            return false;  
    }  
    Key::Key()  
    {  
    }  
    Key::Key(int v)  
    {  
        _key=v;  
    }  
    Key::~Key()  
    {  
    }  
      
    void main()  
    {  
          
        map<Key,int> ClassMap;  
        Key one(1);  
        ClassMap.insert(make_pair(one,1));  
        Key two(2);  
        ClassMap.insert(make_pair(two,2));  
        Key three(3);  
        ClassMap.insert(make_pair(three,3));  
        map<Key,int>::iterator itor=ClassMap.begin();  
        while(itor!=ClassMap.end())  
        {  
            cout<<itor->first._key<<" ~~ "<<itor->second<<endl;  
            ++itor;  
        }  
    }