web-dev-qa-db-ja.com

std :: mapでchar *をキーとして使用する

私は次のコードが機能しない理由を理解しようとしていますが、キータイプとしてchar *を使用することの問題であると仮定していますが、どうすれば解決できるのか、なぜ発生しているのか分かりません。 (HL2 SDKで)私が使用する他のすべての関数は、char*だからstd::stringは多くの不必要な合併症を引き起こすでしょう。

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;
}
74
Josh Renwald

比較ファンクターをマップに与える必要があります。そうしないと、ポインターが比較されます。ポインターが指すヌル終了ストリングではありません。一般的に、これはマップキーをポインターにしたい場合に使用します。

例えば:

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

map<char *, int, cmp_str> BlahBlah;
127
GWW

char*は、文字列ではなく正確に同じポインターを使用してマップに確実にアクセスすることが確実でない限り、使用できません。

例:

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

s1を使用してマップにアクセスすると、s2を使用してマップにアクセスする場合とは異なる場所を取得します。

44

2つの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;
22
aschepler

char *を使用して文字列を使用して比較しています。それらは同じではありません。

char *は、charへのポインターです。最終的には、値はcharの有効なアドレスとして解釈される整数型です。

文字列は文字列です。

コンテナは正常に動作しますが、キーがchar *で値がintであるペアのコンテナとして機能します。

8
Daniel Daranas

std::map<const char*, int>で動作するようにできますが、マップ中にこれらの文字列を変更してはならないため、const以外のポインターを使用しないでください(キーにconstが追加されていることに注意してください)それらをキーと呼びます。 (マップはconstにすることでキーを保護しますが、これはpointerのみを制限し、それが指す文字列ではありません。)

しかし、単にstd::map<std::string, int>を使用しないのはなぜですか?それは頭痛なしで箱から出して動作します。

8
sbi

他の人が言うように、おそらくこの場合、char *の代わりにstd :: stringを使用する必要がありますが、それが本当に必要な場合は、原則としてキーとしてのポインターには何の問題もありません。

このコードが機能しないもう1つの理由は、マップ内で使用可能なエントリを見つけたら、同じキー(char *)を使用してマップに再挿入しようとするためです。そのキーは既にマップに存在するため、挿入は失敗します。 map :: insert()の標準では、この動作を定義しています...キー値が存在する場合、挿入は失敗し、マップされた値は変更されません。とにかく削除されます。まず削除してから再挿入する必要があります。

Char *をstd :: stringに変更しても、この問題は残ります。

私はこのスレッドがかなり古いことを知っており、あなたはこれをすべて修正しましたが、私が答えている将来の視聴者のために、この点を指摘している人はいませんでした。

2
Toby Mitchell

複数のソースファイルで要素を見つけようとすると、char *をマップキーとして使用するのに苦労しました。要素が挿入されている同じソースファイル内のすべてのアクセス/検索が正常に機能します。ただし、別のファイルでfindを使用して要素にアクセスしようとすると、間違いなくマップ内にある要素を取得できません。

Plabo が指摘されているように、別のcppファイルでアクセスされた場合、ポインター(コンパイル単位ごとに定数char *がある)はまったく同じではないことがわかります。

0
Jie Xu