Основной вопрос при присвоении значения unordered_map

У меня есть unordered_map, который сохраняет int как ключ как указатель как значение. Мне нужно проверить наличие ключа. Если ключ недоступен, мне нужно вставить ключ и значение. Какой из них лучший подход?

Спасибо.

unordered_map<int, classA*>testMap;
classA* ptr = testMap[1];
if(ptr == NULL)
   testMap[1] = new classA;


OR

unordered_map<int, classA*>::iterator it = testMap.find(1);
if(it == testMap.end())
{
  testMap.insert(make_pair(1, new classA));
}

Ответ 1

Ни один из методов не является хорошим, потому что оба используют два запроса на карте, где этого было бы достаточно.

Лучшим методом является получение ссылки на элемент, и если эта ссылка является нулевым указателем, назначьте ей:

classA*& ptr = testMap[1];
if (ptr == 0)
    ptr = new classA;

Это работает, потому что запрос несуществующего элемента на карте автоматически вставляет его (построено по умолчанию, поэтому будет вставлен нулевой указатель), а operator[] возвращает ссылку на этот элемент (будь он вновь создан или уже существует).

Но обратите внимание, что семантика между этим методом (или вашим первым методом) и вашим вторым методом тонко отличается: ваш второй метод только вставляет элемент, если ключ не существует на карте. Мой метод (и ваш первый метод) также создает новый элемент, если ключ уже существует, но его значение было нулевым указателем.

Ответ 2

Второй - лучший подход. В первом случае, когда вы выполняете classA* ptr = testMap[1], вы создаете элемент в хеше со значением по умолчанию NULL.

Если вы изменили значение карты на что-то другое, кроме указателя (например, vector), тогда у вас может не быть подходящего значения по умолчанию для проверки. Кроме того, в будущем NULL может быть допустимым значением для вашей карты, поэтому ваш тест по умолчанию будет бессмысленным.

Ответ 3

Я бы сделал следующее:

typedef unordered_map<int, classA*>::iterator my_iterator;
std::pair<my_iterator, my_iterator> range = testMap.equal_range(1);
if (range.first == range.second) //element not in map
{
    testMap.insert(range.first, new classA);
}

РЕДАКТИРОВАТЬ: Благодаря лиджу за указание, что это бессмысленно. equal_range возвращает и [end, end] пару, если значение не найдено для underoded_map.

Ответ 4

С точки зрения эффективности я считаю, что они эквивалентны.

В первом случае, если запись еще не существует, вызов:

classA* ptr = testMap[1];

фактически создаст пустой элемент на карте, который вы затем заселяете во время инструкции if.

Второе добавит только запись на карту при вызове insert.

Итак, я полагаю, это сводится к тому, какой стиль вам подходит лучше!