Documente Academic
Documente Profesional
Documente Cultură
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 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:
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:
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; }
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);