Posted on 2011-10-15 08:00
S.l.e!ep.¢% 閱讀(801)
評論(0) 編輯 收藏 引用 所屬分類:
C++
前段時間整理臺歷的邏輯數據時考慮到需要去兼容已發布的照片書數據,很多地方做了兼容處理,比如下面這個例子:
????? 在很多時候,我們常常會使用map儲存一些需要經常查詢數據結構,而map的key在一開始設計的時候往往不多加思索,很容易就認為它就是個純ID,比如一個數字,或者一個字符串。當隨著產品變得復雜,或者不同數據結構的添加,這個假設往往不堪一擊—-比如在網游中的游戲ID竟然被認為是唯一的Key,而實際上如果涉及到服務器合并,這個ID也就不那么唯一了。很顯然需要解決這個問題就是擴充原來的Key:要么還是將附加信息直接和原先的Key合并,要么將它倆合并到一個數據結構中—-而后者會引來一個小問題:
?????? 如果我把這個Key儲存在某個有序容器中,我就需要重新告訴他各個key是按照什么順序排序的。
????? 在做照片書的時候,服務器告訴你:哦,我們的模板ID就是唯一標識了,你拿這個ID就可以得到我這邊關于這個作品的所有數據。而到了做臺歷的時候,服務器換了種說法:不好意思,為了充分利用資源,我們很多臺歷用得是同一個模板,你還要告訴我這個臺歷的產品ID。于是shit就這么發生了。
?????? 可憐的Key就從string templateID,變成了struct TemplateKey{string m_sTemplateID;string m_sProductID};嗯,這不是什么問題,比較煩的就是如果你用map來儲存,那你就得自己重寫排序比較函數,于是你有了如下的代碼:
1 | bool operator<( const TemplateSearchKey &searchKey) const |
3 | ???????? return m_sProductID < searchKey.m_sProductID && |
4 | ??????????????? m_sTemplateID < searchKey.m_sTemplateID; |
可惜的是這樣的代碼大錯特錯,試試下面的代碼
07 | struct TemplateSearchKey |
09 | ???? string m_sProductID; |
10 | ???? string m_sTemplateID; |
11 | ???? TemplateSearchKey( const string& sTemplateID, const string& sProductID) |
13 | ???????? m_sTemplateID??? = sTemplateID; |
14 | ???????? m_sProductID??? = sProductID; |
17 | ???? bool operator<( const TemplateSearchKey &searchKey) const |
19 | ???????? return m_sProductID < searchKey.m_sProductID && |
20 | ??????????????? m_sTemplateID < searchKey.m_sTemplateID; |
26 | ???? map<TemplateSearchKey,string> testMap; |
27 | ???? TemplateSearchKey key( "" , "2" ); |
28 | ???? TemplateSearchKey key1( "" , "1" ); |
29 | ???? TemplateSearchKey key2( "" , "3" ); |
30 | ???? testMap[key] = "a" ; |
31 | ???? testMap[key1] = "b" ; |
32 | ???? testMap[key2] = "c" ; |
33 | ???? cout<<testMap[key]<<endl; |
做調整:
1 | bool operator<( const TemplateSearchKey &searchKey) const |
4 | ??????? return m_sProductID < searchKey.m_sProductID || |
5 | ?????????????? m_sTemplateID < searchKey.m_sTemplateID; |
?
而這個連編譯都是不通過的……出一個ASSERT告訴你你的<符號是無效的。
01 | template < class _Pr, class _Ty1, class _Ty2> inline |
02 | bool __CLRCALL_OR_CDECL _Debug_lt_pred(_Pr _Pred, const _Ty1& _Left, const _Ty2& _Right, |
04 | ???? const wchar_t *_Where, unsigned int _Line) |
08 | if (!_Pred(_Left, _Right)) |
12 | else if (_Pred(_Right, _Left)) |
14 | ???? _DEBUG_ERROR2( "invalid operator<" , _Where, _Line); |
于是趕緊琢磨了下,改了:
01 | bool operator<( const TemplateSearchKey &searchKey) const |
04 | ???? if (m_sProductID == searchKey.m_sProductID) |
06 | ???????? return m_sTemplateID < searchKey.m_sTemplateID; |
10 | ???????? return m_sProductID < searchKey.m_sProductID; |
OK,編譯通過,運行沒問題。可問題也來了,如果有3個成員呢?4個5個呢?一個比較好的方法就是直接把每個成員hash成一個數值,然后各個數值鏈起來,通過比較最后這個值來確定大小。于是就想了,為啥不學JAVA直接把排序功能切成兩部分—-兩個函數hasCode和equals,一個用來確定東西怎么排序,而另外一個用來確定是不是同一個東西。而STL這種做法雖然簡練卻晦澀,需要用戶自己去考慮我寫完的排序函數是不是符合傳說中的排序三定律(有時候即使符合也不能完全反應用戶原意)。