LINUX.ORG.RU

Глюки STL?


0

0

Здравствуйте!

У меня такая проблема: нужно из списка удалить элемент удовлетворяющий определённому критерию. Вот, пишу примерно такой код:

...

class THEXData { public: char * Data; long Address; byte DataCount; THEXData( char count ); ~THEXData(); };

...

list<THEXData> lst; ... list<THEXData>::iterator iter = lst.begin(); for ( ; iter != lst.end(); iter++) { if ( iter->Address == 0xFFE0 ) lst.erase( iter ); }

Первый раз когда я написал подобный код под Виндой в C++ Builder 5, он у меня вытер не только этот элемент, но и все последующие. Второй раз я вставил такой код в консольный проект - начал выбрасывать исключение. Попробовал компилить g++ - такая же лабуда.

Может это не STL глючит, а я? Подскажите, плиз.

★★

Учите матчасть! Оператор присваивания кто за тебя перегружать будет? А?
А конструктор копирования? У тебя в список элементы по значению добавляются. После чего для исходного объекта скорее всего срабатывает деструктор по выходе из зоны видимости. И прибивает динамические данные. Дефолтный конструктор копирования просто копирует блок памяти и все, так что у тебя элементы в списке содержат указатели на уже освобожденную память. Естественно, при попытке что-то сделать с этим адресом (например, повторно его освободить при erase) тебя возникают неприятности. Напиши для своего класса оператор присваивания и конструктор копирования и посмотри результат!

anonymous
()

А кто итератор сохранять будет???

Писать надо так:

iter = lst.erase(iter);

СТРАУСТРУПА НУЖНО ДОЧИТАТЬ РАЗ НАЧАЛ!

anonymous
()

и так for ( ; iter != lst.end() && !(lst.empty()); ++iter)

anonymous
()

Все это в обшем правильно, но к данном случае не поможет.
Даже если заменить объект на int, то проблема останется.
Как я понимаю, все дело в том, что erase(iter) не меняет самого
итератора, он по-прежнему указывает на то место, где когда то 
был объект. А следующий за ним iter++ указывает куда угодно, что
и приводит программу к краху.
Так что нужно либо написать, что то типа:
  for ( ; iter != lst.end(); ) 
    {
      if ( iter->Address == 0xFFE0)
	iter=lst.erase( iter );
      else
	++iter;
    };
либо использовать связку 
remove_if, erase

anonymous
()

ну вот и и вы туда же

откройте Страуструпа - там подробно это описано

А за "что-то типа" просто увольняют

anonymous
()

erase возвращает следующий итератор за удаленным.

iterator erase(iterator __position) { _List_node_base* __next_node = __position._M_node->_M_next; _List_node_base* __prev_node = __position._M_node->_M_prev; _Node* __n = static_cast<_Node*>(__position._M_node); __prev_node->_M_next = __next_node; __next_node->_M_prev = __prev_node; _Destroy(&__n->_M_data); _M_put_node(__n); return iterator(static_cast<_Node*>(__next_node)); }

Как заметил предыдущий автор, за это действительно могут уволить :) У западных компаний недавно появилась практика: один из опытных программистов этой компании может просматривать, на испытательном сроке, написанный вами код, если он увидит такое, то .... bye-bye

Их можно понять, ведь сейчас программеров как грязи :) , но вот опытных пока очень немного, так что будьте внимательней.

anonymous
()

ну про увольнять тут перегибают... только если STL обязательна и кандидат заявил, что СТЛ для него что дом родной...
а по поводу оригинального вопроса... Страуструп это здорово и всегда полезно, но еще необходимо перед вопросом в форуме (или коллегам в офисе) посмотреть документацию от производителя. Там в первом же абзаце написано, что итератор списка является присваевавемым (assignable) следовательно earse будет инвалидировать итератор (ну там не написано конечно, что надо делать it=erase(it) - это (имхо) очевидно).

laban
()

>откройте Страуструпа - там подробно это описано
у Страуструпа, что лежит на столе, 956 страниц. 
Если Вас не затруднит уточните о чем конкретно Вы говорите.
Кстати, не стоит считать, что Страуступ истина в последней инстанции,
не все что он пишет- соответствует действительности

Обратимся к первоисточнику: 

ISO/IEC    ISO/IEC 14882:1998(E)
23  Containers library     23.1.1 Sequences

7    The iterator returned from a.erase(q) points to the element immediately following q prior to the element being erased.  If no such element exists, a.end() is returned.

>А за "что-то типа" просто увольняют
Я очень заинтригован. Что здесь неправильно на Ваш взгляд?
      if ( iter->Address == 0xFFE0)
	iter=lst.erase( iter );
      else
	++iter;
И не могли бы Вы показать, как должно быть.

anonymous
()

anonimous1 писал: "Учите матчасть! Оператор присваивания кто за тебя перегружать будет? А? А конструктор копирования? У тебя в список элементы по значению добавляются. После чего для исходного объекта скорее всего срабатывает деструктор по выходе из зоны видимости. И прибивает динамические данные. Дефолтный конструктор копирования просто копирует блок памяти и все, так что у тебя элементы в списке содержат указатели на уже освобожденную память. Естественно, при попытке что-то сделать с этим адресом (например, повторно его освободить при erase) тебя возникают неприятности. Напиши для своего класса оператор присваивания и конструктор копирования и посмотри результат!"

Я же писал, что код ПРИМЕРНО такой! У меня в коде есть и конструктор копирования и оператор присвоения. А при замене этого класса просто на int получаем такую же ошибку. Так что, скорее всего, тот anonimous, который оставил своё сообщение над моим, прав. Я ещё не проверял. Доступа к Инету регулярного нет :-(.

kkk ★★
() автор топика

Да, кстати, я смотрел руководство по STL из C++ Builder 6 (точно такое же и в Cygwin). В Линуксе правда не смотрел - сразу на форум полез.

kkk ★★
() автор топика

Глюки STL ? Нет, просто неграмотность...

Читать Страуструпа! 3'-е издание, глава 10, пункт 10.4.4.1 -- "Копирование
объектов".

Там подробно описано, как действовать, если в классе имеются члены,
являющиеся указателями.

Хотя в данном случае (IMHO) гораздо лучше вместо char* Data использовать
string Data, чем писать копирующий конструктор и переопределять
присваивание.

Dselect ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.