Menghapus () elemen dalam vektor tidak berfungsi

10

Saya punya vektor. Saya perlu menghapus 3 elemen terakhir di dalamnya. Dijelaskan logika ini. Program macet. Apa yang bisa menjadi kesalahannya?

vector<float>::iterator d = X.end();
    for (size_t i = 1; i < 3; i++) {
        if (i == 1) X.erase(d);
        else X.erase(d - i);
    }
dbUser11
sumber
Pembunuh di sini dtidak benar-benar ada. Ini adalah nilai kenari satu masa lalu yang hanya dapat digunakan untuk menemukan bagian akhir vector. Anda tidak bisa menghapusnya. Selanjutnya, segera setelah Anda menghapus iterator, itu hilang. Anda tidak dapat menggunakannya dengan aman sesudahnya untuk apa pun, termasuk d - i.
user4581301

Jawaban:

9

Jika setidaknya ada 3 item dalam vektor, untuk menghapus 3 item terakhir adalah sederhana - cukup gunakan pop_back 3 kali:

#include <vector>
#include <iostream>

int main() 
{
    std::vector<float> v = { 1, 2, 3, 4, 5 };
    for (int i = 0; i < 3 && !v.empty(); ++i)
       v.pop_back();

    for ( const auto &item : v ) std::cout << item << ' ';
        std::cout << '\n';
}

Keluaran:

1 2
PaulMcKenzie
sumber
11

Ini adalah perilaku yang tidak ditentukan untuk meneruskan end()iterator ke parameter 1 yang erase()berlebihan. Bahkan jika tidak, erase()membatalkan iterator yang "pada dan setelah" elemen yang ditentukan, membuat dtidak valid setelah iterasi loop pertama.

std::vectormemiliki erase()kelebihan 2-parameter yang menerima serangkaian elemen untuk dihapus. Anda tidak memerlukan loop manual sama sekali:

if (X.size() >= 3)
    X.erase(X.end()-3, X.end());

Demo Langsung

Remy Lebeau
sumber
3

Pertama, X.end()tidak mengembalikan iterator ke elemen terakhir vektor, melainkan mengembalikan iterator ke elemen melewati elemen terakhir dari vektor, yang merupakan elemen yang sebenarnya tidak dimiliki vektor, itu sebabnya ketika Anda mencoba untuk hapus dengan X.erase(d)crash program.

Alih-alih, asalkan vektor mengandung setidaknya 3 elemen, Anda dapat melakukan hal berikut:

X.erase( X.end() - 3, X.end() );

Yang sebaliknya pergi ke elemen terakhir ketiga, dan menghapus setiap elemen setelah itu sampai tiba X.end().

EDIT: Hanya untuk memperjelas, X.end()adalah LegacyRandomAccessIterator yang ditentukan untuk memiliki -operasi yang valid yang mengembalikan LegacyRandomAccessIterator lain .

Nikko77
sumber
2

Definisi end()dari cppreference adalah:

Mengembalikan iterator yang merujuk ke elemen past-the-end dalam wadah vektor.

dan sedikit di bawah:

Itu tidak menunjuk ke elemen apa pun, dan dengan demikian tidak akan dereferensi.

Dengan kata lain, vektor tidak memiliki elemen yang diakhiri () menunjuk ke. Dengan mendereferensi non-elemen melalui metode erase (), Anda mungkin mengubah memori yang bukan milik vektor. Karenanya hal-hal buruk dapat terjadi sejak saat itu.

Ini adalah konvensi C ++ biasa untuk menggambarkan interval sebagai [rendah, tinggi), dengan nilai "rendah" termasuk dalam interval, dan nilai "tinggi" dikeluarkan dari interval.

jpmarinier
sumber
2

Anda bisa menggunakan reverse_iterator:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<float> X = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

    // start the iterator at the last element
    vector<float>::reverse_iterator rit = X.rbegin();

    // repeat 3 times
    for(size_t i = 0; i < 3; i++)
    {
        rit++;
        X.erase(rit.base());
    }

    // display all elements in vector X
    for(float &e: X)
        cout << e << '\n';

    return 0;
}

Ada beberapa hal yang perlu disebutkan:

  • reverse_iterator ritdimulai pada elemen terakhir dari vector X. Posisi ini disebut rbegin.
  • erasemembutuhkan klasik iteratoruntuk bekerja dengannya. Kami mendapatkannya dari ritmenelepon base. Tapi iterator baru itu akan menunjuk ke elemen berikutnya dari ritarah maju.
  • Itu sebabnya kami memajukan ritsebelum menelepon basedanerase

Juga jika Anda ingin tahu lebih banyak tentang reverse_iterator, saya sarankan mengunjungi jawaban ini .

Pengguna yang disanitasi
sumber
2

Sebuah komentar (sekarang dihapus) dalam pertanyaan menyatakan bahwa "tidak ada - operator untuk iterator." Namun, kode berikut ini mengkompilasi dan berfungsi di keduanya MSVCdan clang-cl, dengan standar yang ditetapkan baik C++17atau C++14:

#include <iostream>
#include <vector>

int main()
{
    std::vector<float> X{ 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f };
    for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
    std::vector<float>::iterator d = X.end();
    X.erase(d - 3, d);  // This strongly suggest that there IS a "-" operator for a vector iterator!
    for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
    return 0;
}

Definisi yang disediakan untuk operator-adalah sebagai berikut (di <vector>header):

    _NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
        _Vector_iterator _Tmp = *this;
        return _Tmp -= _Off;
    }

Namun, saya jelas bukan pengacara bahasa C ++, dan mungkin saja ini adalah salah satu ekstensi Microsoft yang 'berbahaya'. Saya akan sangat tertarik untuk mengetahui apakah ini bekerja pada platform / kompiler lain.

Adrian Mole
sumber
2
Saya pikir itu valid, karena iterator vektor adalah akses acak, dan -didefinisikan untuk tipe iterator tersebut.
PaulMcKenzie
@ PaulMcKenzie Memang - analisa statis dentang (yang bisa sangat ketat dengan standar) tidak memberikan peringatan tentang hal itu.
Adrian Mole
1
Bahkan jika tidak ada operator-definisi untuk iterator, Anda bisa menggunakan std::advance()atau std::prev()sebaliknya.
Remy Lebeau
1

Pernyataan ini

    if (i == 1) X.erase(d);

memiliki perilaku yang tidak terdefinisi.

Dan pernyataan ini mencoba untuk menghapus hanya elemen sebelum elemen terakhir

    else X.erase(d - i);

karena Anda memiliki satu lingkaran dengan hanya dua iterasi

for (size_t i = 1; i < 3; i++) {

Anda perlu sesuatu seperti yang berikut ini.

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

int main() 
{
    std::vector<float> v = { 1, 2, 3, 4, 5 };

    auto n = std::min<decltype( v.size() )>( v.size(), 3 ); 
    if ( n ) v.erase( std::prev( std::end( v ), n ), std::end( v ) );

    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << '\n';

    return 0;
}

Output program adalah

1 2 
Vlad dari Moskow
sumber