Bir vektördeki bir öğeyi silmek () çalışmaz


10

Bir vektörüm var. İçindeki son 3 öğeyi silmem gerekiyor. Bu mantığı açıkladı. Program çöküyor. Hata ne olabilir?

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);
    }

Buradaki katil dgerçekten yok. Sadece sonunu bulmak için kullanılabilen son bir kanarya değeri vector. Onu kaldıramazsın. Sonra, bir yineleyiciyi sildiğiniz anda, gitti. Daha sonra da dahil olmak üzere hiçbir şey için güvenle kullanamazsınız d - i.
user4581301

Yanıtlar:


9

Vektörde en az 3 öğe varsa, son 3 öğeyi silmek kolaydır - sadece pop_back'i 3 kez kullanın :

#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';
}

Çıktı:

1 2

11

İse tanımsız davranış geçmek end()1-parametresi yineleyici erase()aşırı. Olmasa bile erase(), belirtilen öğenin "başında ve sonrasında" olan yineleyicileri geçersiz kılar dve 1. döngü yinelemesinden sonra geçersiz kılar .

std::vectorerase()kaldırılacak bir dizi öğeyi kabul eden 2 parametreli bir aşırı yüklemeye sahiptir. Manuel bir döngüye ihtiyacınız yoktur:

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

Canlı Demo


3

İlk olarak, X.end()bir yineleyiciyi vektörün son öğesine döndürmez, daha çok vektörün son öğesinin ötesindeki öğeye bir yineleyici döndürür, bu da vektörün gerçekte sahip olmadığı bir öğedir, bu yüzden X.erase(d)program çöktüğünde silin .

Bunun yerine, vektörün en az 3 öğe içermesi koşuluyla, aşağıdakileri yapabilirsiniz:

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

Bunun yerine üçüncü son öğeye gider ve bundan sonra her öğeyi silene kadar siler X.end().

EDIT: Sadece açıklığa kavuşturmak için, X.end()başka bir LegacyRandomAccessIterator- döndüren geçerli bir işlem olması belirtilen bir LegacyRandomAccessIterator olduğunu .


2

end()From cppreference'nin tanımı :

Vektör kapsayıcısındaki son öğeyi işaret eden bir yineleyici döndürür.

ve biraz aşağıda:

Herhangi bir öğeye işaret etmez ve bu nedenle kaydı kaldırılamaz.

Başka bir deyişle, vektörün sona eren () hiçbir öğesi yoktur. Bu kaldırma tarafından olmayan eleman silme () yöntemi aracılığıyla, muhtemelen vektörüne ait olmayan bir bellek değiştirmektedir. Dolayısıyla oradan çirkin şeyler olabilir.

“Düşük” değeri ile, [düşük ve yüksek) halinde aralıkları tanımlamak için her zamanki C ++ konvansiyonudur dahil aralığı içinde, ve “yüksek” değeri hariç aralığından.


2

Şunları kullanabilirsiniz 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;
}

Söylenecek birkaç şey var:

  • reverse_iterator ritöğesinin son öğesinde başlar vector X. Bu konuma denir rbegin.
  • eraseklasik iteratorçalışmak için gerektirir . Bunu ritarayarak elde ederiz base. Ancak bu yeni yineleyici bir sonraki öğeyi ritileri yönde gösterecektir .
  • Biz ilerlemek yüzden ritçağırmadan önce baseveerase

Ayrıca hakkında daha fazla bilgi edinmek isterseniz reverse_iterator, bu yanıtı ziyaret etmenizi öneririm .


2

Sorudaki bir yorum (şimdi silindi) "yineleyici için operatör yok" ifadesini kullandı. Bununla birlikte, aşağıdaki kod her ikisinde de derlenir ve çalışırMSVC ve clang-clstandart ya C++17da olarak ayarlanır 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;
}

The için sağlanan tanım operator-aşağıdaki gibidir ( <vector>başlıkta):

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

Ancak, kesinlikle C ++ dil avukatı değilim ve bunun 'tehlikeli' Microsoft uzantılarından biri olması mümkündür. Bunun diğer platformlarda / derleyicilerde işe yarayıp yaramadığını bilmek isterim.


2
Bence bu geçerli, çünkü bir vektörün yineleyicileri rasgele erişimli ve -bu yineleyiciler için tanımlanmış.
PaulMcKenzie

@PaulMcKenzie Gerçekten - clang statik analizörü (standartlara oldukça katı olabilir) bu konuda hiçbir uyarı vermedi.
Adrian Mole

1
operator-Yineleyiciler için tanımlanmış olmasa bile , sadece std::advance()veya std::prev()yerine kullanabilirsiniz .
Remy Lebeau

1

Bu açıklama

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

tanımlanmamış davranışı var.

Ve bu ifade yalnızca son öğeden önceki öğeyi kaldırmaya çalışır

    else X.erase(d - i);

çünkü sadece iki yinelemeli bir döngünüz var

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

Aşağıdaki gibi bir şeye ihtiyacınız var.

#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;
}

Program çıktısı

1 2 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.