std::map сравнить функцию и NULL

Я написал функцию сравнения для std::map, поэтому у меня могут быть пользовательские типы ключей.

std::map<GGString *, GGObject *, GGDictionaryMapCompare> _map;

...

class GGDictionaryMapCompare
{
public:
    bool operator()(GGString * lhs, GGString * rhs)
    {
        return strcmp(lhs->str(), rhs->str()) < 0;
    }
};

Код, который добавляет элементы:

GGObject *GGDictionary::addKeyObject(GGString *theKey, GGObject *theObject)
{
    if (theKey == NULL || theObject == NULL)
        return NULL;

    _map.insert(std::pair<GGString *, GGObject *>(theKey, theObject));

    return theObject;
}

Код, вызывающий сбой:

GGObject *GGDictionary::objectForKey(GGString *theKey)
{
    if (theKey == NULL)
        return NULL;

    std::map<GGString *, GGObject *, GGDictionaryMapCompare>::iterator ii = _map.find(theKey);
    if (ii == _map.end())
    return NULL;

    return GGAutoRelease(ii->second);
}

Трассировки стека:

#0  0x00009f15 in GGString::str()
#1  0x0004a4c4 in GGDictionaryMapCompare::operator()(GGString*, GGString*)
#2  0x0004a3d3 in std::_Rb_tree<GGString*, std::pair<GGString* const, GGObject*>, std::_Select1st<std::pair<GGString* const, GGObject*> >, GGDictionaryMapCompare, std::allocator<std::pair<GGString* const, GGObject*> > >::find(GGString* const&)
#3  0x00049b04 in std::map<GGString*, GGObject*, GGDictionaryMapCompare, std::allocator<std::pair<GGString* const, GGObject*> > >::find(GGString* const&)
#4  0x00048ec9 in GGDictionary::objectForKey(GGString*)

Проблема в том, что lhs приходит в NULL. Я никогда не вставляю NULL в карту, поэтому этого не должно происходить. Есть идеи, почему? Или я просто неправильно выполняю функцию сравнения? Я могу защититься от получения NULL, но, похоже, что-то не так, и я не хочу лечить симптом, а не проблему.

Спасибо


person Roger Gilbrat    schedule 22.08.2013    source источник
comment
Покажите, где вы добавляете элементы.   -  person imreal    schedule 23.08.2013
comment
Не могли бы вы опубликовать код о том, как вы это используете и/или как вы добавляете элементы на карту? Я мог бы попытаться воспроизвести проблему. Я не вижу никаких очевидных проблем с этим кодом, поэтому, возможно, это что-то странное в реализации.   -  person Michael Oliver    schedule 23.08.2013
comment
Если GGString входит в NULL, это вставка NULL key, которую вы ищете, а НЕ NULL значение   -  person Mike Vine    schedule 23.08.2013
comment
Я добавил новый код, но вставка ключей или значений NULL защищена. Это единственная функция, которая вставляет ключи/значения.   -  person Roger Gilbrat    schedule 23.08.2013
comment
Вы не проверяете аргумент theKey на NULL. map::find будет использовать ваш компаратор.   -  person dhavenith    schedule 23.08.2013
comment
Просто добавил код для проверки того, что ключ является NULL, и это не решает проблему, все равно происходит сбой в том же месте.   -  person Roger Gilbrat    schedule 23.08.2013
comment
Что делает GGAutoRelease?   -  person user2093113    schedule 23.08.2013
comment
Вы уверены, что указатель действительно NULL, а не просто недействителен?   -  person Mark Ransom    schedule 23.08.2013
comment
Вы уверены, что не изменяете _map где-либо еще, например. с непреднамеренным _map[foo], где foo есть NULL?   -  person Nate Kohl    schedule 23.08.2013
comment
Да, я проверяю весь свой код, добавляю только одно место.   -  person Roger Gilbrat    schedule 23.08.2013
comment
GGString * обычно является признаком плохого дизайна. Существует очень мало случаев, когда полезно различать пустой контейнер и отсутствие контейнера. В качестве дополнительного преимущества у std::map<GGString, GGObject *> не было бы этих проблем.   -  person MSalters    schedule 23.08.2013


Ответы (3)


В этом коде:

GGObject *GGDictionary::objectForKey(GGString *theKey)
{
    std::map<GGString *, GGObject *, GGDictionaryMapCompare>::iterator ii = _map.find(theKey);
    if (ii == _map.end())
        return NULL;

    return GGAutoRelease(ii->second);
}

Вы не проверяете, является ли theKey NULL. Соответственно, когда компаратор вызывается для theKey и любого элемента map, вы будете разыменовывать NULL.

Чтобы исправить это, попробуйте добавить проверку NULL:

GGObject *GGDictionary::objectForKey(GGString *theKey)
{
    if (theKey == NULL) return NULL;

    std::map<GGString *, GGObject *, GGDictionaryMapCompare>::iterator ii = _map.find(theKey);
    if (ii == _map.end())
        return NULL;

    return GGAutoRelease(ii->second);
}

Надеюсь это поможет!

person templatetypedef    schedule 22.08.2013
comment
Добавление этого кода не решает проблему, все равно вылетает в том же месте. - person Roger Gilbrat; 23.08.2013
comment
@RogerGilbrat- Вы уверены, что передаете указатель NULL, а не указатель мусора? Можете ли вы сообщить нам, что такое плохой указатель? - person templatetypedef; 23.08.2013

Интересно, следует ли изменить функцию сравнения ключей примерно так:

bool operator()(const GGString *&lhs, const GGString *&rhs)
{
    if (lhs == NULL || rhs == NULL)
    {
       return false;
    }
    return strcmp(lhs->str(), rhs->str()) < 0;
}

В основном я думаю, что параметры должны быть постоянными ссылками, а также что функция должна защищать от разыменования указателей NULL.

person MorbidFuzzball    schedule 22.08.2013
comment
Это делает сравнения медленнее и, вероятно, лечит симптом. - person ypnos; 23.08.2013
comment
Поддержка null может быть хорошей идеей, но эта функция не дает строгого слабого упорядочения. Для любого значения a, comp(a, null) и comp(null, a) оба являются ложными, что означает, что все значения эквивалентны null, и, следовательно, все значения эквивалентны друг другу. - person Derek Ledbetter; 23.08.2013
comment
Вместо этого сделайте следующее: if (rhs == NULL) return false; if (lhs == NULL) return true; - person Derek Ledbetter; 23.08.2013

Вы уверены, что сбой происходит при доступе к NULL? Вы храните указатели на карте; Возможно ли, что вы удалили один из указателей после его сохранения на карте? Что-то вроде этого:

dict->addKeyObject( key1, obj1 );
delete key1; // now dict has a pointer to deleted key1
dict->addKeyObject( key2, obj2 ); // now dict will compare key2 to key1, causing crash
person Michael    schedule 22.08.2013