Указатели на элементы std :: vector и std :: list - PullRequest
29 голосов
/ 20 июля 2010

У меня 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 ]

25 голосов
/ 20 июля 2010

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

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

9 голосов
/ 20 июля 2010

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

6 голосов
/ 20 июля 2010

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

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

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

3 голосов
/ 20 июля 2010

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

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

1 голос
/ 20 июля 2010

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

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

1 голос
/ 20 июля 2010

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

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
0 голосов
/ 18 декабря 2017
  1. для векторов №.
  2. для списков да.как?Итератор работает как указатель на определенный узел в списке.так что вы можете присвоить значения любой структуре, например:

    список mylist;

    pair temp;

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

...