Pertanyaan Menghapus item dari vektor, sementara di C ++ 11 range 'for' loop?


Saya memiliki vektor IInventory *, dan saya mengulang daftar menggunakan rentang C ++ 11, untuk melakukan hal-hal dengan masing-masing.

Setelah melakukan beberapa hal dengan satu, saya mungkin ingin menghapusnya dari daftar dan menghapus objek. Saya tahu saya bisa menelepon delete pada penunjuk kapan saja untuk membersihkannya, tetapi apa cara yang tepat untuk menghapusnya dari vektor, sementara dalam rentang for lingkaran? Dan jika saya menghapusnya dari daftar, pengulangan saya akan batal?

std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());

for (IInventory* index : inv)
{
    // Do some stuff
    // OK, I decided I need to remove this object from 'inv'...
}

76
2018-04-28 04:00


asal


Jawaban:


Tidak, kamu tidak bisa. Berbasis jangkauan for adalah ketika Anda perlu mengakses setiap elemen wadah sekali.

Anda harus menggunakan yang normal for loop atau salah satu sepupunya jika Anda perlu memodifikasi wadah saat Anda pergi, mengakses elemen lebih dari satu kali, atau beriterasi dengan cara non-linear melalui penampung.

Sebagai contoh:

auto i = std::begin(inv);

while (i != std::end(inv)) {
    // Do some stuff
    if (blah)
        i = inv.erase(i);
    else
        ++i;
}

71
2018-04-28 04:02



Setiap kali elemen dihapus dari vektor, Anda harus mengasumsikan iterator pada atau setelah elemen terhapus tidak lagi berlaku, karena masing-masing elemen yang berhasil menghapus elemen dipindahkan.

Sebuah for-loop berbasis-range hanyalah gula sintaksis untuk loop "normal" menggunakan iterator, jadi hal di atas berlaku.

Itu dikatakan, Anda hanya bisa:

inv.erase(
    std::remove_if(
        inv.begin(),
        inv.end(),
        [](IInventory* element) -> bool {
            // Do "some stuff", then return true if element should be removed.
            return true;
        }
    ),
    inv.end()
);

45
2018-04-28 04:34



Anda sebaiknya tidak memodifikasi vektor sambil mengulanginya. Gunakan ungkapan penghapusan-hapus. Jika Anda melakukannya, Anda mungkin mengalami beberapa masalah. Karena dalam vector sebuah erase membatalkan semua iterator yang dimulai dengan elemen yang dihapus hingga end() Anda harus memastikan bahwa iterator Anda tetap valid dengan menggunakan:

for (MyVector::iterator b = v.begin(); b != v.end();) { 
    if (foo) {
       b = v.erase( b ); // reseat iterator to a valid value post-erase
    else {
       ++b;
    }
}

Perhatikan, bahwa Anda membutuhkan b != v.end() tes apa adanya. Jika Anda mencoba untuk mengoptimalkannya sebagai berikut:

for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)

Anda akan bertemu dengan UB sejak Anda e tidak valid setelah yang pertama erase panggilan.


10
2018-04-28 04:31



Apakah itu persyaratan yang ketat untuk menghapus elemen sementara dalam lingkaran itu? Jika tidak, Anda bisa mengatur pointer yang ingin Anda hapus ke NULL dan membuat yang lain melewati vektor untuk menghapus semua pointer NULL.

std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );

for ( IInventory* &index : inv )
{
    // do some stuff
    // ok I decided I need to remove this object from inv...?
    if (do_delete_index)
    {
        delete index;
        index = NULL;
    }
}
std::remove(inv.begin(), inv.end(), NULL);

5
2018-04-28 22:18



maaf untuk necroposting dan juga maaf jika keahlian c ++ saya menghalangi jawaban saya, tetapi jika Anda mencoba melakukan iterasi melalui setiap item dan membuat perubahan yang mungkin (seperti menghapus indeks), coba gunakan backwords for loop.

for(int x=vector.getsize(); x>0; x--){

//do stuff
//erase index x

}

saat menghapus indeks x, loop berikutnya adalah untuk item "di depan" iterasi terakhir. saya sangat berharap ini membantu seseorang


1
2018-05-25 20:09



OK, saya terlambat, tapi bagaimanapun: Maaf, tidak benar apa yang saya baca sejauh ini - itu aku s mungkin, Anda hanya perlu dua iterator:

std::vector<IInventory*>::iterator current = inv.begin();
for (IInventory* index : inv)
{
    if(/* ... */)
    {
        delete index;
    }
    else
    {
        *current++ = index;
    }
}
inv.erase(current, inv.end());

Hanya dengan memodifikasi nilai, poin iterator tidak akan membatalkan iterator lainnya, jadi kita dapat melakukan ini tanpa harus khawatir. Sebenarnya, std::remove_if (implementasi gcc setidaknya) melakukan sesuatu yang sangat mirip (menggunakan loop klasik ...), hanya saja tidak menghapus apa pun dan tidak menghapus.

Perlu diketahui, bagaimanapun, bahwa ini bukan thread safe (!) - namun, ini berlaku juga, untuk beberapa solusi lain di atas ...


1
2018-03-15 12:29



Saya akan menunjukkan dengan contoh, contoh di bawah ini menghapus elemen aneh dari vektor:

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};

    //method 1
    for(auto it = vecInt.begin();it != vecInt.end();){
        if(*it % 2){// remove all the odds
            it = vecInt.erase(it);
        } else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 2
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 2
    for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
        if (*it % 2){
            it = vecInt.erase(it);
        }else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 3
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 3
    vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
                 [](const int a){return a % 2;}),
                 vecInt.end());

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

}

output aw di bawah ini:

024
024
024

Perlu diingat, metode erase akan mengembalikan iterator berikutnya dari iterator yang dilewati.

Dari sini , kita bisa menggunakan metode yang lebih menghasilkan:

template<class Container, class F>
void erase_where(Container& c, F&& f)
{
    c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
            c.end());
}

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
    //method 4
    auto is_odd = [](int x){return x % 2;};
    erase_where(vecInt, is_odd);

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;    
}

Lihat di sini untuk melihat cara menggunakannya std::remove_if. https://en.cppreference.com/w/cpp/algorithm/remove


1
2017-07-29 01:56