Sunteți pe pagina 1din 7

Iteratori.

Iteratorii sunt generalizări ale pointerilor; aceştia ne permit să lucrăm cu diverse structuri de date (containere) în mod
uniform.
Un iterator este un obiect care serveşte pentru a parcurge un container, într-un mod asemănător pointerilor în cazul
vectorilor.
Iteratorii pot fi incrementaţi cu ++, dereferenţiaţi cu * şi comparaţi cu !=. Containerele pot genera iteratori cu
funcţiile begin() şi end().
Funcţia begin() întoarce o valoare de iterator pe primul element din container. Exemplu:
vector<int> v(10);
vector::iterator it=v.begin();
Funcţia end() poziţionează iteratorul după ultimul element din container. Intervalul [v.begin(), v.end())
reprezintă un domeniu sau interval de iteratori. Toate elementele it[v.begin(), v.end()) din acest domeniu
(exceptând desigur v.end()) pot fi dereferenţiate şi *it reprezintă un element din container având tipul value_type.
Algoritmii generici acţionează asupra containerelor prin intermediul iteratorilor. Astfel algoritmul copy() utilizează,
pentru a copia o porţiune din containerul cs în containerul cd, trei iteratori: un iterator pe primul element copiat din
containerul sursă, un iterator după ultimul element copiat din containerul sursă şi un iterator la prima poziţie din containerul
destinaţie:
copy(cs.begin(),cs.end(),cd.begin());
Iteratorii pot fi împărţiţi în categorii de iteratori, în funcţie de operaţiile care se pot efectua asupra lor. (iteratori de
intrare, iteratori de ieşire, iteratori de avans, iteratori bidirecţionali şi iteratori cu acces direct). Aceste clase formează o
ierarhie având la bază o clasă de iteratori cu posibilităţi foarte limitate (iteratorii de intrare şi de ieşire), iar clasele derivate au
posibilităţi mai mari.
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag : public input_iterator_tag{};
struct bidirectional_iterator_tag : public forward_iterator_tag{};
struct random_access_iterator_tag : public bidirectional_iterator_tag{};
Aşa cum avem pointeri la obiecte const, putem defini iteratori la elemente const:
vector::const_iterator it;
Există trei tipuri de adaptori ai iteratorilor: iteratori inverşi (reverse iterators), iteratori de inserţie
(insert iterators) şi iteratori pe memorie (raw storage iterators).
Iteratorii inverşi inversează comportarea operatorilor ++ şi --. Toate containerele standard asigură funcţiile
rbegin() şi rend(), asigurând poziţionarea pe ultimul element, respectiv înaintea primului element din container.
Iteratorii pe memorie realizează în mod eficient copierea unui container într-o zonă de memorie neiniţializată,
folosind funcţiile get_temporary_buffer() şi return_temporary_buffer() .
Toţi iteratorii (exceptând iteratorii de ieşire) asigură o funcţie distanţă între doi iteratori, având un tip asociat
difference_type.
Un iterator este minimal caracterizat prin tipul valorii elementelor containerului şi tipul funcţiei distanţă specifice
containerului asociat.
Fiecărui tip de iterator i se asociază o clasă de proprietăţi (sau trăsături) iterator_traits, care conţine: o serie
de nume de tipuri recunoscute de iterator precum: tipul elementelor containerului (value_type), tipul distanţei
(difference_type), tipul dimensiunii containerului (size_type), tipul referinţă (reference) şi pointer (pointer)
asociate elementelor containerului, categoria iteratorului (iterator_category): Clasele de proprietăţi servesc deci pentru
exportul unor nume de tipuri din clasa iterator.
Clasa de proprietăţi trebuie specificată pentru fiecare tip iterator nou definit.
template <class Itor>
struct iterator_traits{
typedef typename Itor::value_type value_type;
typedef typename Itor::difference_type difference_type;
typedef typename Itor::pointer pointer;
typedef typename Itor::reference reference;
typedef typename Itor::iterator_category iterator_category;
};
1
În cazul în care iteratorul este un simplu pointer (pentru vectori şi tablouri predefinite C), distanţa între iteratori se
calculează printr-o simplă scădere şi are tipul predefinit ptrdiff_t, avem particularizarea (specializarea) clasei de
proprietăţi:
template <class T>
struct iterator_traits<T*>{
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
tzpedef size_t size_type;
typedef random_access_iterator_tag iterator_category;
};
Numele tipurilor care apar în clasa de proprietăţi pot fi date ca parametrii ai şablonului. În acest caz, clasa de
proprietăţi precedentă va avea forma:
template <class Categ, class T, class Dist=ptrdiff_t,class Ptr=T*,class Ref=T& >
struct iterator_traits{
typedef T value_type;
typedef Dist difference_type;
typedef Ptr pointer;
typedef Ref reference;
typedef Categ iterator_category;
};
Vom exemplifica definirea funcţiei distanţă între doi iteratori. În caz că iteratorii sunt de intrare, distanţa este numărul
de deplasări necesar ajungerii în poziţia indicată de cel de-al doilea iterator:
template <class InItor>
typename iterator_traits<InItor>::difference_type dist(InItor i1, InItor i2,
input_iterator_tag){
typename iterator_traits<InItor>::difference_type d=0;
while(i1++ != i2)
d++;
return d;
}
În cazul iteratorilor cu acces direct, distanţa se calculează prin simpla diferenţă a iteratorilor:
template <class RAItor>
typename iterator_traits<RAItor>::difference_type dist(RAItor i1, RAItor i2,
random_acces_iterator_tag){
return i2-i1;
}
Se observă că cel de-al treilea paramatru a fost bdat numai pentru a deosebi semnăturile celor două funcţii. Cele două
funcţii pot fi comasate într-una singură:
template <class Itor>
typename iterator_traits<Itor>::difference_type dist(Itor i1, Itor i2){
return dist(i1, i2, iterator_traits<Itor>::iterator_category());
};
Toate containerele standard STL definesc tipurile: iterator şi const_iterator şi metodele
begin()/end() pentru container.
Clasele container definite de utilizator pot utiliza algoritmi generici numai dacă le asociem clase iteratori
corespunzătoare.
Dacă o clasă container definită de utilizator foloseşte un container standard, atunci trebuie să delegăm containerului
utilizator tipurile şi metodele clasei iterator asociate containerului standard. De exemplu:
//delegarea iteratorilor clasei vector catre clasa grupa
class grupa{
typedef vector<char*> gr_stud;
gr_stud g;
public:
typedef gr_stud::iterator iterator;//preluam iterator din vector
typedef gr_stud::const_iterator const_iterator;
iterator begin(){return g.begin();};
iterator end(){return g.end();};
2
// alte metode
}
Definiţi un container având tipul elementelor şi dimensiunea parametrizată, pe baza tablourilor predefinite din C.
// preluat din Jossutis The C++ Standard Library - A Tutorial and Reference
#include <cstddef>

