Sunteți pe pagina 1din 7

Iteratori.

Iteratorii sunt generalizri ale pointerilor; acetia ne permit s lucrm cu diverse structuri de date (containere) n mod uniform. Un iterator este un obiect care servete pentru a parcurge un container, ntr-un mod asemntor pointerilor n cazul vectorilor. Iteratorii pot fi incrementai cu ++, derefereniai cu * i comparai cu !=. Containerele pot genera iteratori cu funciile begin() i end(). Funcia begin() ntoarce o valoare de iterator pe primul element din container. Exemplu: vector<int> v(10); vector::iterator it=v.begin(); Funcia end() poziioneaz 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 (exceptnd desigur v.end()) pot fi derefereniate i *it reprezint un element din container avnd tipul value_type. Algoritmii generici acioneaz asupra containerelor prin intermediul iteratorilor. Astfel algoritmul copy() utilizeaz, pentru a copia o poriune 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 poziie din containerul destinaie: copy(cs.begin(),cs.end(),cd.begin()); Iteratorii pot fi mprii n categorii de iteratori, n funcie de operaiile care se pot efectua asupra lor. ( iteratori de intrare, iteratori de ieire, iteratori de avans, iteratori bidirecionali i iteratori cu acces direct). Aceste clase formeaz o ierarhie avnd la baz o clas de iteratori cu posibiliti foarte limitate (iteratorii de intrare i de ieire), iar clasele derivate au posibiliti mai mari. struct struct struct struct struct input_iterator_tag{}; output_iterator_tag{}; forward_iterator_tag : public input_iterator_tag{}; bidirectional_iterator_tag : public forward_iterator_tag{}; random_access_iterator_tag : public bidirectional_iterator_tag{}; Aa cum avem pointeri la obiecte const, putem defini iteratori la elemente const: vector::const_iterator it; Exist trei tipuri de adaptori ai iteratorilor: iteratori inveri (reverse iterators), iteratori de inserie (insert iterators) i iteratori pe memorie (raw storage iterators). Iteratorii inveri inverseaz comportarea operatorilor ++ i --. Toate containerele standard asigur funciile rbegin() i rend(), asigurnd poziionarea pe ultimul element, respectiv naintea primului element din container. Iteratorii pe memorie realizeaz n mod eficient copierea unui container ntr-o zon de memorie neiniializat, folosind funciile get_temporary_buffer() i return_temporary_buffer() . Toi iteratorii (exceptnd iteratorii de ieire) asigur o funcie distan ntre doi iteratori, avnd un tip asociat difference_type. Un iterator este minimal caracterizat prin tipul valorii elementelor containerului i tipul funciei distan specifice containerului asociat. Fiecrui tip de iterator i se asociaz o clas de proprieti (sau trsturi) iterator_traits, care conine: o serie de nume de tipuri recunoscute de iterator precum: tipul elementelor containerului ( value_type), tipul distanei (difference_type), tipul dimensiunii containerului (size_type), tipul referin (reference) i pointer (pointer) asociate elementelor containerului, categoria iteratorului (iterator_category): Clasele de proprieti servesc deci pentru exportul unor nume de tipuri din clasa iterator. Clasa de proprieti 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; };

n cazul n care iteratorul este un simplu pointer (pentru vectori i tablouri predefinite C), distana ntre iteratori se calculeaz printr-o simpl scdere i are tipul predefinit ptrdiff_t, avem particularizarea (specializarea) clasei de proprieti: 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 proprieti pot fi date ca parametrii ai ablonului. n acest caz, clasa de proprieti 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 funciei distan ntre doi iteratori. n caz c iteratorii sunt de intrare, distana este numrul de deplasri necesar ajungerii n poziia 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, distana 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 semnturile celor dou funcii. Cele dou funcii 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 begin()/end() pentru container. STL definesc tipurile: iterator i const_iterator i metodele

