迭代器在列表中丢失引用

时间:2014-05-11 13:31:49

标签: c++ list iterator

我的问题出在主要功能上。每当我执行remove()时,迭代器都会指向thrash数据。经过调查,我发现问题主要在于。我试图在循环中使用相同的迭代器进行多次删除,即使它编译也不是正确的用法。我将我创建的列表与de std :: list进行了比较,结果发现当你尝试在循环中使用相同的迭代器删除时,std :: list具有相同的输出。

这是我的清单(请忽略评论,它们是葡萄牙语):

template <typename T>
class Lista
{
 private:
  struct Node
  {
   Node()
   {
    prox = NULL;
    ant = NULL;
   }
   Node (T _data)
   {
    data = _data;
    prox = ant = NULL;
   }
   Node (T _data, Node * _prox, Node * _ant)
   {
    data = _data;
    prox = _prox;
    ant = _ant;
   }
   T data;  
   Node* prox; //Points to previous element
   Node* ant;  //Points to next elemente
  };
  Node* first; 
  Node* last;  
  unsigned length=0; //size
 public:

  //Classe iterador
  class iterator
  {
   public:
   friend class Lista<T>;

   Node * pNode;

   iterator(Node * _pNode)
   {
     pNode = _pNode;
   }

   iterator()
   {
    pNode = NULL;
   }

     void operator++()
     {
      if(pNode)
       pNode = pNode->prox;
     }
     void operator--()
     {
      if(pNode)
       pNode = pNode->ant;
     }
     void operator--(int)
     {
      if(pNode)
       pNode = pNode->ant;
     }
     void operator++(int)
     {
      if(pNode)
       pNode = pNode->prox;
     }
     bool operator==(iterator rval)
     {
      return (pNode == rval.pNode);
     }
     bool operator!=(iterator rval)
     {
      return !(pNode == rval.pNode);
     }
     T operator*()
     {
      if(pNode)
       return pNode->data;
      return T();
     }
     iterator operator+(unsigned _i)
     {
      iterator iter = *this;
      for (unsigned i = 0; i < _i; ++i)
      {
       if (iter.pNode)
        ++iter;
       else
         break;
      }
      return iter;
      }
  };

  //Classe iterador reverso
  class reverse_iterator
  {
    friend class Lista<T>;

    Node * pNode;

    reverse_iterator(Node * _pNode)
    {
     pNode = _pNode;
    }

    reverse_iterator()
    {
     pNode = NULL;
    }
     public:
      void operator++()
      {
       if(pNode)
        pNode = pNode->ant;
      }
      void operator--()
      {
       if(pNode)
        pNode = pNode->prox;
      }
      void operator--(int)
      {
       if(pNode)
        pNode = pNode->prox;
      }
      void operator++(int)
      {
       if(pNode)
        pNode = pNode->ant;
      }
      bool operator!=(reverse_iterator rval)
      {
       return !(pNode == rval.pNode);
      }
      bool operator==(reverse_iterator rval)
      {
       return (pNode == rval.pNode);
      }
      T operator*()
      {
       if(pNode)
        return pNode->data;
       return T();
      }
      reverse_iterator operator+(int _i)
      {
       reverse_iterator iter = *this;
       for (int i = 0; i < _i; ++i)
       {
        if (iter.pNode)
         ++iter;
        else
          break;
       }
       return iter;
       }
   };

  //Construtor Padrão
  Lista()
  {
   first = last = NULL;
  }

  //Contrutor "fill"(veja std::list para detalhes)
  Lista(unsigned n, const T& val)
  {
   first = last = NULL;
   for(unsigned i=0; i< n;i++)
    push_front(val);
  }

  //construtor "range"(veja std::list para detalhes)
  Lista(iterator inicio, iterator fim)
  {
   first = last = NULL;
   insert(begin(),inicio,fim);
  }

  //construtor por cópia
  Lista(const Lista& l)
  {
   first = last = NULL;
   Node* temp = l.first;
   while(temp != NULL)
   {
    push_back(temp->data);
    temp = temp->prox;
   }
  }

  //Destrutor
  ~Lista()
  {
   if (!empty())
   {
    Node * iter = first;
    Node * tmp = iter;
    while (iter != NULL)
    {
     tmp = iter;
     iter = iter->prox;
     delete tmp;
     length--;
    }
   }
  }

  //Retorna um iterador que aponta para o primeiro elemento da lista
  iterator begin()
  {
   return iterator(first);
  }

  //Retorna um iterador que aponta para o proximo elemento depois do ultimo("past-the-end")
  iterator end()
  {
   return iterator(NULL); // MUDANÇA AKI: DE LAST->PROX PRA NULL
  }

