• <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 - 我并不覺的自豪,我所嘗試的事情都失敗了······習(xí)慣原本生活的人不容易改變,就算現(xiàn)狀很糟,他們也很難改變,在過程中,他們還是放棄了······他們一放棄,大家就都是輸家······讓愛傳出去,很困難,也無法預(yù)料,人們需要更細(xì)心的觀察別人,要隨時注意才能保護(hù)別人,因為他們未必知道自己要什么·····

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

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

                 hash_map類在頭文件hash_map中,和所有其它的C++標(biāo)準(zhǔn)庫一樣,頭文件沒有擴展名。如下聲明:

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

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

                      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;
                      };

                 當(dāng)然,這只是一個簡單模型,C++標(biāo)準(zhǔn)庫的泛型模版一向以嵌套復(fù)雜而聞名,初學(xué)時看類庫,無疑天書啊。微軟的hash_map類還聚合了hash_compare仿函數(shù)類,hash_compare類里有聚合了less仿函數(shù)類,亂七八糟的。

                 下面說說使用方法:

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

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

                      int val = CharHash[szInput];

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

                      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);
                           }
                      };

                 很好,有了這個仿函數(shù),就可以正確的使用字符串指針型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];
                 
                 現(xiàn)在就可以正常工作了。至此,簡單類型的使用方法介紹完畢。

                 二、用戶自定義類型:比如對象類型,結(jié)構(gòu)體。
                 這種情況比價復(fù)雜,我們先說簡單的,對于C++標(biāo)準(zhǔn)庫的string類。
                 
                 慶幸的是,微軟為basic_string(string類的基類)提供了hash方法,這使得使用string對象做索引簡單了許多。值得注意(也值得郁悶)的是,雖然支持string的hash,string類卻沒有重載比較運算符,所以標(biāo)準(zhǔn)的hash_compare仿函數(shù)依舊無法工作。我們繼續(xù)重寫less仿函數(shù)。
                     
                      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(我認(rèn)為微軟的CString比標(biāo)準(zhǔn)庫的string設(shè)計要灑脫一些)更加復(fù)雜一些。很顯然,標(biāo)準(zhǔn)庫里不包含對于CString的支持,但CString卻重載了比較運算符(郁悶)。我們必須重寫hash_compare仿函數(shù)。值得一提的是,在Virtual Stdio 2003中,CString不再是MFC的成員,而成為ATL的成員,使用#include <atlstr.h>就可以使用。我沒有采用重寫hash_compare仿函數(shù)的策略,而僅僅是繼承了它,在模版庫中的繼承是沒有性能損耗的,而且能讓我偷一點懶。
                 首先重寫一個hash_value函數(shù):
                 
                      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仿函數(shù):
                 
                      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仿函數(shù)的引入,因為CString具備比較運算符,我們可以使用默認(rèn)的less仿函數(shù),在這里映射為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;
                      };
                 
                 讓我們自寫的類都派生自這里,有一個標(biāo)準(zhǔn),接下來定義我們的類:
                 
                      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值,也可以使用更復(fù)雜的函數(shù)計算所有域總的hash值
                                return(m_value ^ 0xdeadbeef 
                           }
                       
                           virtual bool operator < (const IHashable& val) const
                           {
                                return(m_value < ((CTest&)val).m_value);
                           }
                      };
                 
                 用這個類的對象做為hash索引準(zhǔn)備工作如下,因為接口中規(guī)定了比較運算符,所以這里可以使用標(biāo)準(zhǔn)的less仿函數(shù),所以這里忽略:
                 
                      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];
                 
                 可以看到正確的數(shù)字被返回。
                 
                 三、關(guān)于hash_map的思考:
                 
                 1、性能分析:采用了內(nèi)聯(lián)代碼和模版技術(shù)的hash_map在效率上應(yīng)該是非常優(yōu)秀的,但我們還需要注意如下幾點:
                 
                 * 經(jīng)過查看代碼,字符串索引會比簡單類型索引速度慢,自定義類型索引的性能則和我們選擇hash的內(nèi)容有很大關(guān)系,簡單為主,這是使用hash_map的基本原則。
                 * 可以通過重寫hash_compair仿函數(shù),更改里面關(guān)于桶數(shù)量的定義,如果取值合適,也可以得到更優(yōu)的性能。如果桶數(shù)量大于10,則牢記它應(yīng)該是一個質(zhì)數(shù)。
                 * 在自定義類型是,重載的等號(或者拷貝構(gòu)造)有可能成為性能瓶頸,使用對象指針最為索引將是一個好的想法,但這就必須重寫less仿函數(shù),理由同使用字符串指針作為索引。

            posted on 2008-01-12 18:31 小果子 閱讀(9692) 評論(0)  編輯 收藏 引用 所屬分類: 學(xué)習(xí)筆記
            久久精品中文字幕一区| 婷婷久久综合九色综合绿巨人| 久久这里有精品视频| 国产69精品久久久久99尤物| 久久无码人妻一区二区三区| 久久久SS麻豆欧美国产日韩| 久久久久亚洲AV综合波多野结衣 | 99久久国产亚洲高清观看2024 | 久久久综合香蕉尹人综合网| 国产精品视频久久| 国产精品久久久久9999高清| 国内精品久久久久久99蜜桃| 久久精品人人做人人爽97| 人妻无码αv中文字幕久久琪琪布| 亚洲AV无码久久精品蜜桃| 亚洲精品乱码久久久久久自慰| 精品久久人人爽天天玩人人妻| 久久综合色老色| 久久久久久精品成人免费图片| 久久久噜噜噜久久中文字幕色伊伊| 久久这里只有精品首页| 久久人人爽人人爽人人片AV麻烦| 色播久久人人爽人人爽人人片AV| 99久久做夜夜爱天天做精品| 久久精品无码一区二区WWW| 久久午夜无码鲁丝片秋霞| 亚洲va久久久噜噜噜久久| 久久精品国产亚洲AV大全| 韩国三级大全久久网站| 99久久人人爽亚洲精品美女| 青青草原综合久久大伊人导航| 亚洲国产日韩欧美综合久久| 亚洲AV无码久久精品成人| 欧美一区二区精品久久| 日本高清无卡码一区二区久久| 一级女性全黄久久生活片免费 | 亚洲第一极品精品无码久久| 久久精品一区二区国产| 久久精品国产黑森林| 亚洲色大成网站www久久九| 久久99国产精品久久99果冻传媒|