Указатели на элементы 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
?
Ответы
Ответ 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);