  //Retorna um iterador reverso que aponta para o ultimo elemento da lista
  reverse_iterator rbegin()
  {
   return reverse_iterator(last);
  }

  //Retorna um iterador reverso que aponta para o elemento teórico que antecede o primeior elemnto da lista
  reverse_iterator rend()
  {
   return reverse_iterator(NULL); // MUDANÇA AKI: DE FIRST->ANT PRA NULL
  }

  //Verifica se a lista está vazia, retornado "true" caso positivo
  bool empty()const
  {
   return (first == last && first == NULL);
  }

  //Retorna o numero de elementos da lista
  unsigned size()const
  {
   return length;
  }

  //Retorna uma referência direta para o primeiro elemento da lista(first)
  T front()
  {
   if(first)
   {
    return first->data;
   }
  }

  //Retorna uma referência direta para o ultimo elemento da lista(last)
  T back()
  {
   if(last)
   {
    return last->data;
   }
  }

  //Insere um novo elemento(nó) no inicio da lista
  void push_front(const T& val)
  {
   if(!empty())
   {
    Node* temp = new Node(val);
    temp->prox = first;
    first->ant = temp;
    first = temp;
    length++;
   }
   else
   {
    Node* temp = new Node(val);
    first = last = temp;
    length++;
   }
  }

  //Remove e destroi o primeiro elemento(nó) da lista
  void pop_front()
  {
    if(size() == 1)
    {
     Node* temp = first;
     first = last = NULL;
     delete temp;
     length--;
    }
    else if (size()>1)
    {
     Node* temp = first;
     first = first->prox;
     first->ant = NULL;
     delete temp;
     length--;
    }
  }

  //Insere um novo elemento(nó) no fim da lista
  void push_back(const T& val)
  {
   if(!empty())
   {
    Node* temp = new Node(val);
    last->prox = temp;
    temp->ant = last;
    last = temp;
    length++;
   }
   else
   {
    Node* temp = new Node(val);
    first = last = temp;
    length++;
   }
  }

  //Remove e destroi o ultimo elemento(nó) da lista
  void pop_back()
  {
   if(size() ==1)
   {
    Node* temp = last;
    first = last = NULL;
    delete temp;
    length--;
   }
   else if(size()>1)
   {
    Node* temp = last;
    last = last->ant;
    last->prox = NULL;
    delete temp;
    length--;
   }
  }

  //Insert "single element"(veja std::list para detalhes)
  iterator insert(iterator position,const T&val)
  {
   if(!empty())
   {
    if(position != begin() && position != end())
    {
     Node* temp = new Node(val);
     for(iterator it = begin();it!=end();it++)
     {
      if(it.pNode->prox == position.pNode)
      {
       it.pNode->prox = temp;
       temp->ant = it.pNode;
       temp->prox = position.pNode;
       position.pNode->ant = temp;
       length++;
       return iterator(temp);
      }
     }
    }
    else
    {
     if(position == begin())
     {
      push_front(val);
      return iterator(first);
     }
     else if (position == end())
     {
      push_back(val);
      return iterator(last);
     }
    }
   }
   push_front(val);
   return iterator(first);
  }

  //Insert "fill"(veja std::list para detalhes)
  void insert (iterator position, unsigned n, const T& val)
  {
   iterator it = position;
   while(n)
   {
    it = insert(it,val);
    n--;
   }

  }

  //Insert "range"(veja std::list para detalhes)
  void insert (iterator position, iterator primeiro, iterator ultimo)
  {
   if(position != NULL)
   {
    if(ultimo != end() && primeiro != end())
    {
     iterator it = position;
     while( primeiro != ultimo)
     {
      it = insert(it,*ultimo);
      ultimo--;
     }
     it = insert(it,*primeiro);
    }
    else if( ultimo == end() && primeiro != end())
    {
     ultimo = primeiro;
     iterator it = position;
     do
     {
      ultimo++;
     }while(ultimo.pNode->prox);

     while( primeiro != ultimo)
     {
      it = insert(it,*ultimo);
      ultimo--;
     }
     it = insert(it,*primeiro);
    }
   }
   else
   {
    first = last = NULL;
    if(primeiro.pNode != NULL && ultimo.pNode != NULL)
    {
     while(primeiro != ultimo)
     {
      push_front(*ultimo);
      ultimo--;
     }
     push_front(*primeiro);
    }
    else
    {
     if(ultimo.pNode == NULL && primeiro.pNode != NULL)
     {
      ultimo = primeiro;
      do
      {
       ultimo++;
      }while(ultimo.pNode->prox);

      while( primeiro != ultimo)
      {
       push_front(*ultimo);
       ultimo--;
      }
      push_front(*primeiro);
     }
    }
   }
  }

