Documente Academic
Documente Profesional
Documente Cultură
Curs13 Iteratori
Curs13 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>
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;
// dimensiunea e constanta
size_type size() const { return dim; }
size_type max_size() const { return dim; }
#include <algorithm>
#include <functional>
#include "carray.hpp"
#include "print.hpp"
using namespace std;
int main()
{
carray<int,10> 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
};
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 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));
}
#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;
}
int main()
{
// creaza istream iterator care citeste intregi de la cin
istream_iterator<int> intReader(cin);