Saya memiliki vektor IInventory *, dan saya melakukan perulangan melalui daftar menggunakan C ++ 11 range for, untuk melakukan hal-hal dengan masing-masing.
Setelah melakukan beberapa hal dengannya, saya mungkin ingin menghapusnya dari daftar dan menghapus objeknya. Saya tahu saya dapat memanggil delete
penunjuk kapan saja untuk membersihkannya, tetapi apa cara yang tepat untuk menghapusnya dari vektor, saat berada dalam for
lingkaran jangkauan ? Dan jika saya menghapusnya dari daftar akankah loop saya tidak valid?
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'...
}
std::remove_if
predikat "melakukan sesuatu" dan mengembalikan nilai true jika Anda ingin elemen tersebut dihapus.std::list
bawah iniJawaban:
Tidak, tidak boleh. Berbasis rentang
for
adalah saat Anda perlu mengakses setiap elemen wadah satu kali.Anda harus menggunakan
for
loop normal atau salah satu sepupunya jika Anda perlu mengubah penampung saat Anda melanjutkan, mengakses elemen lebih dari sekali, atau melakukan iterasi secara non-linier melalui penampung.Sebagai contoh:
sumber
true
, AFAIU, dan tampaknya lebih baik cara ini tidak mencampur logika iterasi dengan predikat.remove_if
lebih baik.erase
mengembalikan iterator baru yang valid. mungkin tidak efisien, tetapi dijamin berhasil.Setiap kali elemen dihapus dari vektor, Anda harus menganggap iterator pada atau setelah elemen terhapus tidak lagi valid, karena setiap elemen yang menggantikan elemen terhapus dipindahkan.
Perulangan-untuk berbasis rentang hanyalah gula sintaksis untuk perulangan "normal" yang menggunakan iterator, jadi hal di atas berlaku.
Karena itu, Anda cukup:
sumber
vector
tidak akan pernah dialokasikan karena ada panggilan keerase
. Alasan iterator tidak valid adalah karena setiap elemen yang menggantikan elemen yang dihapus dipindahkan.[&]
akan sesuai, untuk memungkinkannya "melakukan beberapa hal" dengan variabel lokal.remove_if
dengan.erase
, jika tidak ada yang terjadi.std::remove_if
adalah O (n).Idealnya, Anda tidak boleh memodifikasi vektor saat mengulanginya. Gunakan idiom hapus-hapus. Jika ya, Anda mungkin akan mengalami beberapa masalah. Karena dalam
vector
sebuaherase
membatalkan semua iterator yang dimulai dengan elemen yang dihapus hinggaend()
Anda perlu memastikan bahwa iterator Anda tetap valid dengan menggunakan:Perhatikan, bahwa Anda memerlukan
b != v.end()
tes apa adanya. Jika Anda mencoba mengoptimalkannya sebagai berikut:Anda akan mengalami UB karena Anda
e
tidak valid setelaherase
panggilan pertama .sumber
std::remove
, dan itu O (N ^ 2) bukan O (N).Apakah merupakan persyaratan ketat untuk menghapus elemen saat berada dalam loop itu? Jika tidak, Anda dapat mengatur pointer yang ingin Anda hapus ke NULL dan membuat satu lagi melewati vektor untuk menghapus semua pointer NULL.
sumber
maaf untuk necroposting dan juga maaf jika keahlian c ++ saya menghalangi jawaban saya, tetapi jika Anda mencoba mengulang setiap item dan membuat kemungkinan perubahan (seperti menghapus indeks), coba gunakan backwords for loop.
saat menghapus indeks x, pengulangan berikutnya adalah untuk item "di depan" iterasi terakhir. Saya sangat berharap ini membantu seseorang
sumber
OK, aku terlambat, tapi tetap: Maaf, tidak benar apa yang saya baca sejauh - itu adalah mungkin, Anda hanya perlu dua iterator:
Hanya memodifikasi nilai yang ditunjukkan oleh iterator tidak akan membatalkan iterator lain, jadi kita dapat melakukan ini tanpa harus khawatir. Sebenarnya,
std::remove_if
(setidaknya implementasi gcc) melakukan sesuatu yang sangat mirip (menggunakan loop klasik ...), hanya tidak menghapus apa pun dan tidak menghapus.Namun, ketahuilah bahwa ini bukan thread safe (!) - namun, ini juga berlaku untuk beberapa solusi lain di atas ...
sumber
erase
(asalkan Anda menghapus lebih dari satu elemen, tentu saja)?Saya akan menunjukkan dengan contoh, contoh di bawah ini menghapus elemen ganjil dari vektor:
keluaran aw di bawah ini:
Perlu diingat, metode ini
erase
akan mengembalikan iterator berikutnya dari iterator yang diteruskan.Dari sini , kita dapat menggunakan metode yang lebih banyak menghasilkan:
Lihat di sini untuk mengetahui cara menggunakan
std::remove_if
. https://en.cppreference.com/w/cpp/algorithm/removesumber
Bertentangan dengan judul utas ini, saya akan menggunakan dua umpan:
sumber
Solusi yang jauh lebih elegan adalah beralih ke
std::list
(dengan asumsi Anda tidak memerlukan akses acak cepat).Anda kemudian dapat menghapus dengan
.remove_if
dan fungsi C ++ dalam satu baris:Jadi di sini saya hanya menulis sebuah functor yang menerima satu argumen (the
Widget*
). Nilai yang dikembalikan adalah kondisi untuk menghapus aWidget*
dari daftar.Menurut saya sintaks ini cocok. Saya tidak berpikir saya akan pernah menggunakan
remove_if
untuk std :: vektor - ada begitu banyakinv.begin()
daninv.end()
kebisingan di sana Anda mungkin lebih baik menggunakan penghapusan berbasis indeks-integer atau hanya penghapusan berbasis iterator biasa biasa (seperti yang ditunjukkan di bawah). Tetapi Anda tidak boleh benar-benar menghapus dari tengah-tengahstd::vector
, jadi beralih ke alist
untuk kasus penghapusan daftar tengah yang sering ini disarankan.Catatan namun saya tidak mendapatkan kesempatan untuk memanggil
delete
padaWidget*
's yang telah dihapus. Untuk melakukan itu, akan terlihat seperti ini:Anda juga bisa menggunakan loop berbasis iterator biasa seperti:
Jika Anda tidak suka panjangnya
for( list<Widget*>::iterator iter = widgets.begin() ; ...
, Anda bisa menggunakansumber
remove_if
sebuahstd::vector
pekerjaan, dan bagaimana itu membuat kompleksitas menjadi O (N).std::vector
akan selalu menggeser setiap elemen setelah Anda menghapus satu, membuat pilihanstd::list
yang jauh lebih baik.remove_if
akan menggeser setiap elemen ke atas dengan jumlah spasi yang dibebaskan. Pada saat Anda menghitung penggunaan cacheremove_if
padastd::vector
penghapusan melebihi kemungkinan daristd::list
. Dan mempertahankanO(1)
akses acak.Saya pikir saya akan melakukan yang berikut ...
sumber
Anda tidak dapat menghapus iterator selama iterasi loop karena jumlah iterator tidak cocok dan setelah beberapa iterasi Anda akan memiliki iterator yang tidak valid.
Solusi: 1) ambil salinan vektor asli 2) ulangi iterator menggunakan salinan ini 2) lakukan beberapa hal dan hapus dari vektor asli.
sumber
Menghapus elemen satu-per-satu dengan mudah mengarah ke kinerja N ^ 2. Lebih baik menandai elemen yang harus dihapus dan menghapusnya sekaligus setelah loop. Jika saya dapat menganggap nullptr dalam elemen tidak valid dalam vektor Anda, maka
harus bekerja.
Jika "Lakukan beberapa hal" Anda tidak mengubah elemen vektor dan hanya digunakan untuk membuat keputusan untuk menghapus atau menyimpan elemen, Anda dapat mengubahnya menjadi lambda (seperti yang disarankan di posting sebelumnya seseorang) dan menggunakan
sumber