  //Remove um único elemento(nó) de determinada posição da lista,retornando um iterador que aponta para o elemento seguinte
  //aquele removido/destruido por ultimo.
  iterator erase (iterator position)
  {
   if(!empty())
   {
    if(position != end())
    {
     if(size() == 1)
     {
      pop_front();
      return end();
     }
     else
     {
      iterator it = begin();
      while(it != end())
      {
       if(it == position)
       {
        if(position != begin() && position.pNode != last) //MUDANÇA: position != end() para position.pNode != last
        {
         Node* temp = position.pNode->ant;
         position++;
         temp->prox = position.pNode;
         position.pNode->ant = temp;
         delete it.pNode;
         length--;
         return position;
        }
        else
        {
         if(position == begin())
         {
          //Node* temp = first; //MUDANÇA: Tirou o novo nó
          position = it.pNode->prox; //MUDANÇA: position = temp->prox; para position = it.pNode->prox;
          first = position.pNode;
          first->ant = NULL; //ADICIONADO
          delete it.pNode; //MUDANÇA: delete tempo para delete it.pNOde
          length--;
          return position;
         }
         else if(position.pNode == last) //MUDANÇA: position != end() para position.pNode != last
         {
          //Node* temp = last; //MUDANÇA: Tirou o novo nó
          position = end(); //MUDANÇA: position =  temp->prox para position = end()
          last = it.pNode->ant; //MUDANÇA: last = position.pNode para last = it.pNode->ant
          last->prox = NULL; //ADICIONADO
          delete it.pNode; //MUDANÇA: delete tempo para delete it.pNOde
          length--;
          return position;
         }
        }
       }
       it++;
      }
     }
    }
    else
     return end();
   }
   else
     return end();
  }

  //Remove todos elementos(nós) de um intervalo da lista(inclusive o apontado por "primeiro" e exclusive o apontado por "ultimo"),
  //retornando um iterador que aponta para o elemento seguinte aquele removido/destruido por ultimo.
  iterator erase (iterator primeiro, iterator ultimo)
  {
   if(ultimo.pNode)
   {
    for(iterator it = primeiro; it != ultimo;)
     it = erase(it);
    return ultimo;
   }
   else
   {
    iterator itaux = begin()+(size()-1);
    for(iterator it = primeiro;it!=itaux;)
     it = erase(it);
    return ultimo;
   }
  }

  //Remove da lista todos os elementos(nós) cujo conteúdo se compara igualmente com "val"


     iteratorvoid remove (const T& val)
  {
    for(iterator it = begin();it != end();)
    {
     if(*it == val)
       it = erase(it);
     else
      it++;
    }
  }

  //Inverte a ordem dos elementos(nós) da lista
  void reverse()
  {
   if(empty() || size() == 1)
    return;
   else
   {
    Node* temp1 = first;
    Node* temp2 = temp1->prox;
    temp1->prox = NULL;
    temp1->ant = temp2;
    while(temp2 != NULL)
    {
     temp2->ant = temp2->prox;
     temp2->prox = temp1;
     temp1 = temp2;
     temp2 = temp2->ant;
    }
    last = first;
    first = temp1;
   }
  }
};

这是主要的(请忽略评论,它们是葡萄牙语):

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <list>

#include "Lista.h" // ------ ALTERAR AQUI

#define N 500

typedef Lista<int> lista_ref_type;
//typedef std::list< int > lista_ref_type;
typedef std::list< int > lista_type;

void compare( lista_type &lista, lista_ref_type &lista_ref ) {

    lista_type::iterator it = lista.begin();
    lista_ref_type::iterator it_ref = lista_ref.begin();
    while( it_ref != lista_ref.end() ) {
        assert( it != lista.end() );
        assert( *it == *it_ref);
        it++;
        it_ref++;
    }
    assert( it == lista.end() );
    return;
}