template<class T, std::size_t dim>


class carray {
private:
T v[dim]; // tablou predefinitcu elemente de tip T si dimensiune fixata

public:
// definiri de tipuri
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;

// suport pentru iteratori


iterator begin() { return v; }
const_iterator begin() const { return v; }
iterator end() { return v+dim; }
const_iterator end() const { return v+dim; }

// acces direct la elemente


reference operator[](std::size_t i) { return v[i]; }
const_reference operator[](std::size_t i) const { return v[i]; }

// dimensiunea e constanta
size_type size() const { return dim; }
size_type max_size() const { return dim; }

// conversie la tablou obisnuit


T* as_array() { return v; }
};

#include <algorithm>
#include <functional>
#include "carray.hpp"
#include "print.hpp"
using namespace std;

int main()
{
carray<int,10> a;

for (unsigned i=0; i<a.size(); ++i) {


a[i] = i+1;
}
afisare(a);

reverse(a.begin(),a.end());
afisare(a);

transform(a.begin(),a.end(), // sursa
a.begin(), // destinatie
negate<int>()); // operatie
afisare(a);

Pentru clasele vector şi string iteratorii sunt chiar pointeri, aşa că delegarea iteratorilor este mai simplă:
3
O clasă iterator are forma:
template <class T>
class Itor{
public:
// constructori, destructor
bool operator!=(const Itor<T>&) const;
bool operator==(const Itor<T>&) const;
Itor<T>& operator++(); //prefix
Itor<T> operator++(int); //postfix
T& operator*() const;
T* operator->() const;
private:
//asocierea cu containerul
};

Iteratori de intrare (InputIterator).


Un iterator de intrare serveşte pentru a citi dintr-un container.
Un iterator de intrare it oferă: incrementare (it++ sau ++it), dereferenţiere pentru acces la informaţie(*it în
dreapta unei atribuiri -rvalue) şi comparaţie cu alt iterator (prin == sau !=).
Informaţia indicată de iterator poate fi numai accesată (citită), nu şi modificată (adică nu este permisă o operaţie de
tipul *it=15 ). Exemplu:

vector<int> x;
vector<int>::iterator itv;
v.push_back(15);
v.push_back(24);
for(itv=v.begin(); itv!=v.end(); itv++)
cout << *itv;
// operatia cin >> *itv este incorecta
Algoritmii bazaţi pe iteratori de intrare sunt cu o trecere unică, într-un singur pas şi nu se bazează pe valori precedente
din această trecere (iteratorul poate fi numai incrementat).

Iteratori de ieşire (OutputIterator).


Un iterator de ieşire transferă informaţie în container (scriere într-o singură trecere). Operaţiile permise sunt
incrementarea (it++ sau ++it) şi dereferenţierea pentru modificare informaţie – lvalue (*it=...).Iteratorii de ieşire nu
pot fi comparaţi cu operatorii == sau !=.

Iteratori de inserţie.
Unii algoritmi generici depun rezultatele într-un container. Aceasta impune rezervarea unui spaţiu fixat în containerul
destinaţie. Dacă numărul elementelor din rezultat nu poate fi cunoscut dinainte, vom utiliza un adaptor de inserţie pentru a
crea elemente în containerul destinaţie, atunci când avem nevoie.
Un iterator de inserţie adaptează un iterator de ieşire pentru a asigura anumite funcţionalităţi. Iteratorii de inserţie pot
fi:
 back_inserter<container> care întoarce un iterator de ieşire, prin intermediul căruia se adaugă o valoare la
sfârşitul containerului prin operaţia push_back().
De exemplu pentru a copia elementele containerului s în d în ordine inversă folosim:
copy(s.rbegin(), s.rend(), back_inserter(d));
 front_inserter<container> care întoarce un iterator de ieşire la începutul containerului, prin intermediul
căruia se adaugă o valoare la începutul containerului prin operaţia push_front()
 inserter<container, iterator> care întoarce un iterator de ieşire la poziţia din container indicată de
iterator, prin intermediul căruia se adaugă o valoare prin operaţia insert().
De exemplu copierea tuturor elementelor lui s în d se poate face prin:
copy(s.begin(), s.end(), inserter(d, d.begin()));

4
Iteratori flux de ieşire (ostream OutputIterator).
Pentru a defini un iterator pentru un obiect ostream folosim una din formele:
ostream_iterator<T> nume(ostream& os);
ostream_iterator<T> nume(ostream& os, const char* delimitator);
Un algoritm de tip copy() având un parametru iterator destinaţie va scrie datele de tip T în fluxul os, separându-le
cu delimitatorul dat ca al doilea parametru. Exemple:
//definire iterator pe fluxul de ieşire pentru întregi
//întregii sunt separati prin spatii
ostream_iterator<int> OutIt(cout, ” ”);
copy(v.begin(),v.end(),OutIt); // echivalent cu
// for(it=v.begin(); it!=v.end(); it++)
// cout << *it << ” ”;
Copierea unui fişier, cu numele sursei şi destinaţiei date ca parametri ai liniei de comandă, se realizează prin
programul:
#include <fstream>
#include <algorithm>
#include <iterator>
using namespace std;
void main(int ac, char** av){
ifstream s(av[1]);
ofstream d(av[2]);
copy(istream_iterator<char>(in),istream_iterator<char>(),
ostream_iterator<char>(d));
}

Iteratori flux de intrare (istream InputIterator).


Un iterator flux de intrare istream_iterator<T>() defineşte un iterator pentru un obiect istream. Acesta
permite extracţia de informaţii dintr-un flux de intrare într-un container.
Un iterator flux de intrare se declară.
istream_iterator<T> (istream& is);
în care T reprezintă tipul elementelor citite din flux. Declaraţia de mai sus construieşte un iterator care accesează un obiect
istream.
Pentru a defini sfârşitul fluxului se foloseşte constructorul implicit:
istream_iterator<T> ()
De exemplu citirea din fluxul de intrare a unor întregi într-un container vector se poate realiza cu:
vector<int> v;
istream_iterator<int>(cin) start;
istream_iterator<int>() stop;
copy(start, stop, back_inserter(v));
Primul argument construieşte un iterator pe fluxul cin, al doilea argument crează un pointer la sfârşitul intrării.
Copierea se face prin adăugare la sfârşit în containerul v.
Următorul program citeşte din fluxul cin mai multe şiruri de caractere, cu care creează un vector de şiruri şi le
inserează în fluxul cout separeater între ele cu un spaţiu. Citirea se face cu algoritmul copy cu iteratori pe fluxul de intrare.

#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
#include <iostream>
using namespace std;
void main(){
vector<string> vs;
copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vs));
typedef vector<string>::iterator vitor;
for(vitor i=vs.begin(); i!=vs.end(); ++i)

