Подтвердить что ты не робот

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

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

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

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?

4b9b3361

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

В частности, посмотрите ptr_map

Ответ 7

  • для векторов нет.
  • для списков да. как? Итератор работает как указатель на конкретный node в списке. поэтому вы можете присвоить значения любой структуре, например:

    список mylist;

    Пара < list:: iterator, int > temp;

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