簡體   English   中英

C++ 以 char* 為鍵的 unordered_map

[英]C++ unordered_map with char* as key

嘗試使用以char*為鍵的容器unordered_map時,我感到筋疲力盡(在 Windows 上,我使用的是 VS 2010)。 我知道我必須為char*定義自己的比較 function ,它繼承自binary_function 以下是示例程序。

#include<unordered_map>
#include <iostream>
#include <string>
using namespace std;

template <class _Tp>  
struct my_equal_to : public binary_function<_Tp, _Tp, bool>  
{  
    bool operator()(const _Tp& __x, const _Tp& __y) const  
    { return strcmp( __x, __y ) == 0; }  
};

typedef unordered_map<char*, unsigned int, ::std::tr1::hash<char*>,  my_equal_to<char*> > my_unordered_map;
//typedef unordered_map<string, unsigned int > my_unordered_map;

my_unordered_map location_map;

int main(){
    char a[10] = "ab";
    location_map.insert(my_unordered_map::value_type(a, 10));
    char b[10] = "abc";
    location_map.insert(my_unordered_map::value_type(b, 20));

    char c[10] = "abc";
    location_map.insert(my_unordered_map::value_type(c, 20));

    printf("map size: %d\n", location_map.size());
    my_unordered_map::iterator it;
    if ((it = location_map.find("abc")) != location_map.end())
    {
        printf("found!\n");
    }

    return 0;
} 

我兩次插入相同的 C 字符串abc並查找它。 第二次插入應該會失敗,unordered_map 中將只有一個abc 然而,output 大小是 3。似乎比較 function 在這里不能正常工作。

另外, find function還有一個奇怪的結果,多次運行程序,結果竟然有變化! 有時會找到字符串abc ,而有時找不到abc

誰能幫我解決這個問題? 非常感激您的幫忙!

++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++

編輯:在我自己為char*定義了 hash function 之后,程序運行正常。 下面列出了完整的程序代碼。 謝謝你們。

#include<unordered_map>
#include <iostream>
using namespace std;

template <class _Tp>  
struct my_equal_to : public binary_function<_Tp, _Tp, bool>  
{  
    bool operator()(const _Tp& __x, const _Tp& __y) const  
    { return strcmp( __x, __y ) == 0; }  
};


struct Hash_Func{
    //BKDR hash algorithm
    int operator()(char * str)const
    {
        int seed = 131;//31  131 1313 13131131313 etc//
        int hash = 0;
        while(*str)
        {
            hash = (hash * seed) + (*str);
            str ++;
        }

        return hash & (0x7FFFFFFF);
    }
};

typedef unordered_map<char*, unsigned int, Hash_Func,  my_equal_to<char*> > my_unordered_map;


int main(){
    my_unordered_map location_map;

    char a[10] = "ab";
    location_map.insert(my_unordered_map::value_type(a, 10));
    char b[10] = "abc";
    location_map.insert(my_unordered_map::value_type(b, 20));

    char c[10] = "abc";
    location_map.insert(my_unordered_map::value_type(c, 20));

    printf("map size: %d\n", location_map.size());
    my_unordered_map::iterator it;
    if ((it = location_map.find("abc")) != location_map.end())
    {
        printf("found!\n");
    }

    return 0;
}

注意:使用char * 作為 unordered_map 或其他 STL 容器的鍵類型可能是危險的,一個安全的方法(似乎是唯一的方法)是:在 main function, newmalloc一個塊(例如 c 字符串的數組) 在堆上並用 c 個字符串填充它。 將這 c 個字符串插入到 unordered_map 中。 memory 的分配塊在 main function 的末尾被釋放(通過deletefree )。

比較器很好(盡管傳遞nullptr是未定義的,可能應該處理)

散列, ::std::tr1::hash<char*>散列指針,因此每個“abc”(通常)在另一個桶中

您需要編寫自己的哈希函數,以確保哈希(“abc”)始終給出相同的答案

現在 - 性能會很糟糕,但是哈希值會返回0 - 你應該看到第二個“abc”匹配第一個

根據評論 - 使用std::string簡化內存管理並提供庫支持的哈希和比較器,因此只需std::unordered_map<std::string, X> 這也意味着在刪除unordered map將為您釋放所有字符串。 您甚至可以安全地從堆棧上的char數組中實例化std::strings

如果您仍然想使用char *那么您仍然需要自己的比較器和散列,但是您可以使用std::shared_ptr為您管理內存(不要使用堆棧實例 - 執行new char[] )然后您將有一個std::unordered_map<shared_ptr<char *>, X>但后來沒有內存泄漏的復雜情況。

如果你仍然想使用char *那么你就是在正確的軌道上,但是使用像purify或valgrind這樣的內存泄漏工具來確保你真正控制所有的內存管理是很重要的。 (這通常是任何項目的好主意)

最后,應避免全局變量。

像上面那樣使用char指針作為鍵幾乎肯定不是你想要做的。

STL容器處理存儲的值,在std::unordered_map<char *, unsigned int, ...> ,你正在處理指向c字符串的指針,這些字符串甚至可能在后續的插入/刪除檢查中都沒有。

請注意, my_unordered_map是一個全局變量,但您嘗試插入本地char數組a,b和c。 當插入的c字符串超出范圍時,你期望你的比較函數my_equal_to()strcmp() (您突然有鍵指向隨機垃圾,可以與新插入的未來值進行比較。)

重要的是,STL映射鍵是可復制的值,不能通過外部程序行為改變其含義。 您幾乎肯定會使用std::string或類似的鍵來表示您的鍵值,即使它們的構造乍一看對您來說也很浪費。

以下內容與您打算在上面工作的內容完全一致,並且非常安全:

#include <unordered_map>
#include <iostream>
#include <string>

using namespace std;

// STL containers use copy semantics, so don't use pointers for keys!!
typedef unordered_map<std::string, unsigned int> my_unordered_map;

my_unordered_map location_map;

int main() {
    char a[10] = "ab";
    location_map.insert(my_unordered_map::value_type(a, 10));

    char b[10] = "abc";
    location_map.insert(my_unordered_map::value_type(b, 20));

    char c[10] = "abc";
    location_map.insert(my_unordered_map::value_type(c, 20));

    cout << "map size: " << location_map.size() << endl;

    my_unordered_map::iterator it;
    if ((it = location_map.find("abc")) != location_map.end()) {
        cout << "found \"" << it->first << "\": " << it->second << endl;
    }

    return 0;
}

(現代 C++ 的答案,對於仍然絆倒這個問題的人)

現在,如果您使用C++17或更高版本,則可以使用std::string_view作為 unordered_map 中的鍵。

std::string_view 只保留對原始 char* 數據的引用而不是復制它,當您確定原始 char* 數據比 unordered_map 長時,允許您避免復制。

然而,與 char* 不同的是,std::string_view 實現了各種方法和運算符,如 std::hash,使其在更多地方有用。

std::unordered_map<std::string_view, unsigned int> my_map;
my_map["some literal"] = 123;
printf("%d\n", my_map["some literal"]);

在上面的代碼中,我只把字符串字面量放在map中,這是安全的。 將其他東西放入帶有 string_view 鍵的 map 時要小心 - 你有責任確保它們不會在地圖之前被摧毀!

當你定義諸如“abc”之類的東西時,它會被賦予一個const char *。 每次在程序中編寫“abc”時,都會有一個新的內存。 所以:

const char* x = "abc";
const char* y = "abc";
return x==y;

將永遠返回false,因為每次“abc”被寫入時都會記錄新的內存(抱歉,如果我聽起來有點重復)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM