• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            xiaoguozi's Blog
            Pay it forword - 我并不覺的自豪,我所嘗試的事情都失敗了······習慣原本生活的人不容易改變,就算現狀很糟,他們也很難改變,在過程中,他們還是放棄了······他們一放棄,大家就都是輸家······讓愛傳出去,很困難,也無法預料,人們需要更細心的觀察別人,要隨時注意才能保護別人,因為他們未必知道自己要什么·····

            說來慚愧,使用了很久Visual Stdio 2003了,只知道MFC升級到了7.0,ATL也升級到了7.0,對于這兩個經典的類庫做了一些研究,但一直沒有注意C++標準庫的變化。

                 今天嘗試的使用了stdext::hash_map這個庫,果然不錯。下面寫下一些心得。

                 hash_map類在頭文件hash_map中,和所有其它的C++標準庫一樣,頭文件沒有擴展名。如下聲明:

                      #include <hash_map>
                      using namespace std;
                      using namespace stdext;

                 hash_map是一個聚合類,它繼承自_Hash類,包括一個vector,一個list和一個pair,其中vector用于保存桶,list用于進行沖突處理,pair用于保存key->value結構,簡要地偽碼如下:

                      class hash_map<class _Tkey, class _Tval>
                      {
                      private:
                           typedef pair<_Tkey, _Tval> hash_pair;
                           typedef list<hash_pair>    hash_list;
                           typedef vector<hash_list>  hash_table;
                      };

                 當然,這只是一個簡單模型,C++標準庫的泛型模版一向以嵌套復雜而聞名,初學時看類庫,無疑天書啊。微軟的hash_map類還聚合了hash_compare仿函數類,hash_compare類里有聚合了less仿函數類,亂七八糟的。

                 下面說說使用方法:

                 一、簡單變量作為索引:整形、實性、指針型
                 其實指針型也就是整形,算法一樣。但是hash_map會對char*, const char*, wchar_t*, const wchar_t*做特殊處理。
                 這種情況最簡單,下面代碼是整形示例:
                        hash_map<int, int> IntHash;
                        IntHash[1] = 123;
                        IntHash[2] = 456;

                        int val = IntHash[1];
                        int val = IntHash[2];
                 實型和指針型用法和整形一樣,原理如下:
                 1、使用簡單類型作索引聲明hash_map的時候,不需要聲明模版的后兩個參數(最后一個參數指名hash_map節點的存儲方式,默認為pair,我覺得這就挺好,沒必要修改),使用默認值就好。
                 2、對于除過字符串的其它簡單類型,hash_map使用模版函數 size_t hash_value(const _Kty& _Keyval) 計算hash值,計算方法是經典的掩碼異或法,自動溢出得到索引hash值。微軟的工程師也許開了一個玩笑,這個掩碼被定義為0xdeadbeef(死牛肉,抑或是某個程序員的外號)。
                 3、對于字符串指針作索引的時候,使用定類型函數inline size_t hash_value(const char *_Str)或inline size_t hash_value(const wchar_t *_Str)計算hash值,計算方法是取出每一個字符求和,自動溢出得到hash值。對于字符串型的hash索引,要注意需要自定義less仿函數。
                 因為我們有理由認為,人們使用hash表進行快速查找的預期成本要比在hash表中插入的預期成本低得多,所以插入可以比查找昂貴些;基于這個假設,hash_map在有沖突時,插入鏈表是進行排序插入的,這樣在進行查詢沖突解決的時候就能夠更快捷的找到需要的索引。
                 但是,基于泛型編程的原則,hash_map也有理由認為每一種類型都支持使用"<"來判別兩個類型值的大小,這種設計恰好讓字符串類型無所適從,眾所周知,兩個字符串指針的大小并不代表字符串值的大小。見如下代碼:
                      hash_map<const char*, int> CharHash;
                      CharHash["a"] = 123;
                      CharHash["b"] = 456;

                      char szInput[64] = "";
                      scanf("%s", szInput);

                      int val = CharHash[szInput];

                 最終的結果就是無論輸入任何字符串,都無法找到對應的整數值。因為輸入的字符串指針是szInput指針,和"a"或"b"字符串常量指針的大小是絕對不會相同。解決方法如下:
                 首先寫一個仿函數CharLess,繼承自仿函數基類binary_function(當然也可以不繼承,這樣寫只是符合標準,而且寫起來比較方便,不用被類似于指針的指針和指針的引用搞暈。

                      struct CharLess : public binary_function<const char*, const char*, bool>
                      {
                      public:
                           result_type operator()(const first_argument_type& _Left, const second_argument_type& _Right) const
                           {
                                return(stricmp(_Left, _Right) < 0 ? true : false);
                           }
                      };

                 很好,有了這個仿函數,就可以正確的使用字符串指針型hash_map了。如下:

                      hash_map<const char*, int, hash_compare<const char*, CharLess> > CharHash;
                      CharHash["a"] = 123;
                      CharHash["b"] = 456;

                      char szInput[64] = "";
                      scanf("%s", szInput);

                      int val = CharHash[szInput];
                 
                 現在就可以正常工作了。至此,簡單類型的使用方法介紹完畢。

                 二、用戶自定義類型:比如對象類型,結構體。
                 這種情況比價復雜,我們先說簡單的,對于C++標準庫的string類。
                 
                 慶幸的是,微軟為basic_string(string類的基類)提供了hash方法,這使得使用string對象做索引簡單了許多。值得注意(也值得郁悶)的是,雖然支持string的hash,string類卻沒有重載比較運算符,所以標準的hash_compare仿函數依舊無法工作。我們繼續重寫less仿函數。
                     
                      struct string_less : public binary_function<const string, const string, bool>
                      {
                      public:
                           result_type operator()(const first_argument_type& _Left, const second_argument_type& _Right) const
                           {
                                return(_Left.compare(_Right) < 0 ? true : fase);
                           }
                      };
                       
                 好了,我們可以書寫如下代碼:
                       
                      hash_map<string, int, hash_compare<string, string_less> > StringHash;
                      StringHash["a"] = 123;
                      StringHash["b"] = 456;

                      string strKey = "a";

                      int val = CharHash[strKey];
                 
                 這樣就可以了。
                 
                 對于另外的一個常用的字符串類CString(我認為微軟的CString比標準庫的string設計要灑脫一些)更加復雜一些。很顯然,標準庫里不包含對于CString的支持,但CString卻重載了比較運算符(郁悶)。我們必須重寫hash_compare仿函數。值得一提的是,在Virtual Stdio 2003中,CString不再是MFC的成員,而成為ATL的成員,使用#include <atlstr.h>就可以使用。我沒有采用重寫hash_compare仿函數的策略,而僅僅是繼承了它,在模版庫中的繼承是沒有性能損耗的,而且能讓我偷一點懶。
                 首先重寫一個hash_value函數:
                 
                      inline size_t CString_hash_value(const CString& str)
                      {
                           size_t value = _HASH_SEED;
                           size_t size  = str.GetLength();
                           if (size > 0) {
                                size_t temp = (size / 16) + 1;
                                size -= temp;
                                for (size_t idx = 0; idx <= size; idx += temp) {
                                     value += (size_t)str[(int)idx];
                                }
                           }
                           return(value);
                      }
                 
                 其次重寫hash_compare仿函數:
                 
                      class CString_hash_compare : public hash_compare<CString>
                      {
                      public:
                           size_t operator()(const CString& _Key) const
                           {
                                return((size_t)CString_hash_value(_Key));
                           }
              
                           bool operator()(const CString& _Keyval1, const CString& _Keyval2) const
                           {
                                return (comp(_Keyval1, _Keyval2));
                           }
                      };
                       
                 上面的重載忽略了基類對于less仿函數的引入,因為CString具備比較運算符,我們可以使用默認的less仿函數,在這里映射為comp。好了,我們可以聲明新的hash_map對象如下:

                      hash_map<CString, int, CString_hash_compare> CStringHash;

                 其余的操作一樣一樣的。

                 下來就說說對于自定義對象的使用方法:首先定義
                 
                      struct IHashable
                      {
                           virtual unsigned long hash_value() const = 0;
                           virtual bool operator < (const IHashable& val) const = 0;
                           virtual IHashable& operator = (const IHashable& val) = 0;
                      };
                 
                 讓我們自寫的類都派生自這里,有一個標準,接下來定義我們的類:
                 
                      class CTest : public IHashable
                      {
                      public:
                           int m_value;
                           CString m_message;
                      public:
                           CTest() : m_value(0)
                           {
                           }
                       
                           CTest(const CTest& obj)
                           {
                                m_value = obj.m_value;
                                m_message = obj.m_message;
                           }
                      public:
                           virtual IHashable& operator = (const IHashable& val)
                           {
                                m_value   = ((CTest&)val).m_value;
                                m_message = ((CTest&)val).m_message;
                                return(*this);
                           }
                       
                           virtual unsigned long hash_value() const
                           {
                                // 這里使用類中的m_value域計算hash值,也可以使用更復雜的函數計算所有域總的hash值
                                return(m_value ^ 0xdeadbeef 
                           }
                       
                           virtual bool operator < (const IHashable& val) const
                           {
                                return(m_value < ((CTest&)val).m_value);
                           }
                      };
                 
                 用這個類的對象做為hash索引準備工作如下,因為接口中規定了比較運算符,所以這里可以使用標準的less仿函數,所以這里忽略:
                 
                      template<class _Tkey>
                      class MyHashCompare : public hash_compare<_Tkey>
                      {
                      public:
                           size_t operator()(const _Tkey& _Key) const
                           {
                                return(_Key.hash_value());
                           }
                       
                           bool operator()(const _Tkey& _Keyval1, const _Tkey& _Keyval2) const
                           {
                                return (comp(_Keyval1, _Keyval2));
                           }
                      };
                       
                 下來就這樣寫:
                 
                      CTest test;
                      test.m_value = 123;
                      test.m_message = "This is a test";
                 
                      MyHash[test] = 2005;
                       
                      int val = MyHash[test];
                 
                 可以看到正確的數字被返回。
                 
                 三、關于hash_map的思考:
                 
                 1、性能分析:采用了內聯代碼和模版技術的hash_map在效率上應該是非常優秀的,但我們還需要注意如下幾點:
                 
                 * 經過查看代碼,字符串索引會比簡單類型索引速度慢,自定義類型索引的性能則和我們選擇hash的內容有很大關系,簡單為主,這是使用hash_map的基本原則。
                 * 可以通過重寫hash_compair仿函數,更改里面關于桶數量的定義,如果取值合適,也可以得到更優的性能。如果桶數量大于10,則牢記它應該是一個質數。
                 * 在自定義類型是,重載的等號(或者拷貝構造)有可能成為性能瓶頸,使用對象指針最為索引將是一個好的想法,但這就必須重寫less仿函數,理由同使用字符串指針作為索引。

            posted on 2008-01-12 18:31 小果子 閱讀(9692) 評論(0)  編輯 收藏 引用 所屬分類: 學習筆記
            久久久国产精品| 久久国产精品成人影院| 99久久成人国产精品免费| 97香蕉久久夜色精品国产| 久久精品?ⅴ无码中文字幕| 99久久99久久精品国产片| 亚洲国产精品热久久| 热久久这里只有精品| 青青草原综合久久大伊人精品| 996久久国产精品线观看| 久久99精品国产99久久| 久久线看观看精品香蕉国产| 国产精品久久波多野结衣| 日韩精品国产自在久久现线拍| 国产精品永久久久久久久久久 | 无码日韩人妻精品久久蜜桃| 久久夜色精品国产亚洲| 久久精品一区二区三区AV| 色婷婷综合久久久久中文| 国产精品久久久久久久久鸭 | 国产日产久久高清欧美一区| 久久精品国产亚洲av水果派| 久久99毛片免费观看不卡| 99精品久久久久久久婷婷| 国产精品女同一区二区久久| 久久综合鬼色88久久精品综合自在自线噜噜| 97视频久久久| 久久r热这里有精品视频| 久久婷婷人人澡人人| 精品国产99久久久久久麻豆| 996久久国产精品线观看| 亚洲精品久久久www| 99re久久精品国产首页2020| 亚洲国产精品综合久久一线 | 99精品国产99久久久久久97| 久久精品国产亚洲AV无码麻豆 | 94久久国产乱子伦精品免费| 久久久久香蕉视频| 国产综合久久久久| 久久综合色老色| 国产午夜电影久久|