2012-01-03 4 views
2

У меня есть моя петля, проходящая через элементы вектора. Хотя в этом цикле некоторые элементы (я хочу, чтобы они были) удалены. Хотя std :: vector не позволяет это делать, и я хотел бы получить альтернативу.Альтернатива для std: vector для удаления ее элементов при прохождении цикла?

for(unsigned int j = 0; j < rectArray.size(); j++) 
{ 
if(rectArray[j] == 2) 
    { 
    rectArray.erase(rectArray.begin() + j); 
    } 
//... 
} 

Считаете ли вы, что std :: list будет хорош здесь? Могу ли я использовать что-то еще?

+2

Вас интересует порядок элементов? Если нет, вы можете быстро удалить его из середины, заменив его на 'back()', а затем удалив его с помощью 'pop_back()'. –

+4

Как насчет 'std :: remove_if'? – jrok

+3

Обратите внимание, что этот цикл неверен, поскольку он пропускает каждый элемент после удаления. – UncleBens

ответ

1

Я бы предложил изменить ваш код таким образом, чтобы он использовал итераторы вместо фактического вектора. Это намного чище и эффективнее, как это:

for (auto it = rectArray.begin(); it != rectArray.end(); ++it) 
{ 
    // Access the current element with *it 
    // If you want you can pass `it` and `rectArray.end()` as 
    // the lower and upper bounds of the new collection, 
    // rather than doing expensive resizes of the vector. 
} 

Обратите внимание, что auto является C++ 11 функция (как я использовал его). Если ваш компилятор поддерживает, что вы также можете использовать 11 C++ в Еогеасп:

for (auto it : rectArray) { 
    // same as before 
} 
+0

Первая версия требует немного больше работы, чтобы получить правильный итератор после стирания элемента; вторая версия не позволяет полностью стереть текущий элемент внутри цикла. –

+0

@MikeSeymour Мое предложение состояло в том, чтобы не удалять их вообще, а просто использовать итераторы как нижние, так и верхние границы. –

+0

'auto' не работает для меня. Я должен был сделать это: 'for (std :: list :: iterator it = listOfCvRects.begin(); it! = ListOfCvRects.end(); it ++) \t {' – Patryk

1

Извлечение элемента из середины вектора дорого - потому что вам нужно переместить все последующие элементы вниз.

Если вам нужно добавить/удалить элементы в середину контейнера, список обычно лучше.

7

Если элементы вектора очень дороги для копирования, самым простым, вероятно, является std::copy_if (или иначе скопировать те, которые вы хотите сохранить) в новый вектор, а затем замените их на оригинал. Также есть remove_if, а затем resize.

Если элементы очень дороги для перемещения, тогда list избежит этого, но это зависит от того, что еще вы делаете с коллекцией. Если вы сделаете что-то еще, что будет чересчур медленным с list, то вы только что переместили проблему в другом месте.

+0

+1 для 'copy_if' –

+2

@Seth: хотя моя рекомендация в этом случае может повлиять на мое волнение, что он, наконец, стандартный ;-) –

+0

Интересно, почему нет' move_if', хотя .. Там уже есть 'move' для диапазонов. Может быть, они хотят, чтобы мы имитировали его с помощью 'std :: make_move_iterator', который мог пойти ужасно неправильно.: | – Xeo

1

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

1

Возможная альтернатива std::remove_if. Немного быстрее и не требует функтора, однако он не поддерживает порядок.

auto end = std::end(rectArray); 
for(auto it = std::begin(rectArray); it != end; ++it) 
{ 
    if(it->remove_me()))  
     std::swap(*it, *--end); // or even faster *it = std::move(*--end); 
}  
rectArray.erase(end, std::end(rectArray)); 
+2

Это примерно то, что делает 'remove_if', не так ли? – jrok

+0

Да, это было предложено. Это решение немного быстрее и не требует функтора (pre C++ 11). – ronag

+1

@jrok: грубо, но 'remove_if' поддерживает порядок последовательности, тогда как это приводит элементы от конца к середине. –

0

Если вы делаете много удалений, список, вероятно, путь. Вот несколько примеров кода, которые помогут.

#include <vector> 
#include <list> 
#include <algorithm> 

using namespace std; 

class Widget 
{ 
public: 
    explicit Widget(int someNumber); 
    bool ShouldDelete(); 
    bool ShouldDeleteComplex(int a, int b, int c); 
private: 
    int _someNumber; 
}; 

Widget::Widget(int someNumber) : _someNumber(someNumber) 
{ 
} 

bool Widget::ShouldDelete() 
{ 
    if (_someNumber > 2) 
    { 
     return true; 
    } 
    return false; 
} 

bool Widget::ShouldDeleteComplex(int a, int b, int c) 
{ 
    if ((a * b - c) > _someNumber) 
    { 
     return true; 
    } 
    return false; 
} 

int main() 
{ 
    list<Widget> lw; 

    lw.push_back(Widget(1)); 
    lw.push_back(Widget(2)); 
    lw.push_back(Widget(3)); 

    // delete from list using functor 
    lw.remove_if(mem_fun_ref(&Widget::ShouldDelete)); 

    // delete from list using lambda function 
    lw.remove_if([] (Widget& x) { return x.ShouldDeleteComplex(1, 2, 0); } ); 

    vector<Widget> vw; 

    vw.push_back(Widget(1)); 
    vw.push_back(Widget(2)); 
    vw.push_back(Widget(3)); 

    // delete using functor 
    vw.erase(remove_if(vw.begin(), vw.end(), mem_fun_ref(&Widget::ShouldDelete)), vw.end()); 

    // delete using lambda function 
    vw.erase(
     remove_if(vw.begin(), vw.end(), 
     [] (Widget& x) { return x.ShouldDeleteComplex(1, 2, 0); } 
    ), 
     vw.end()); 

    return 0; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^