Domanda

C'è un modo per verificare se un iteratore (se si tratta di un vettore, un elenco, una deque ...) è (ancora) dereferencable, vale a dire non è stato invalidato?

Sono stato con try-catch, ma c'è un modo più diretto per fare questo?

Esempio: (che non funziona)

list<int> l;
for (i = 1; i<10; i++) {
    l.push_back(i * 10);
}

itd = l.begin();
itd++;
if (something) {
    l.erase(itd);
}

/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}
È stato utile?

Soluzione

I assume you mean "è un iteratore valido", che non è stata invalidata a causa di modifiche al contenitore (ad esempio, l'inserimento / cancellazione a / da un vettore). In questo caso, no, non è possibile determinare se un iteratore è (in modo sicuro) dereferencable.

Altri suggerimenti

Come ha detto jdehaan, se l'iteratore non è stato invalidato e punti in un contenitore, è possibile controllare paragonandolo alla container.end().

Si noti, tuttavia, che se l'iteratore è singolare - perché non è stato inizializzato o è diventata invalida dopo un'operazione di mutazione sul contenitore (iteratori del vettore vengono invalidate quando si aumenta la capacità del vettore , per esempio) - l'unica operazione che si è permesso di eseguire su di essa è assegnazione. In altre parole, non è possibile verificare se un iteratore è singolare o meno.

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not

Non portabile risposta: Sì - in Visual Studio

iteratori STL Visual Studio hanno una modalità "debug", che fanno esattamente questo. Lei non vorrebbe attivare questa in nave costruisce (c'è in testa), ma utile nel controllare costruisce.

letto su qui ( questo sistema può e infatti non cambiare ogni release, in modo da trovare la documentazione specifici per la versione).

Modifica Inoltre, devo aggiungere: iteratori di debug in Visual Studio sono stati progettati per esplodere immediatamente quando li (comportamento invece non definito) si utilizza; di non permettere "interrogazione" del loro stato.

Di solito si prova verificando se è diverso dalla fine (), come

if (it != container.end())
{
   // then dereference
}

Inoltre utilizzando la gestione delle eccezioni per la sostituzione logica è male in termini di progettazione e prestazioni. La sua domanda è molto buona ed è definitivamente la pena di una sostituzione nel codice. La gestione delle eccezioni come i nomi dice deve essere utilizzato solo per problemi imprevisti rare.

  

C'è un modo per verificare se un iteratore (se si tratta di un vettore, un elenco, una deque ...) è (ancora) dereferencable, cioè non è stato invalidato?

No, non c'è. Invece è necessario controllare l'accesso al contenitore mentre l'iteratore esiste, ad esempio:

  • Il filo non deve modificare il contenitore (invalidando l'iteratore) mentre è ancora utilizzando un iteratore istanziato per quel contenitore

  • Se c'è il rischio che altri thread possono modificare il contenitore, mentre il thread è l'iterazione, quindi al fine di rendere questo scenario thread-safe tuo thread deve acquisire un qualche tipo di blocco sul contenitore (in modo da evitare altri filetti da modificare il contenitore mentre sta usando un iteratore)

Work-around come la cattura un'eccezione non funzionerà.

Questa è una specifica istanza del problema più generale, "posso provare / rilevare se un puntatore è valida?", La risposta che in genere è "no, non è possibile verificare per questo: invece si deve gestire tutte le allocazioni di memoria e le cancellazioni al fine di so se una data puntatore è ancora valido".

Cercando e la cattura non è sicuro, non lo farai, o almeno raramente gettare se il vostro iteratore è "fuori limite".

quello alemjerus dire, un iteratore può sempre essere dereferenziato. Non importa quale uglyness sta sotto. E 'del tutto possibile iterare in altre aree di memoria e scrivere ad altre aree che potrebbero tenere altri oggetti. Sono stato a guardare il codice, a guardare le variabili cambiano per nessun motivo particolare. Questo è un bug che è davvero difficile da individuare.

Inoltre è bene ricordare che l'inserimento e la rimozione di elementi potrebbero potenzialmente invalidare tutti i riferimenti, puntatori e iteratori.

Il mio miglior consiglio è di tenere sotto controllo iteratori, e tenere sempre un iteratore "fine" a portata di mano per essere in grado di verificare se si è alla "fine della linea" per così dire.

In alcuni dei contenitori STL, l'iteratore corrente non è più valido quando si cancella il valore corrente del iteratore. Ciò accade perché l'operazione di cancellazione cambia la struttura memoria interna del contenitore e l'incremento operatore punti iteratore esistenti per una località indefiniti.

Quando si esegue il seguente, iteratore è incementented prima che si passi per cancellare la funzione.

if (something) l.erase(itd++);

  

C'è un modo per verificare se un iteratore è dereferencable

Sì, con gcc contenitori debug disponibile come estensioni GNU. Per std::list è possibile utilizzare __gnu_debug::list invece. Il seguente codice interrompe non appena iteratore non valido è tentato di essere utilizzato. Come contenitori di debug impongono overhead aggiuntivo sono destinati soltanto quando debug.

#include <debug/list>

int main() {
  __gnu_debug::list<int> l;
  for (int i = 1; i < 10; i++) {
    l.push_back(i * 10);
  }

  auto itd = l.begin();
  itd++;
  l.erase(itd);

  /* now, in other place.. check if itd points to somewhere meaningful */
  if (itd != l.end()) {
    //  blablabla
  }
}

$ ./a.out 
/usr/include/c++/7/debug/safe_iterator.h:552:
Error: attempt to compare a singular iterator to a past-the-end iterator.

Objects involved in the operation:
    iterator "lhs" @ 0x0x7ffda4c57fc0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = singular;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
    iterator "rhs" @ 0x0x7ffda4c580c0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = past-the-end;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
Aborted (core dumped)

Il tipo di parametri della funzione di cancellazione di qualsiasi contenitore std (come hai elencato nella tua domanda, vale a dire se si tratta di un vettore, un elenco, una deque ...) è sempre iteratore di questo contenitore solo .

Questa funzione utilizza la prima data iteratore escludere dal contenitore dell'elemento che questi punti iteratore in e anche quelli che seguono. Alcuni contenitori cancelli solo un elemento per un iteratore, e alcuni altri contenitori cancelli tutti gli elementi seguiti da un iteratore (comprendente l'elemento rilevato da questo iteratore) alla estremità del contenitore. Se la funzione di cancellazione riceve due iteratori, poi i due elementi, a punta da ciascun iteratore, vengono cancellati dal contenitore e tutti gli altri fra loro sono cancellati dal contenitore così, ma il punto è che ogni iteratore che viene passato alla funzione di cancellazione di qualsiasi contenitore std non è più valido! anche :

Ogni iteratore che stava indicando qualche elemento che è stato cancellato dal contenitore non è più valido, ma non passa l'estremità del contenitore!

Ciò significa che un iteratore che indicava qualche elemento che è stato cancellato dal contenitore non può essere paragonato a container.end (). Questo iteratore non è valido, e quindi non è dereferencable, cioè non è possibile utilizzare né il * né -> gli operatori, è, inoltre, non incrementabile, cioè non è possibile utilizzare l'operatore ++, ed è, inoltre, non decrementable, cioè non è possibile utilizzare il -. operatore

Inoltre non è paragonabile !!! OSSIA non è possibile anche utilizzare né == nè! = operatori

In realtà non è possibile utilizzare qualsiasi operatore che viene dichiarato e definito nel iteratore std. Non si può fare qualsiasi cosa con questo iteratore, come puntatore nullo.

fare qualcosa con un iteratore valido arresta immediatamente il programma e anche causa il crash del programma e viene visualizzata una finestra di dialogo asserzione. Non v'è alcun modo per continuare il programma non importa ciò che le opzioni che si sceglie, quali pulsanti si sceglie. È solo possibile terminare il programma e il processo facendo clic sul pulsante Interrompi.

Non fate qualsiasi altra cosa con un iteratore non valido, a meno che non è possibile impostare per l'inizio del contenitore, o semplicemente ignorarlo.

Ma prima di decidere cosa fare con un iteratore, in primo luogo è necessario sapere se questo iteratore non è valido o no, se si chiama la funzione di cancellazione del contenitore che si sta utilizzando.

ho fatto da solo una funzione che controlla, test, conosce e restituisce true se un determinato iteratore è valido o meno. È possibile utilizzare la funzione memcpy per ottenere lo stato di qualsiasi oggetto, oggetto, struttura, di classe ed ecc, e, naturalmente, abbiamo sempre utilizzare la funzione memset in un primo momento a chiare o svuotare un nuovo buffer, la struttura, di classe o di qualsiasi oggetto o un elemento :

bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary.
{
    byte buffer[sizeof(i)];
    memset(buffer, 0, sizeof(i));
    memcpy(buffer, &i, sizeof(i));
    return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators".
}

ho già testato questa funzione prima ho postato lì e ho trovato che questa funzione sta lavorando per me.

Mi auguro che molto ho pienamente risposto alla tua domanda e anche aiutato molto!

if (iterator != container.end()) {
   iterator is dereferencable !
}

Se il vostro iteratore doesnt uguale container.end(), e non è dereferencable, stai facendo qualcosa di sbagliato.

utilizzare cancellare con minimo:

   if (something) l.erase(itd++);

in modo da poter verificare la validità del iteratore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top