Domanda

    

Questa domanda ha già una risposta qui:

         

Nella libreria STL alcuni contenitori hanno iteratori e si ritiene comunemente che siano un modo superiore di iterare attraverso questi contenitori piuttosto che semplici per i loop, ad esempio

for ( int i=0; i < vecVector.size(); i++ )
{

..

}

Qualcuno può dirmi perché e in quali casi dovrei usare iteratori e in quali casi lo snippet di codice sopra per favore?

È stato utile?

Soluzione

Nota che l'implementazione di solito del vettore non utilizzerà un " int " come tipo di indice / dimensione. Quindi il tuo codice provocherà almeno gli avvisi del compilatore.

La genericità

Gli iteratori aumentano la genericità del tuo codice.

Ad esempio:

typedef std::vector<int> Container ;

void doSomething(Container & p_aC)
{
    for(Container::iterator it = p_aC.begin(), itEnd = p_aC.end(); it != itEnd; ++it)
    {
       int & i = *it ; // i is now a reference to the value iterated
       // do something with "i"
    }
}

Ora, immaginiamo di cambiare il vettore in un elenco (perché nel tuo caso, l'elenco è ora migliore). Devi solo modificare la dichiarazione typedef e ricompilare il codice.

Se invece avessi usato il codice basato sull'indice, avrebbe dovuto essere riscritto.

Accesso

L'iteratore dovrebbe essere visualizzato come una sorta di super puntatore. & Quot; punti & Quot; al valore (o, nel caso di mappe, alla coppia chiave / valore).

Ma ha dei metodi per passare all'elemento successivo nel contenitore. O il precedente. Alcuni contenitori offrono anche un accesso casuale (il vettore e il deque).

Algoritmi

La maggior parte degli algoritmi STL funziona su iteratori o su intervalli di iteratori (di nuovo, a causa della genericità). Non sarai in grado di utilizzare un indice, qui.

Altri suggerimenti

L'uso di iteratori consente al codice di essere agnostico sull'implementazione del contenitore. Se l'accesso casuale per il tuo contenitore è economico, non c'è molta differenza dal punto di vista delle prestazioni.

Ma in molti casi non saprai se è così. Se si tenta di utilizzare il metodo in un elenco collegato, ad esempio, con la sottoscrizione, il contenitore dovrà percorrere l'elenco su ogni iterazione per trovare l'elemento.

Quindi, a meno che tu non sappia per certo che l'accesso casuale al tuo contenitore è economico, usa un iteratore.

Se si utilizzano iteratori come argomenti della propria funzione, è possibile separarlo dal tipo di " container " Usato. Ad esempio, è possibile indirizzare i risultati di una funzione all'output della console anziché a un vettore (esempio di seguito). Questo trucco può essere enormemente potente per ridurre l'accoppiamento tra le tue classi. Le classi liberamente accoppiate sono molto più facili da testare.

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

using namespace std;

template <typename InputIterator, typename OutputIterator>
void AddOne(InputIterator begin, InputIterator end, OutputIterator dest)
{
    while (begin != end)
    {
        *dest = *begin + 1;
        ++dest;
        ++begin;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    vector<int> data;
    data.push_back(1);
    data.push_back(2);
    data.push_back(3);

    // Compute intermediate results vector and dump to console
    vector<int> results;
    AddOne(data.begin(), data.end(), back_inserter(results));
    copy(results.begin(), results.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    // Compute results and send directly to console, no intermediate vector required
    AddOne(data.begin(), data.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
}

Nel tuo esempio, la chiamata a vecVector.size () è meno efficiente dell'uso di un iteratore. L'iteratore essenzialmente ti incapsula dal doverti preoccupare della dimensione del contenitore su cui viene ripetuto. Inoltre, non è necessario che l'iteratore vada in ordine sequenziale. Deve semplicemente rispondere a una chiamata .next in qualunque modo ritenga opportuno.

Bene, per prima cosa quanto sopra non funzionerà più se trasformi quel vettore in un elenco.

Gli iteratori ti consentono di creare modelli di funzioni che non necessitano di conoscere il tipo di contenitore su cui lavorano. Puoi anche fare quanto segue:

#include <algorithm>

void printvalue(double s)
{
    // Do something with s
}

int _tmain(int argc, _TCHAR* argv[])
{
    double s[20] = {0};

    std::for_each(s, s+20, printvalue);

    return 0;
}

Questo perché un puntatore standard è anche un iteratore valido per for_each.

Dave

L'iteratore è principalmente un livello più alto di astrazione.

Il frammento presuppone che il contenitore possa essere indicizzato. Questo vale per std::vector<> e per alcuni altri contenitori, ad esempio array non elaborati.

Ma std::set<> manca completamente dell'indicizzazione e l'operatore indice di std::map<> inserirà qualsiasi argomento fornito nella mappa - non il comportamento previsto nel tuo ciclo for -.

Inoltre, i problemi di prestazioni sono solo problemi se misurati e dimostrati.

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