Безопасно ли получить объект в std:: map по ссылке?
У меня есть карта, подобная этой
map<int,object> objmap;
object& obj = objmap.find(num)->second;
object& obj2 = objmap[num];
Все изменения, которые я делаю в объекте, должны отражаться на карте. Подобная вещь не может быть выполнена в векторе, поскольку она изменяет расположение объектов, когда требуется больше места. Безопасно ли это делать на std:: map? и это целесообразно?
Вторая версия дает ошибку, так как у моего объекта нет пустого конструктора. Если я объявлю, что пустой конструктор ничего не делает, две линии будут работать одинаково?
Ответы
Ответ 1
Пока объект, о котором идет речь, не удаляется с карты, тогда да, это безопасно. Когда вставляемые в объекты карты не перемещаются, даже если другие элементы добавлены или удалены.
object& obj = objmap.find(num)->second;
Это потенциально опасно, если вы не уверены, что элемент с ключом num
действительно существует на карте. Если вы не уверены, вы можете использовать перегрузку insert
, которая возвращает iterator
и a bool
, которая указывает, был ли вставлен новый элемент, или элемент с данным ключом уже присутствовал на карте.
например.
object& obj = objmap.insert( std::make_pair(num, object(arg1, arg2, argN)) ).first->second;
Ответ 2
Это безопасно, пока элемент не удаляется с карты.
Однако вторая строка небезопасна:
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
только находит: если ключ не найден, ничего не встает и возвращается -
operator[]
всегда возвращает ссылку на значение на карте: отсутствовал ли ключ, вставлена вставка (для стандартного сконструированного значения: таким образом, требование конструктора)
Ответ 3
Если ваш вопрос заключается в том, что std::map
делает недействительными его итераторы в своих мутирующих функциях, тогда ответ отрицательный. Стандартные гарантии std::map
не делают недействительными его итераторы.
Ответ 4
Если вы хотите, чтобы изменения, внесенные в объект на карте, отражались, вы, скорее всего, захотите изменить свою карту, чтобы сохранить указатели на объекты вместо самих объектов. Ссылки могут работать до тех пор, пока вы ничего не делаете с объектом между ссылкой, которая лишает его права.
Например, следующий код будет разбит с использованием ссылок:
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
Ответ 5
Выполнение того, что вы делаете, является обычным способом реализации кэширования.
Если элемент уже существует, оператор [] возвращает элемент. Если его нет, он создаст "пустой" с конструктором по умолчанию, и вы сможете записать его для последующего использования.
Конечно, обычно используется shared_ptr как value_type, который будет иметь "пустое" значение при создании. В этом случае вам нужно получить shared_ptr по ссылке, чтобы вы могли называть reset() на нем.
Как и в случае с любыми коллекциями/кэшированием и т.д., вы должны остерегаться проблем с потоковой безопасностью, если это делается в многопоточном приложении.
То, что я не уверен, что вы хотели знать, заключалось в том, можно ли хранить ссылку где-нибудь (или указатель на нее) и ожидать, что она будет действительной позже, когда на карту будут добавлены другие элементы, и да, это будет.