Указатели на элементы std::vector и std::list

У меня есть std::vector с элементами некоторого класса ClassA, Кроме того, я хочу создать индекс, используя std::map<key,ClassA*> который отображает некоторое значение ключа на указатели на элементы, содержащиеся в векторе.

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

std::vector<ClassA> storage;
std::map<int, ClassA*> map;

for (int i=0; i<10000; ++i) {
  storage.push_back(ClassA());
  map.insert(std::make_pair(storage.back().getKey(), &(storage.back()));
}
// map contains only valid pointers to the 'correct' elements of storage

Как ситуация, если я использую std::list вместо std::vector?

7 ответов

Решение

Векторы - Нет. Поскольку емкость векторов никогда не уменьшается, гарантируется, что ссылки, указатели и итераторы остаются действительными даже при удалении или изменении элементов при условии, что они ссылаются на позицию перед управляемыми элементами. Однако вставки могут сделать недействительными ссылки, указатели и итераторы.

Списки - Да, вставка и удаление элементов не делает недействительными указатели, ссылки и итераторы на другие элементы

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

Использование std::deque! Указатели на элементы стабильны только тогда, когда push_back() используется.

Примечание: итераторы для элементов могут быть недействительными! Указатели на элементы не будут.

Изменить: этот ответ объясняет детали почему: итератор C++ deque признан недействительным после push_front()

Я не уверен, гарантировано ли это, но на практике storage.reserve(needed_size) следует убедиться, что перераспределения не происходит.

Но почему вы не храните индексы?
Преобразовать индексы в итераторы легко, добавив их в начальный итератор (storage.begin()+idx) и легко превратить любой итератор в указатель, сначала разыменовав его, а затем взяв его адрес (&*(storage.begin()+idx)).

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

В частности, взгляните на ptr_map

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

std::vector<ClassA*> storage;
std::map<int, ClassA*> map;

for (int i=0; i<10000; ++i) {
  ClassA* a = new ClassA()
  storage.push_back(a)
  map.insert(std::make_pair(a->getKey(), a))
}
// map contains only valid pointers to the 'correct' elements of storage
  1. для векторов нет.
  2. для списков да. как? Итератор работает как указатель на определенный узел в списке. так что вы можете присвоить значения любой структуре, например:

    список mylist;

    pair temp;

    temp = make_pair (mylist.begin (), x);

Другие вопросы по тегам