C++/Iteratory: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Lethern (dyskusja | edycje)
+< source>, poprawki
Linia 5:
 
Istnieją pewne analogie między iteratorem a wskaźnikiem. Przede wszystkim znajomo wyglądają wyrażenia:
<source lang="cpp">
*nasz_iterator '' // wartością jest element pojemnika wskazywany przez iterator nasz_iterator''
wart = *nasz_iter ''// podstawienie wartości elementu pod zmienną wart''
wart = *nasz_iteratornasz_iter = wart ''// podstawienie wartości zmiennejelementu wpod miejscezmienną pojemnika wskazane przez nasz_iterator''wart
*nasz_iterator = wart // podstawienie wartości zmiennej w miejsce pojemnika wskazane przez nasz_iterator
</source>
Umożliwia nam to przeciążony '''operator*''' , jak i '''operator=''' dzięki któremu możemy pod iterator podstwiać inne wartości.
 
Linia 14 ⟶ 16:
* '''Iteratory wejścia:'''
Taki iterator może odczytać wartość elementu, na który wskazuje, o ile nie wskazuje przekroczenia zakresu – end(). Musi posiadać domyślny konstruktor, konstruktor kopiujący oraz operatory =, ==, !=, ++. Odczytać wartość można:
<source lang="cpp">
wart = *iterator; // '' poprawne użycie iteratora wejścia''
*iteratorwart = wartosc*iterator; // '' niepoprawnepoprawne użycie iteratora wejścia- nie może zapisywać''
wart*iterator = *iteratorwartosc; // '' poprawneniepoprawne użycie iteratora wejścia''- nie może zapisywać
</source>
 
Można inkrementować iterator – aby wskazywał następny składnik:
<source lang="cpp">
iterator++ lub ++iterator // ''inkrementacja jest niemożliwa po przekroczeniu końca''
</source>
 
 
Linia 23 ⟶ 30:
Ten typ iteratora umożliwia tylko zapis wartości do danego składnika – odczyt jest niemożliwy. Musi posiadać domyślny konstruktor, konstruktor kopiujący oraz operatory =, ++.
np.
<source lang="cpp">
*iterator=wartosc; ''// poprawnie''
wartosc=*iterator ''//= błędniewartosc; próbujemy// odczytać''poprawnie
wartosc = *iterator; // błędnie – próbujemy odczytać
</source>
 
 
Linia 39 ⟶ 48:
Sposób użycia iteratora bezpośredniego dostępu;
załóżmy ze nasz iterator wskazuje już składnik n-ty
<source lang="cpp">
iterator+=5; // '' teraz wskazuje (n+5)-ąty składnik
iterator ++= 5; // ''teraz wskazuje (n+65)-tyąty składnik
iterator+=5+; // '' teraz wskazuje (n+56)-ątyty składnik
*iterator[n] = wartosc; // '' przypisujemy n-temu składnikowi wartosc''
</source>
 
== Użycie w poszczególnych kontenerach (z przykładami) ==
Linia 47 ⟶ 58:
Przedstawimy teraz metody poszczególnych kontenerów, które umożliwiają operowanie na iteratorach. Należy podkreślić, że nazwy niektórych metod powtarzają się w różnych pojemnikach a także pełnią te same funkcje, dlatego nie będziemy ich za każdym razem dokładnie opisywać - podobnie jak przeładowanych operatorów.
 
::'''=== Poruszanie się za pomocą iteratorów''' ===
Poruszanie się za pomocą iteratorów po kolejnych składowych może odbywać się na dwa sposoby:<br>
1) '''w przód''' - czyli od początku do końca np.:
<source lang="cpp">
kontener<typ kontenera>::iterator iter; //'' iterator do przodu''
</source>
początek struktury wyznacza metoda begin(); zaś koniec end();
 
2) '''od końca''' - czyli od końca do początku np.:
<source lang="cpp">
kontener<typ kontenera>::reverse_iterator iter; //'' iterator od końca''
skanować możemy od rbegin(); do rend(); , co można utożsamiać z odwróconym początkiem i odwróconym końcem.
</source>
 
skanować możemy od <tt>rbegin();</tt> do <tt>rend(); </tt>, co można utożsamiać z odwróconym początkiem i odwróconym końcem.
Niestety używanie metod rbegin(); i rend(); do skanowania w przód spowoduje błąd kompilacji. Problem ukazuje poniższy fragment kodu:
vector<string> tablica;
tablica.push_back("wlazł kotek ");
tablica.push_back("na płotek ");
vector<string>::reverse_iterator iter; //'' iterator od końca''
for(iter=tablica.rend(); iter<tablica.rbegin(); iter++) // '' proba skanowania w przód - zle''
{cout<<*iter<<endl;}
 
Użycie zostanie zobrazowane na przykładzie kontenera <tt>vector</tt>.
 
=== Wektor ===
AbyPrzed użyćużyciem iteratora należy najpierw nadać mu wartość. Dlatego gdy chcemy powtarzać pewną operacje w pętli dla każdego elementu wektora – od początku do końca – inicjujemy iterator wartością bezparametrowej funkcji '''begin()'''. Metoda ta zwraca iterator wskazujący na pierwszy element pojemnika wektor. Jest to iterator bezpośredniego dostępu. Nie mamy jednak możliwości porównywania iteratora z wartością NULL (bo iterator zwykłym wskaźnikiem nie jest) musi istnieć metoda która pokazuje koniec naszego wektora. Tak też jest – metoda '''end()''' zwraca iterator za ostatnim elementem. To także jest iterator bezpośredniego dostępu.
 
