Apagar () um elemento em um vetor não funciona

10

Eu tenho um vetor Eu preciso excluir os três últimos elementos nele. Descreveu essa lógica. O programa trava. Qual poderia ser o erro?

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
fonte
O assassino aqui é drealmente não existe. É o valor canário de um passado-final que pode ser usado apenas para encontrar o fim do vector. Você não pode removê-lo. Em seguida, assim que você apaga um iterador, ele desaparece. Você não pode usá-lo com segurança posteriormente para qualquer coisa, inclusive d - i.
User4581301 15/04

Respostas:

9

Se houver pelo menos 3 itens no vetor, excluir os 3 últimos itens é simples - basta usar pop_back 3 vezes:

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

Resultado:

1 2
PaulMcKenzie
fonte
11

É um comportamento indefinido passar o end()iterador para a erase()sobrecarga de 1 parâmetro . Mesmo se não fosse, erase()invalida os iteradores "no e depois" do elemento especificado, tornando dinválido após a primeira iteração do loop.

std::vectorpossui uma erase()sobrecarga de 2 parâmetros que aceita um intervalo de elementos para remover. Você não precisa de um loop manual:

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

Demonstração ao vivo

Remy Lebeau
fonte
3

Primeiro, X.end()não retorna um iterador para o último elemento do vetor, mas retorna um iterador para o elemento após o último elemento do vetor, que é um elemento que o vetor realmente não possui, é por isso que quando você tenta apague-o com X.erase(d)o programa trava.

Em vez disso, desde que o vetor contenha pelo menos três elementos, você pode fazer o seguinte:

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

Que, em vez disso, vai para o terceiro último elemento e apaga todos os elementos depois até chegar a X.end().

EDIT: Apenas para esclarecer, X.end()é um LegacyRandomAccessIterator que está especificado para ter uma -operação válida que retorna outro LegacyRandomAccessIterator .

Nikko77
fonte
2

A definição de end()from cppreference é:

Retorna um iterador referente ao elemento passado no final do contêiner de vetor.

e um pouco abaixo:

Não aponta para nenhum elemento e, portanto, não deve ser desreferenciado.

Em outras palavras, o vetor não possui nenhum elemento para o qual end () aponte. Ao desreferenciar esse não elemento através do método erase (), você possivelmente está alterando a memória que não pertence ao vetor. Daí coisas feias podem acontecer a partir daí.

É a convenção usual do C ++ descrever intervalos como [baixo, alto), com o valor "baixo" incluído no intervalo e o valor "alto" excluído do intervalo.

jpmarinier
fonte
2

Você pode usar um 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;
}

Há poucas coisas a serem mencionadas:

  • reverse_iterator ritcomeça no último elemento do vector X. Esta posição é chamada rbegin.
  • eraserequer clássico iteratorpara trabalhar. Nós conseguimos isso ritligando base. Mas esse novo iterador apontará para o próximo elemento de ritfrente.
  • É por isso que avançamos ritantes de ligar baseeerase

Além disso, se você quiser saber mais reverse_iterator, sugiro visitar esta resposta .

sanitizedUser
fonte
2

Um comentário (agora excluído) na pergunta afirmou que "não existe operador para um iterador". No entanto, o código a seguir compila e funciona em ambos MSVCe clang-cl, com o padrão definido como C++17ou 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;
}

A definição fornecida para operator-é a seguinte (no <vector>cabeçalho):

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

No entanto, certamente não sou advogado da linguagem C ++ e é possível que essa seja uma daquelas extensões 'perigosas' da Microsoft. Eu ficaria muito interessado em saber se isso funciona em outras plataformas / compiladores.

Adrian Mole
fonte
2
Eu acho que é válido, já que os iteradores de um vetor são de acesso aleatório e -são definidos para esses tipos de iteradores.
PaulMcKenzie 15/04
@PaulMcKenzie De fato - o analisador estático de clang (que pode ser bastante rigoroso com os padrões) não deu nenhum aviso sobre isso.
Adrian Mole
11
Mesmo se não houver operator-definido para os iteradores, você pode simplesmente usar std::advance()ou em std::prev()vez disso.
Remy Lebeau
1

Esta afirmação

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

tem comportamento indefinido.

E esta declaração tenta remover apenas o elemento antes do último elemento

    else X.erase(d - i);

porque você tem um loop com apenas duas iterações

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

Você precisa de algo como o seguinte.

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

A saída do programa é

1 2 
Vlad de Moscou
fonte