Безопасно ли получить объект в std :: map по ссылке? - PullRequest
16 голосов
/ 29 декабря 2010

У меня есть такая карта

map<int,object> objmap;
object& obj = objmap.find(num)->second;
object& obj2 = objmap[num];

Какие бы изменения я ни сделал в объекте, они должны быть отражены на карте. То же самое нельзя сделать в векторе, так как он меняет расположение объектов, когда ему нужно больше места. Безопасно ли делать это в std :: map? и это целесообразно? Вторая версия выдает ошибку, поскольку у моего объекта нет пустого конструктора. Если я объявлю, что пустой конструктор ничего не делает, две строки будут работать одинаково?

Ответы [ 5 ]

22 голосов
/ 29 декабря 2010

Пока рассматриваемый объект не удален с карты, тогда да, это безопасно. Вставленные в карту объекты не перемещаются, даже если другие элементы добавлены или удалены.

object& obj = objmap.find(num)->second;

Это потенциально опасно, если вы не уверены, что элемент с ключом num действительно существует на карте. Если вы не уверены, вы можете использовать перегрузку insert, которая возвращает iterator и bool, которые указывают, был ли добавлен новый элемент или элемент с данным ключом уже присутствовал на карте.

1010 * Е.Г. *

object& obj = objmap.insert( std::make_pair(num, object(arg1, arg2, argN)) ).first->second;
11 голосов
/ 29 декабря 2010

Это безопасно, если элемент не удален с карты.

Однако вторая строка не вполне безопасна:

object& obj = objmap.find(num)->second;

Если на карте нет элементов с ключом num, find вернет objmap.end(). Эта возможность должна быть проверена перед разыменованием возвращаемого итератора:

const std::map<int, object>::iterator it = objmap.find(num);
if (it != objmap.end())
{
    object& obj = it->second;
    /* ... */
}

Теперь, если цель на самом деле не найти , а реально вставить, вызов operator[] вполне возможен (хотя, как вы уже заметили, для получения конструктора без параметров требуется значение) , Но вы должны понимать, что это две совершенно разные вещи:

  • find только находит: если ключ не найден, ничего не вставляется и возвращается итератор end
  • operator[] всегда возвращает ссылку на значение на карте: при отсутствии ключа происходит вставка (для созданного по умолчанию значения: таким образом, требование конструктора)
4 голосов
/ 29 декабря 2010

Если ваш вопрос заключается в том, делает ли std::map недействительными свои итераторы в своих мутирующих функциях, тогда ответ отрицательный. Стандарт гарантирует, что std::map не делает недействительными его итераторы.

1 голос
/ 29 декабря 2010

Если вы хотите, чтобы изменения, внесенные в объект на карте, были отражены, вы, вероятно, захотите изменить свою карту, чтобы хранить указатели на объекты вместо самих объектов. Ссылки могут работать до тех пор, пока вы не сделаете ничего с объектом между ссылками, что сделает его недействительным.

Например, следующий код не работает с использованием ссылок:

object& obj = objmap.find(num)->second;
objmap.erase(objmap.find(num)); // should check for objmap.end() - left out for simplicity
obj.DoSomething(); // this object has been destroyed, so the reference is invalid
0 голосов
/ 29 декабря 2010

Выполнение того, что вы делаете, является распространенным способом реализации кэширования.

Если элемент уже существует, оператор [] возвращает элемент. Если его там нет, он создаст «пробел» с конструктором по умолчанию, и вы можете написать в него для дальнейшего использования.

Конечно, обычно используется shared_ptr в качестве значения_типа, который при создании будет иметь «пустое» значение. В этом случае вам нужно получить shared_ptr по ссылке, чтобы вы могли вызывать reset () для него.

Как и в случае любых коллекций / кэширования и т. Д., Вы должны остерегаться проблем с безопасностью потоков, если это делается в многопоточном приложении.

Я не уверен, что вы хотели знать, можно ли где-то сохранить ссылку (или указатель на нее) и ожидать, что она будет действительной позже, когда на карту будут добавлены другие элементы, и да, так и будет.

...