2010-11-14 2 views
2

Я думал, что следующий код будет работать, но он сработает, когда целевой виджет находится в конце вектора.Безопасный способ непрерывного удаления из std :: vector?

for(std::vector<AguiWidget*>::iterator it = children.begin(); 
     it != children.end(); ++it) 
    { 
     if((*it) == widget) 
      it = children.erase(it); 
    } 

Я хочу, чтобы он прошел и удалил любой экземпляр, который он нашел в виджетах. Я понимаю, что этот метод N^2, но поскольку это событие управляется, это нормально. Я просто не знаю, почему это должно потерпеть неудачу. Когда это произойдет, «это» == виджет.

Благодаря

+0

Заканчивать http://stackoverflow.com/questions/347441/erasing-elements-from-a-vector – GWW

ответ

7

Вы можете использовать стирающие удалить идиомы, чтобы стереть все элементов, которые равны widget.

children.erase(remove(children.begin(), children.end(), widget), children.end()); 
2

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

for(std::vector<AguiWidget*>::iterator it = children.begin(); 
    it != children.end();) 
{ 
    if(*it == widget) 
     children.erase(it++); 
    else 
     ++it; 
} 

Обратите внимание, что я не увеличиваю итератор внутри оператора for-loop.

+1

Это основная причина, оригинал для() цикл завершается неудачей. Он всегда увеличивал итератор до повторного завершения условия завершения цикла. Изменение итератора на end() в теле цикла, а затем попытка «++ it» отправляет вас в goofy-land. – Blastfurnace

+0

нет причин использовать списки здесь. –

+0

@Matthieu: Мое упоминание об этом связано с тем, что списки намного эффективнее, чем векторы для задач, связанных с удалением внутренних элементов. Хотя для выполнения этой задачи с векторами вполне логично, для каждого удаления будет большой штраф, потому что все следующие элементы должны быть сдвинуты в памяти, и это может привести к большому количеству копий и перераспределению памяти. Списки не страдают этой проблемой, потому что они могут просто переставить несколько указателей, чтобы выполнить одно и то же. –

0

Вы понимаете, что сравниваете указатели, а не разыменования, не так ли?

Можете ли вы рассказать нам, что произойдет, если вы используете идиому удаления-стирания? Это собирается быть быстрым (эр, чем ваш код) и правильно:

children.erase(std::remove_if(children.begin(), children.end(), 
           std::bind1st(std::equal_to<AguiWidget*>(), 
              widget))); 

Кроме того, не забудьте удалить указатели первого.

for_each_if(children.begin(), children.end(), 
      std::bind1st(std::equal_to<AguiWidget*>(), widget), 
      Delete()); 

Конечно, вы должны быть уверены, что на одном и том же объекте нет двух указателей.

0

В дополнение к ответу Blastfurnace вы также можете сделать это с помощью простого цикла for, если вы сделаете это назад.

for (widgets::reverse_iterator it = children.rbegin(), end = children.rend(); 
    it != end; ++it) 
{ 
    if (*it == widget) { children.erase(it.base()); } 
} 
Смежные вопросы