Aby użyć iteratora należy najpierw nadać mu wartość. Dlatego gdy chcemy powtarzać pewną operacje w pętli dla każdego elementu wektora – od początku do końca – inicjujemy iterator wartością bezparametrowej funkcji '''begin()'''. Metoda ta zwraca iterator wskazujący na pierwszy element pojemnika wektor. Jest to iterator bezpośredniego dostępu. Nie mamy jednak możliwości porównywania iteratora z wartością NULL (bo iterator zwykłym wskaźnikiem nie jest) musi istnieć metoda która pokazuje koniec naszego wektora. Tak też jest – metoda '''end()''' zwraca iterator za ostatnim elementem. To także jest iterator bezpośredniego dostępu.
 
Przykład:
<source lang="cpp">
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> tab;
 
''// inicjujemy wektor kolejnymi liczbami naturalnymi''
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> tab;
''//inicjujemy wektor kolejnymi liczbami naturalnymi''
tab.push_back(1);
tab.push_back(2);
tab.push_back(3);
'' // wyswietlenie skladnikow wektora tab w petli przy pomocy iteratora''
vector<int>::iterator i;
for( i=tab.begin(); i<!=tab.end(); ++i )
{
cout<< *i <<endl'\n';
}
return 0;
}
</source>
 
Program spowoduje wyświetlenie kolejnych elementów pojemnika:
1
2
Linia 98 ⟶ 106:
 
Metody begin() i end() skonstruowane są do przeglądania wektora od początku do końca. Co jeśli chcemy działać na składowych wektora w odwrotnej kolejności? Nie ma problemu. Istnieje bowiem metoda '''rbegin()''', która zwraca odwrócony iterator wskazujący na ostatni element pojemnika (mówi się także, że jest to odwrócony początek). Odwołuje się on do elementu bezpośrednio poprzedzającego iterator wskazywany przez end. Jest to odwrócony iterator bezpośredniego dostępu. Mamy także metode '''rend()''', która zwraca odwrócony iterator do elementu odwołującego się do elementu bezpośrednio poprzedzającego pierwszy element kontenera wector (zwany także odwróconym końcem). rend() wskazuje miejsce bezpośrednio poprzedzające składnik do którego odwoływałby się begin(). Oto przykład:
<source lang="cpp">
 
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<int> tab;
''// inicjujemy wektor kolejnymi liczbami naturalnymi''
tab.push_back(1);
tab.push_back(2);
tab.push_back(3);
''// wyświetlenie skladników wektora tab w pętli przy pomocy odwróconego iteratora''
vector<int>::reverse_iterator i;
for( i=tab.rbegin(); i<!=tab.rend(); ++i )
{
cout<<*i<<endl'\n';
}
return 0;
}
</source>
 
Wykonanie programu spowoduje wyświetlenie składników kontenera w odwrotnej kolejności:
3
2
Linia 129 ⟶ 137:
 
=== Listy jedno- i dwukierunkowe ===
 
Na liście dwukierunkowej działamy bardzo podobnie jak na wektorze. Tu także dysponujemy metodami '''begin()''' i '''end()''' zwracającymi wartość iteratora odpowiednio: na początek listy i tuż za koniec. Jest to iterator dwukierunkowy i możemy na nim działać zarówno do przodu (dzięki ++) jak i do tyłu (przez --). Mamy także analogiczne '''rbegin()''' i '''rend()''' zwracające odwrócony iterator do listy.
 
Przykład:
<source lang="cpp">
#include <iostream>
#include <listiostream>
#include <iostreamlist>
using namespace std;
int main ()
{
''// tworzymy i inicjujemy nowa liste znakow''
list<char> lista;
lista.push_back('a');
lista.push_back('b');
lista.push_back('c');
 
''// i wyswietlamy ja w petli do przodu''
list<char>::iterator i;
cout<<"lista po kolei: ";
for( i=lista.begin(); i!=lista.end(); ++i )
{
cout<<*i<<" ";
}
 
''//i do tylu''
cout<<endl<<"lista//i oddo tylu: ";
cout<<"\nLista od tylu: ";
for( i = lista.end(); i!=lista.begin(); --i )
{
cout<<*i<<" ";
}
 
''// znow wyswietlamy liste - teraz przy pomocy odworconego iteratora''
list<char>::reverse_iterator i2;
cout<<endl<<"lista\nLista od tylu z odwroconym iteratorem: ";
for( i2=lista.rbegin(); i2 != lista.rend(); i2++ )
{
cout<<*i2<<" ";
}
cout<<endl'\n';
return 0;
}
</source>
 
Wynikiem działania programu będzie:
lista po kolei: a b c
Linia 181 ⟶ 192:
 
Iterator w zbiorach działa jak w innych kontenerach. Iterator działający na zbiorze jest dwukierunkowy – możemy więc korzystać ze wszystkich operatorów przeciążonych dla iteratora dwukierunkowego. Dodatkowo (jak z własności zbioru wynika) należy wspomnieć, że metoda '''begin()''' daje dostęp iteratorowi do pierwszego elementu zbioru, który równocześnie posiada najniższy klucz. W zbiorach możemy także korzystać z odwróconych iteratorów.
<source lang="cpp">
 
#include<iostream>
#include<set>
using namespace std;
int main()
{
int o;
set<int> zbior;
Linia 193 ⟶ 204:
zbior.insert(1);
zbior.insert(11);
set<int>::iterator i; // teraz i jest wskaznikiem do zbioru
for(i=zbior.begin();i!=zbior.end();++i)
cout<<*i<<endl;
return 0;
}
 
set<int>::iterator i; // teraz i jest wskaznikiem do zbioru
for( i=zbior.begin(); i!=zbior.end(); ++i )
{ cout<<*iteri<<endl'\n';}
 
return 0;
}
</source>
Wynikiem pracy programu będzie wypisanie cyfr:
1