2010-11-11 140 views
61

我想弄清楚爲什麼下面的代碼不工作,並且我假設它是使用char *作爲鍵類型的問題,但是我不確定我如何解決它或爲什麼發生。我使用的所有其他功能(在HL2 SDK中)使用char*,因此使用std::string會導致很多不必要的複雜情況。使用char *作爲std :: map中的鍵

std::map<char*, int> g_PlayerNames; 

int PlayerManager::CreateFakePlayer() 
{ 
    FakePlayer *player = new FakePlayer(); 
    int index = g_FakePlayers.AddToTail(player); 

    bool foundName = false; 

    // Iterate through Player Names and find an Unused one 
    for(std::map<char*,int>::iterator it = g_PlayerNames.begin(); it != g_PlayerNames.end(); ++it) 
    { 
     if(it->second == NAME_AVAILABLE) 
     { 
      // We found an Available Name. Mark as Unavailable and move it to the end of the list 
      foundName = true; 
      g_FakePlayers.Element(index)->name = it->first; 

      g_PlayerNames.insert(std::pair<char*, int>(it->first, NAME_UNAVAILABLE)); 
      g_PlayerNames.erase(it); // Remove name since we added it to the end of the list 

      break; 
     } 
    } 

    // If we can't find a usable name, just user 'player' 
    if(!foundName) 
    { 
     g_FakePlayers.Element(index)->name = "player"; 
    } 

    g_FakePlayers.Element(index)->connectTime = time(NULL); 
    g_FakePlayers.Element(index)->score = 0; 

    return index; 
} 
+10

有時候做正確的事情一開始就會受傷。改變你的代碼使用'std:string'一次,然後開心。 – 2010-11-11 18:06:42

+1

有什麼樣的併發症?有一個從char *到std :: string的隱式轉換。 – tenfour 2010-11-11 18:07:50

+0

您不得使用'char *'作爲映射鍵。請參閱[我的答案](http://stackoverflow.com/questions/4157687/using-char-as-a-key-in-stdmap/4157811#4157811)爲什麼。 – sbi 2010-11-11 18:17:42

回答

108

你需要給映射賦予一個比較函子,否則它會比較char *指針而不是字符串。一般情況下,任何時候你都希望你的map key是一個指針。

即。

struct cmp_str 
{ 
    bool operator()(char const *a, char const *b) 
    { 
     return std::strcmp(a, b) < 0; 
    } 
}; 

map<char *, int, cmp_str> BlahBlah; 

編輯:Acutally無視我的編輯,仿函數更容易使用。

+2

實際上他可以通過'&std :: strcmp'作爲第三個模板參數 – 2010-11-11 18:08:41

+20

否,'strcmp'返回一個正整數,零整數或負整數。地圖仿函數需要返回true,否則返回false。 – aschepler 2010-11-11 18:11:30

+4

@Armen:我不認爲它有效,因爲第三個模板參數需要類似於'f(a,b)= a b, 0 else)'。 – kennytm 2010-11-11 18:12:12

41

不能使用char*除非你是絕對100%相信您一定會用完全相同的指針,而不是字符串來訪問地圖。

例子:

char *s1; // pointing to a string "hello" stored memory location #12 
char *s2; // pointing to a string "hello" stored memory location #20 

如果訪問地圖s1你會得到一個不同的位置與s2訪問它。

+3

非常好的解釋。 – 2010-11-11 18:09:44

8

您在比較是否使用char *來使用字符串。他們不一樣。

A char *是一個指向字符的指針。最終,它是一個整數類型,其值被解釋爲char的有效地址。

字符串是一個字符串。

容器能夠正常工作,但作爲密鑰對的容器,其密鑰爲char *,值爲int

+1

指針不需要是長整數。有一些平臺(如win64,如果你曾經聽說過:-)),其中一個長整數比一個指針小,我相信也有更多的晦澀難懂的平臺,其中指針和整數被加載到不同的寄存器中,其他方法。 C++只要求指針可以轉換成一些整數類型並返回;請注意,這並不意味着您可以將任何足夠小的整數轉換爲指針,而只是轉換指針所獲得的整數。 – 2012-04-16 14:00:47

+0

@ChristopherCreutzig,謝謝你的評論。我相應地編輯了我的答案。 – 2012-04-16 14:14:35

21

兩個C風格的字符串可以有相同的內容,但在不同的地址。而map比較指針,而不是內容。

轉換爲std::map<std::string, int>的成本可能不如您想象的那麼多。

但是,如果你確實需要使用const char*爲映射鍵,嘗試:

#include <functional> 
#include <cstring> 
struct StrCompare : public std::binary_function<const char*, const char*, bool> { 
public: 
    bool operator() (const char* str1, const char* str2) const 
    { return std::strcmp(str1, str2) < 0; } 
}; 

typedef std::map<const char*, int, StrCompare> NameMap; 
NameMap g_PlayerNames; 
+0

感謝您的信息。根據我的經驗,我強烈建議轉換爲std :: string。 – user2867288 2014-07-27 02:17:31

-5

有沒有問題,只要它支持比較(<>==)和分配使用任何密鑰類型。

應該提到的一點 - 考慮到您正在使用模板類。由於結果編譯器將爲char*int*生成兩個不同的實例。而兩者的實際代碼實際上是相同的。

因此 - 我會考慮使用void*作爲關鍵類型,然後根據需要進行轉換。 這是我的意見。

+6

他們的鑰匙只需要支持'<'。但它需要以有用的方式實施。這不是用指針。現代編譯器將摺疊相同的模板實例。 (我知道VC肯定會這樣做)。我絕不會使用'void *',除非測量顯示這可以解決很多問題。 __Abandoning類型安全不應該過早地完成.__ – sbi 2010-11-11 18:20:16

8

你可以得到它與std::map<const char*, int>工作,但不得使用非const指針(注意,重點增加const),因爲你不能改變這些字符串,而地圖是指他們作爲密鑰。 (雖然地圖保護,使它們const它的鑰匙,這隻會constify的指針,不是指向字符串)。

但是你爲什麼不乾脆用std::map<std::string, int>?它可以在沒有頭痛的情況下使用。

2

正如其他人所說的,在這種情況下,您應該使用std :: string而不是char *,儘管如果這是真正需要的指針作爲關鍵字原則上沒有任何錯誤。

我覺得這段代碼不起作用的另一個原因是因爲一旦你在地圖中找到了一個可用的條目,你試圖用相同的鍵(char *)將它重新插入到地圖中。由於該鍵已經存在於您的地圖中,插入將失敗。 map :: insert()的標準定義了這種行爲...如果鍵值存在,插入失敗並且映射的值保持不變。然後無論如何它都會被刪除。您需要先刪除它,然後重新插入。

即使您將char *更改爲std :: string,此問題仍然存在。

我知道這個帖子已經很老了,你現在已經修復了這一切,但是我沒有看到有人提出這個觀點,所以爲了我未來的觀衆回答。

0

當我嘗試在多個源文件中查找元素時,很難將char *用作映射關鍵字。在插入元素的同一源文件中進行所有訪問/查找時,它工作正常。但是,當我嘗試使用另一個文件中的查找來訪問該元素時,我無法獲取絕對在地圖內的元素。

原來是因爲Plabo指出,指針(每個編譯單元都有自己的常量char *)在另一個cpp文件中訪問時並不相同。

相關問題