int main() {

    lista_ref_type lista_ref;
    lista_type lista;

    srand( time(NULL) );

    assert( lista_ref.size() == lista.size() );
    assert( lista_ref.empty() == lista.empty() );

    for( int i=0; i<N; i++) {
        int valor = rand();
        if( valor/RAND_MAX > 0.5 ) {
            lista_ref.push_back(valor);
            lista.push_back(valor);
        } else {
            lista_ref.push_front(valor);
            lista.push_front(valor);
        }
        assert( lista_ref.size() == lista.size() );
        assert( lista_ref.empty() == lista.empty() );
    }

    compare( lista, lista_ref);
    std::cout << "Etapa 1: OK!" << std::endl;

    for( int i=0; i<N; i++) {
        assert( lista_ref.size() == lista.size() );
        assert( lista_ref.empty() == lista.empty() );
        assert( lista_ref.front() == lista.front() );
        assert( lista_ref.back() == lista.back() );

        int valor1 = rand()%lista.size();

        lista_type::iterator it = lista.begin();
        lista_ref_type::iterator it_ref = lista_ref.begin();
        while(valor1) {
            assert( it_ref != lista_ref.end() );
            assert( it != lista.end() );
            assert( *it == *it_ref);
            it++;
            it_ref++;
            valor1--;
        }
        assert( *it == *it_ref);
        //lista.remove( *it );                     //THE PROBLEM IS HERE. I CANT CALL REMOVE(*IT) IN THIS LOOP
        //lista_ref.remove( *it_ref );              //THE PROBLEM IS HERE. I CANT CALL REMOVE(*IT) IN THIS LOOP
    }

    //assert( lista.empty() );
    //assert( lista_ref.empty() );
    std::cout << "Etapa 2: OK!" << std::endl;

    for( int i=0; i<N; i++) {
        int valor = rand();
        if( valor/RAND_MAX > 0.5 ) {
            lista_ref.push_back(valor);
            lista.push_back(valor);
        } else {
            lista_ref.push_front(valor);
            lista.push_front(valor);
        }
    assert( lista_ref.size() == lista.size() );
        //assert( lista_ref.empty() == lista.empty() );
    }

    compare( lista, lista_ref);
    std::cout << "Etapa 3: OK!" << std::endl;

    lista.reverse();
    lista_ref.reverse();

    compare( lista, lista_ref);
    std::cout << "Etapa 4: OK!" << std::endl;

    for( int i=0; i<N; i++) {
        assert( lista_ref.size() == lista.size() );
        //assert( lista_ref.empty() == lista.empty() );
        assert( lista_ref.front() == lista.front() );
        assert( lista_ref.back() == lista.back() );
        int valor = rand();
        if( valor/RAND_MAX > 0.5 ) {
            lista_ref.pop_back();
            lista.pop_back();
        } else {
            lista_ref.pop_front();
            lista.pop_front();
        }
    }

    //assert( lista.empty() );
    //assert( lista_ref.empty() );
    std::cout << "Etapa 5: OK!" << std::endl;

    for( int i=0; i<N; i++) {
        assert( lista_ref.size() == lista.size() );
        int valor1 = rand()%(lista.size()+1);
        int valor2 = rand();
        if(valor1 == lista.size())
            valor1 = 0;

        lista_type::iterator it = lista.begin();
        lista_ref_type::iterator it_ref = lista_ref.begin();
        while( it_ref != lista_ref.end() && valor1) {
            assert( it != lista.end() );
            assert( *it == *it_ref);
            it++;
            it_ref++;
            valor1--;
        }
        lista.insert( it, valor2);
        lista_ref.insert( it_ref, valor2);
    }

    compare( lista, lista_ref);
    std::cout << "Etapa 6: OK!" << std::endl;

    for( int i=0; i<N; i++) {
        assert( lista_ref.size() == lista.size() );
        int valor1 = rand()%lista.size();
        int valor2 = rand();

        lista_type::iterator it = lista.begin();
        lista_ref_type::iterator it_ref = lista_ref.begin();
        while(valor1) {
            assert( it_ref != lista_ref.end() );
            assert( it != lista.end() );
            assert( *it == *it_ref);
            it++;
            it_ref++;
            valor1--;
        }
        lista.erase( it);
        lista_ref.erase( it_ref );
    }

    //assert( lista.empty() );
    //assert( lista_ref.empty() );
    std::cout << "Etapa 7: OK!" << std::endl;

    std::cout << "UFA!" << std::endl;

    return 0;
}

2 个答案:

答案 0 :(得分:2)

在删除之前,您需要将迭代器推进到您要移除的元素之外。

像(未经测试)

之类的东西
    for(iterator it = begin();it != end();)
    {
     iterator current = it++;

     if(*current == val)
       erase(current);
    }

或者,当然,使用库算法http://www.sgi.com/tech/stl/remove_if.html

答案 1 :(得分:0)

对于仅删除第一个项目的特定情况,保证至少剩余一个项目,您所要做的就是在删除之前移动迭代器。

但是,一般来说,你甚至不知道迭代器可以引用的是否还有任何项目。

因此,对于一般情况,你必须决定你想要什么,并考虑没有剩下的情况。