std :: map에서 char *를 키로 사용


81

다음 코드가 작동하지 않는 이유를 알아 내려고 노력하고 있으며 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;
}

14
때로는 옳은 일을하는 것이 처음에는 아파요. std:string한 번만 사용하도록 코드를 변경 하고 나중에 행복해 지세요.
Björn Pollex 2010

1
어떤 종류의 합병증? char *에서 std :: string으로의 암시 적 변환이 있습니다.
tenfour 2010

1
char*지도 키로 사용해서는 안됩니다 . 왜 내 대답을 참조하십시오 .
sbi

이것은를 사용 하지 않아 발생하는 불필요한 합병증 인 것 같습니다 std::string.
Pedro d' Aquino

이진 키를 사용하기 위해 맵이 키가 다른 값보다 '작은'값을 갖는 대신 키가 같은지 알 필요가 없을까요?
CodeMinion 2013-08-12

답변:


140

비교 펑터를 맵에 제공해야합니다. 그렇지 않으면 포인터가 가리키는 null로 끝나는 문자열이 아닌 포인터를 비교합니다. 일반적으로지도 키를 포인터로 사용하려는 경우입니다.

예를 들면 :

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

map<char *, int, cmp_str> BlahBlah;

2
실제로 그는 &std::strcmp세 번째 템플릿 매개 변수로 전달할 수 있습니다
Armen Tsirunyan 2010

23
아니요, strcmp양수, 0 또는 음의 정수를 반환합니다. 맵 펑 터는보다 작 으면 true를 반환하고 그렇지 않으면 false를 반환해야합니다.
aschepler 2010

4
@Armen : 3 번째 템플릿 매개 변수가 f(a,b) = a<b, 아니라 같은 것을 예상하기 때문에 작동하지 않는다고 생각합니다 f(a,b) = (-1 if a<b, 1 if a>b, 0 else).
kennytm 2010

28
아, 미안 해요. 게시하기 전에 생각하지 않았어요. 댓글이 거기에 머물러서 내 조상에게 수치를 가져다 주도록하자 :)
Armen Tsirunyan 2010

2
내가 테스트 한대로 bool operator () (char const * a, char const * b) 뒤에 const와 함께 작동해야합니다. bool operator () (char const * a, char const * b) const {blabla
ethanjyx

45

문자열이 아닌 똑같은 포인터로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.


5
수락 된 답변에 설명 된대로 자신의 비교자를 정의하지 않는 한.
Lukas Kalinski

23

두 개의 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;

정보에 대해서 감사드립니다. 내 경험을 바탕으로 std :: string으로 변환하는 것이 좋습니다.
user2867288

8

으로 작업 할 수는 std::map<const char*, int>있지만 const포인터가 아닌 것을 사용해서는 안됩니다 ( const키 에 대해 추가 된 것에 주의하십시오 ). 맵에서 키로 참조하는 동안 해당 문자열을 변경해서는 안되기 때문입니다. (지도는 키를 만들어 키를 보호하지만 const, 이것은 포인터가 가리키는 문자열이 아닌 포인터 만 구성합니다 .)

하지만 단순히 사용하지 않는 이유는 std::map<std::string, int>무엇입니까? 그것은 두통없이 즉시 작동합니다.


8

a char *를 사용하여 문자열을 사용하여 비교하고 있습니다. 그들은 동일하지 않습니다.

A char *는 char에 대한 포인터입니다. 궁극적으로 값이에 대한 유효한 주소로 해석되는 정수 유형입니다 char.

문자열은 문자열입니다.

컨테이너는 올바르게 작동하지만 키가 a char *이고 값이 int.


1
포인터가 긴 정수일 필요는 없습니다. 긴 정수가 포인터보다 작은 플랫폼 (예 : win64)이 있으며 포인터와 정수가 다른 레지스터에로드되고 다르게 처리되는 모호한 플랫폼도 더 많다고 생각합니다. 다른 방법들. C ++에서는 포인터를 정수 형식으로 변환 할 수만 있으면됩니다. 이것은 충분히 작은 정수를 포인터로 캐스트 할 수 있다는 것을 의미하지 않으며 포인터를 변환하여 얻은 정수만 캐스트 할 수 있습니다.
Christopher Creutzig 2012

@ChristopherCreutzig, 귀하의 의견에 감사드립니다. 그에 따라 내 대답을 편집했습니다.
Daniel Daranas

2

다른 사람들이 말했듯이, 실제로 필요한 경우 포인터를 키로 사용하는 원칙적으로 잘못된 것이 없지만이 경우에는 char * 대신 std :: string을 사용해야합니다.

이 코드가 작동하지 않는 또 다른 이유는 맵에서 사용 가능한 항목을 찾으면 동일한 키 (char *)를 사용하여 맵에 다시 삽입하려고하기 때문이라고 생각합니다. 해당 키가 이미 맵에 있으므로 삽입이 실패합니다. map :: insert ()의 표준은이 동작을 정의합니다 ... 키 값이 존재하면 삽입이 실패하고 매핑 된 값은 변경되지 않습니다. 그런 다음 어쨌든 삭제됩니다. 먼저 삭제 한 다음 다시 삽입해야합니다.

char *를 std :: string으로 변경하더라도이 문제는 남아 있습니다.

이 스레드가 꽤 오래되었다는 것을 알고 있으며 지금까지 모든 문제를 해결했지만 아무도이 문제를 제기하지 않았으므로 앞으로의 시청자를 위해 대답하겠습니다.


0

여러 소스 파일에서 요소를 찾으려고 할 때 char *를 맵 키로 사용하는 데 어려움을 겪었습니다. 요소가 삽입 된 동일한 소스 파일 내에서 모든 액세스 / 찾기가 제대로 작동합니다. 그러나 다른 파일에서 찾기를 사용하여 요소에 액세스하려고하면 확실히 맵 내부에있는 요소를 가져올 수 없습니다.

그 이유는 Plabo가 지적한 것처럼 포인터 (모든 컴파일 단위에는 자체 상수 char *가 있음)가 다른 cpp 파일에서 액세스 될 때 전혀 동일하지 않기 때문입니다.


-5

이 비교 (지원하는만큼 어떤 키 유형을 사용하는 아무 문제가 없다 <, >, ==) 및 할당.

언급해야 할 한 가지 사항- 템플릿 클래스를 사용하고 있다는 점을 고려하십시오 . 결과적으로 컴파일러는 char*및에 대해 두 가지 다른 인스턴스화를 생성합니다 int*. 두 가지 의 실제 코드 는 사실상 동일합니다.

따라서을 void*키 유형으로 사용한 다음 필요에 따라 캐스팅하는 것을 고려 합니다. 제 의견입니다.


8
키는 <. 하지만 도움이되는 방식으로 구현해야합니다. 포인터가 아닙니다. 최신 컴파일러는 동일한 템플릿 인스턴스를 접습니다. (VC가이 작업을 수행한다는 것을 확실히 알고 있습니다.)void* 측정이 많은 문제를 해결하기 위해 이것을 보여주지 않는 한 하지 않을 것입니다. 형식 안전성을 포기하는 것은 너무 일찍 이루어져서는 안됩니다.
sbi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.