Безопасно ли получить объект в 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() на нем.

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

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