5
cout << *i << " ";
cout << endl;
}

//Nicolai M. Josuttis "The C++ Standard Library - A Tutorial and Reference"


#include <iostream>
#include <iterator>
using namespace std;

int main()
{
// creaza istream iterator care citeste intregi de la cin
istream_iterator<int> intReader(cin);

// creaza iterator end-of-stream


istream_iterator<int> intReaderEOF;

// cat timp putem citi elemente cu istream iterato le afisam


while (intReader != intReaderEOF) {
cout << *intReader << endl;
++intReader;
}
}

Iteratori de avans (ForwardIterator).


Iteratorii de avans combină posibilităţile iteratorilor de intrare şi de ieşire, permiţând citirea din şi scrierea în
container.
Iteratori bidirecţionali (BidirectionalIterator).
Au aceleaşi posibilităţi ca iteratorii de avans, permiţând, în plus, operaţia de decrementare (asigurându-ne astfel
traversarea în ambele direcţii ale unui container).
Iteratori cu acces direct (RandomAccessIterator).
Un iterator cu acces direct permite accesul independent la orice element al containerului. Faţă de iteratorii
bidirecţionali permit operaţii aritmetice extinse asupra iteratorilor în timp constant(it+n, it-n, it+=n, it-=n şi
it1-it2) şi operaţii de comparaţie extinse (it1<it2, it1>it2, it1<=it2, it1>=it2).
O funcţie care lucrează cu elementele unui container nu va transmite ca argument şi nici ca valoare întoarsă
containerul, ci un iterator la acesta. De exemplu o funcţie care calculează suma elementelor unui vector:
double suma(vector<double>v){
vector<double>::iterator i;
double s=0.;
for(i=v.begin(); i!=v.end(); i++)
s+=*i;
return s;
}
ar putea fi generalizată pentru orice container astfel:
template <class Cont>
double suma(const Cont& c){
Cont::iterator i;
double s=0.;
for(i=c.begin();i!=c.end(); i++)
s+=*i1;
return s;
}
Funcţia definită mai sus nu funcţionează pentru tablouri predefinite. Dacă transmitem ca parametru un iterator la
container:
template <class Iter>
double suma(Iter start, Iter stop){
double s=0.;
6
while(start!=stop)
s+= *start++;
return s;
}
vom putea folosi funcţia şi pentru tablouri predefinite:
double x[]={1.5,2.,3.5,6.2};
double sum = suma(x, x+4);

S-ar putea să vă placă și