Clasele container definite de utilizator pot utiliza algoritmi generici numai dac le asociem clase iteratori corespunztoare. Dac o clas container definit de utilizator folosete un container standard, atunci trebuie s delegm 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();};

// alte metode } Definii un container avnd 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(), a.begin(), negate<int>()); afisare(a); // sursa // destinatie // operatie

Pentru clasele vector i string iteratorii sunt chiar pointeri, aa c delegarea iteratorilor este mai simpl:

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 servete pentru a citi dintr-un container. Un iterator de intrare it ofer: incrementare (it++ sau ++it), derefereniere pentru acces la informaie(*it n dreapta unei atribuiri -rvalue) i comparaie cu alt iterator (prin == sau !=). Informaia indicat de iterator poate fi numai accesat (citit), nu i modificat (adic nu este permis o operaie 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 bazai 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 ieire (OutputIterator).


Un iterator de ieire transfer informaie n container (scriere ntr-o singur trecere). Operaiile permise sunt incrementarea (it++ sau ++it) i dereferenierea pentru modificare informaie lvalue (*it=...).Iteratorii de ieire nu pot fi comparai cu operatorii == sau !=.

Iteratori de inserie.
Unii algoritmi generici depun rezultatele ntr-un container. Aceasta impune rezervarea unui spaiu fixat n containerul destinaie. Dac numrul elementelor din rezultat nu poate fi cunoscut dinainte, vom utiliza un adaptor de inserie pentru a crea elemente n containerul destinaie, atunci cnd avem nevoie. Un iterator de inserie adapteaz un iterator de ieire pentru a asigura anumite funcionaliti. Iteratorii de inserie pot fi:

back_inserter<container> care ntoarce un iterator de ieire, prin intermediul cruia se adaug o valoare la sfritul containerului prin operaia 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 ieire la nceputul containerului, prin intermediul cruia se adaug o valoare la nceputul containerului prin operaia push_front() inserter<container, iterator> care ntoarce un iterator de ieire la poziia din container indicat de iterator, prin intermediul cruia se adaug o valoare prin operaia insert(). De exemplu copierea tuturor elementelor lui s n d se poate face prin:

copy(s.begin(), s.end(), inserter(d, d.begin()));

Iteratori flux de ieire (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() avnd un parametru iterator destinaie va scrie datele de tip T n fluxul os, separndu-le cu delimitatorul dat ca al doilea parametru. Exemple: //definire iterator pe fluxul de ieire 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 fiier, cu numele sursei i destinaiei 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>() definete un iterator pentru un obiect istream. Acesta permite extracia de informaii 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. Declaraia de mai sus construiete un iterator care acceseaz un obiect istream. Pentru a defini sfritul fluxului se folosete 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 construiete un iterator pe fluxul cin, al doilea argument creaz un pointer la sfritul intrrii. Copierea se face prin adugare la sfrit n containerul v. Urmtorul program citete 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 spaiu. 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)

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 posibilitile iteratorilor de intrare i de ieire, permind citirea din i scrierea n container.

Iteratori bidirecionali (BidirectionalIterator).


Au aceleai posibiliti ca iteratorii de avans, permind, n plus, operaia de decrementare (asigurndu-ne astfel traversarea n ambele direcii ale unui container).

Iteratori cu acces direct (RandomAccessIterator).


Un iterator cu acces direct permite accesul independent la orice element al containerului. Fa de iteratorii bidirecionali permit operaii aritmetice extinse asupra iteratorilor n timp constant(it+n, it-n, it+=n, it-=n i it1-it2) i operaii de comparaie extinse (it1<it2, it1>it2, it1<=it2, it1>=it2). O funcie 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 funcie 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; } Funcia definit mai sus nu funcioneaz pentru tablouri predefinite. Dac transmitem ca parametru un iterator la container: template <class Iter> double suma(Iter start, Iter stop){ double s=0.;

while(start!=stop) s+= *start++; return s; } vom putea folosi funcia 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