Проблемы с контейнерами C ++ - PullRequest
       11

Проблемы с контейнерами C ++

1 голос
/ 05 сентября 2010

У меня есть std :: list в программе на C ++, которая содержит объекты класса A. Допустим, у меня есть 10 объектов.У меня есть ссылка на 6-й объект, хранящийся в другой структуре данных, скажем, ref_6.Допустим, мне нужно удалить 8-й элемент из моего списка.Для этого я бы использовал pop_front 8 раз и сохранил 8 объектов в векторе, а push_front 7 раз использовал, чтобы вставить первые 7 элементов обратно в список, так что теперь мой результирующий список будет иметь 9 элементов.Теперь, когда я пытаюсь получить доступ к объекту, хранящемуся в ref_6, который был 6-м элементом, я не могу это сделать.В этой ссылке есть некоторая ценность мусора.Я предполагаю, что, когда я делаю pop и push, расположение в памяти одного и того же объекта меняется.Как мне с этим бороться?

Ответы [ 4 ]

2 голосов
/ 05 сентября 2010

Почему вы стираете вещи таким образом?D: Это не стек.Вся точка (и только точка *) списка состоит в том, что вы можете удалить любой элемент за постоянное время.(Хотя обнаружение является линейным.)

Просто сделайте это:

typedef std::list<T> list_type;

list_type mylist; // populate it

list_type::iterator iter =  mylist.begin();
std::advance(iter, 8); // move to 8th item

mylist.erase(iter); // erase it

И никакие другие итераторы не будут аннулированы(Действительно, удаление элемента делает недействительными любые ссылки на него.)


* Вероятно, вам даже не следует использовать список.Списки хороши, когда дело доходит до изучения структур данных, но они довольно ужасны.

0 голосов
/ 07 сентября 2010

Я имею в виду ваш ответ.Извините, я неправильно понял учетную запись ... Пожалуйста, примите во внимание следующее:

std::list<A> tList;
A tA;

tList.push_back(tA);
assert(&tA == &tList.back()); // boom!

A *tAPtr = &tList.front();

tList.erase(tList.front());
// try to access tAPtr:
tAPtr->Foo(); // boom! (probably)

Дело в том, что экземпляры A хранятся по значению (= копируются), так что выделать по своей сути небезопасно.Используйте std::list<std::tr1::shared_ptr<A> > вместо!

0 голосов
/ 05 сентября 2010

Здесь что-то пахнет подозрительно ... Вы храните объект типа T в std::list<T> по значению.Вы храните ссылки на эти объекты в других местах.Это верно?Если да, я вижу несколько проблем ... Многие манипуляции со списками могут сделать недействительными сохраненные ссылки, поскольку std::list<T> гарантирует только последовательный порядок значений элементов типа T.Если вы хотите хранить ссылки на эти элементы в нескольких местах, используйте std::tr1::shared_ptr<T> и std::list<std::shared_ptr<T> >.Затем вы можете безопасно удалить или добавить (даже изменить) элементы в вашем списке, и ссылки, хранящиеся в других местах, остаются в силе.Остерегайтесь хранения std::list<T>iterators, проблема будет той же.

0 голосов
/ 05 сентября 2010

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

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

std::list::iterator = /*somehow get the iterator to the 8th element*/ <br> yourList.erase(8th_element_iterator);

Первый шаг (получение итератора для 8-го элемента) может быть выполнен, например, путем получения итератора начала списка и продвижения его на 7 позиций вперед:

std::list::iterator first_iter = yourList.begin(); <br> std::list::iterator 8th_iter = std::advance(first_iter, 7);

...