STL, Iteratoren - public.fh

Transcription

STL, Iteratoren - public.fh
Die verschiedenen Programmierparadigmen von C++
© Helmke
Die verschiedenen Programmierparadigmen von C++
5 Die Standard Template Library (STL)
© Helmke
Klasse Komponente
Code auf dieser Seite verstanden
1. Verstehe nur Bahnhof
class Komponente {
2. Verstehe die Schnittstelle
public:
und den kompletten Code
Komponente() {};
3.
Verstehe das Problem,
void addLieferant(const Lieferant& );
void entferneLieferant(const Lieferant& ); aber nicht jedes Schlüsselwort
bool hatLieferant(const Lieferant& li) const;
private:
5.1 Container
5.2 Iteratoren
(*)
5.3 Funktionsobjekte
5.4 Algorithmen
(*) Auch in dieser Datei
Komponente(const Komponente&); /* verbotener Aufruf */
Komponente& operator=(const Komponente&); /* verbotener Aufruf */
vector<Lieferant> container;
};
void Komponente::addLieferant(const Lieferant& li) { /*… */ }
void Komponente:: entferneLieferant(const Lieferant& li ) { /*… */ }
bool Komponente:: hatLieferant(const Lieferant& li) const { /*… */ }
Lehrbuch: 6.4
Kompendium, 3. Auflage: 8.12, 11.5
Kompendium, 4. Auflage: 11.5
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
1
© Helmke
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
Ergebnis:
___ 1
__ 2
__ 3
© Helmke
Klasse Komponente (2)
Klasse Komponente (3)
void Komponente::addLieferant(const Lieferant& li) {
container.push_back(li);
}
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (int i=0; i<container.size(); ++i) {
if (container[i] == li) {
Container werden normalerweise
return true;
aber mit Iteratoren durchlaufen.
}
}
return false;
}
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (vector<Lieferant>::iterator iter = container.begin();
iter != container.end(); ++iter) {
if (*iter == li) {
return true;
}
}
}
return false; }
WS 2015/16
V 4.01; © Hon. Prof. Helmke
10
WS 2015/16
V 4.01; © Hon. Prof. Helmke
9
11
Die verschiedenen Programmierparadigmen von C++
© Helmke
Iteratoren (2)
Die verschiedenen Programmierparadigmen von C++
© Helmke
Die Iteratoren begin und end
Die Schnittstelle der Iteratoren entspricht genau der Art und Weise wie
Zeiger in C über Arrays wandern.
Im Unterschied zu Zeigern können Iteratoren aber mit beliebigen
Containern und nicht nur mit Arrays umgehen. Sie können aber
auch mit normalen Arrays umgehen.
begin()
iter
end()
Iteratoren von verschiedenen Containern besitzen unterschiedliche
Typen, aber die gleiche Schnittstelle.
Um mit Containern arbeiten zu können, stellen Container
entsprechende Elementfunktionen bereit, die beiden wichtigsten sind
begin() und end().
WS 2015/16
erstes Element
V 4.01; © Hon. Prof. Helmke
12
Die verschiedenen Programmierparadigmen von C++
© Helmke
Klasse Komponente (3)
Code auf dieser Seite verstanden
bool Komponente:: hatLieferant(const Lieferant& li) const {
1. Verstehe nur Bahnhof
for (int i=0; i<container.size(); ++i) {
2. Prinzip der Iteratoren als eine Art
if (container[i] == li) {
return true;
Zeiger verstanden
}
}
3. Verstanden, könnte Code dieser
return false;
Methode nun selbst schreiben
}
4. Kannte Iteratoren schon vorher
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (vector<Lieferant>::iterator iter = container.begin();
iter != container.end(); ++iter) {
if (*iter == li) {
return true;
}
}
}
return false; }
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Ergebnis:
___ 1
__ 2
__ 3 __4
14
WS 2015/16
letztes Element
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
13
© Helmke
Iteratoren (3)
#include <list>
using namespace std;
container::begin() liefert einen
Iterator, der die Position des ersten
Elements im Container repräsentiert.
list<float> cont;
for (int i=1; i<4; ++i) {
cont. push_front(i*2.2);
cont. push_back(i*1.1);
}
container::end() liefert einen
Iterator, der die Position hinter dem
letzten Element im Container
repräsentiert.
// Alle Elemente über Iteratoren
// ausgeben:
list<float>::iterator iter = cont.begin();
for (; iter != cont.end(); ++iter) {
cout << *iter << ' ';
}
WS 2015/16
Falls begin() gleich end(), ist der
Container leer (container::empty()
liefert true).
V 4.01; © Hon. Prof. Helmke
15
Die verschiedenen Programmierparadigmen von C++
© Helmke
Iteratoren (4)
Die verschiedenen Programmierparadigmen von C++
Iteratoren (4)
Ausgabe ?
1. 0 1
2. 1 2 1 2
3. 1 2
4. 1 1
#include <list>
using namespace std;
list<int> cont;
for (int i=1; i<3; ++i) {
cont. push_back(i);
}
// Alle Elemente über Iteratoren
// ausgeben:
list<int>::iterator iter = cont.begin();
Ergebnis
__ 0 1
_-_ 1 2
Der Typ des Iterators ist jeweils im zugehörigen Container per
typedef festgelegt, z.B.:
class list {
public:
typedef ... iterator;
...
};
__ 1 2 1 2
__ 1 1
Der genaue Typ ist implementierungsabhängig.
Daneben gibt es auch den Typ const_iterator. Bei ihm ist über den
*-Operator (bzw. ->-Operator) nur ein konstanter (nicht modifizierender)
Zugriff auf das Element, auf das der Iterator verweist, möglich.
for (; iter != cont.end(); ++iter) {
cout << *iter << ' ';
}
Hiervon ist jedoch der Typ „const
WS 2015/16
© Helmke
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
16
© Helmke
Iteratoren (6)
WS 2015/16
iterator“ zu unterscheiden.
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
17
© Helmke
Überladung des Increment-Operators
class iterator /*...*/
{
/*...*/
for (cont.begin(); iter != cont.end(); ++iter) {
cout << *iter << ' ';
}
iterator& operator++() {
„nächste Position markieren“
return *this;
}
Sofern die Elemente des Containers Objekte mit Komponenten sind,
sind (normalerweise) auch der Operator „->“ und „.“ (Punkt)
definiert:
iter-> komp
(*iter). komp
iterator operator++(int) {
iterator temp = *this;
„nächste Position markieren“
return temp;
}
In einigen Implementierungen der STL kann der „->“-Operator
aber evtl. nicht definiert sein. (*iter).komp funktioniert allerdings
immer.
list<int>:::iterator iter1, iter2;
...
// iter1 zeige auf Liste mit
// den Elementen 1,2,3,4,
// und zwar auf 1
iter2 = ++iter1;
// iter2 und iter1 zeigen nun auf 2
// iter1 zeige vorher auf 2
iter2 = iter1++;
// iter1 zeigt nun auf 3,
// iter2 zeigt auf 2
/*...*/
};
WS 2015/16
V 4.01; © Hon. Prof. Helmke
18
WS 2015/16
V 4.01; © Hon. Prof. Helmke
19
Die verschiedenen Programmierparadigmen von C++
© Helmke
Die verschiedenen Programmierparadigmen von C++
Klasse Komponente (4): Besser mit const_iterator
Klasse Komponente (5): Besser mit const_iterator
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (vector<Lieferant>::const_iterator iter = container.begin();
iter != container.end(); ++iter) {
if (*iter == li) {
return true;
}
Dieser Suchvorgang kommt ganz häufig vor:
}
Von Anfang bis Ende prüfen, ob ein
}
Element des Containers gleich einem anderen ist.
return false;
}
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (vector<Lieferant>::const_iterator iter = container.begin();
iter != container.end(); ++iter) {
if (*iter == li) {
return true;
}
}
}
return false; }
bool Komponente:: hatLieferant(const Lieferant& li) const {
vector<Lieferant>::const_iterator iter=
find(container.begin(), container.end(), li);
if (iter == container.end()) { return false;}
else {return true;}
}
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (auto iter = container.begin(); iter != container.end(); ++iter) { …
bool Komponente:: hatLieferant(const Lieferant& li) const {
for (auto iter
WS 2015/16
: container)
{ // iter wäre hier vom Typ Lieferant.
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
20
© Helmke
WS 2015/16
21
© Helmke
Wir bauen uns unser eigenes find.
int array[100];
/* ..*/
find(&array[10], &array[49], 99)
bool Komponente:: hatLieferant(const Lieferant& li) const {
vector<Lieferant>::const_iterator iter=
find(container.begin(), container.end(), li);
if (iter == container.end()) { return false;}
else {return true;}
}
template <typename T, typename W>
T find (T anf, T ende, W value) {
while (anf != ende && !(*anf == value) ) { // Reihenfolge wichtig
++anf;
}
return anf;
bool Komponente:: hatLieferant(const Lieferant& li) const {
if (find(container.begin(), container.end(), li) == container.end()) {
return false;}
else {return true; } }
}
bool Komponente:: hatLieferant(const Lieferant& li) const {
return (find(container.begin(), container.end(), li) != container.end());
}
V 4.01; © Hon. Prof. Helmke
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
Klasse Komponente (5): Besser mit const_iterator
WS 2015/16
© Helmke
bool Komponente:: hatLieferant(const Lieferant& li) const {
return (find(container.begin(), container.end(), li) != container.end());
}
22
WS 2015/16
V 4.01; © Hon. Prof. Helmke
23
Die verschiedenen Programmierparadigmen von C++
© Helmke
Algorithmus find
Die verschiedenen Programmierparadigmen von C++
© Helmke
Algorithmus for_each
#include <algorithm>
using namespace std;
template <class InputIter, class Tp>
inline InputIter find(InputIter first, InputIter last, const Tp& val) {
while (first != last && *first != val) {
++first;
}
return first;
}
void MachWas(int i) { cout << i*i; }
int array[100];
for_each(&array[0], & array[100], MachWas);
sort(&array[10], & array[20]);
template <typename Iter, typename Func>
void for_each(Iter anf, Iter end, Func f){
while (anf!= end) {
f(*anf); ++anf;
}
}
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
24
© Helmke
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
Beispiele für Algorithmen
Clicker
vector<int> vec;
...
list<int> cont;
for (int i=1; i<13; ++i) {
cont. push_back( i );
}
vector<int>::iterator pos;
pos = min_element(vec.begin(), vec.end());
cout << * max_element(vec.begin(), vec.end());
sort(vec.begin(), vec.end());
...
Ausgabe?
1. 12 12 12
2. 12 12 11
3. 13 12 12
4. 12 11 12
Ergebnis:
_-__ 12 12 12 ___12 12 11 __ 13 12 12 __ 12 11 12
// Ab dem zweiten Element nach 5 suchen
pos = find(vec.begin()+1, vec.end(), 5);
V 4.01; © Hon. Prof. Helmke
© Helmke
cout << * max_element(vec.begin(), vec.end());
cout << * max_element(vec.begin()++, vec.end());
cout << * max_element(++vec.begin(), vec.end());
// Reihenfolge vom zweiten bis vorletzten Element umkehren
reverse(vec.begin()+1, vec.end()-1);
...
WS 2015/16
25
26
WS 2015/16
V 4.01; © Hon. Prof. Helmke
27
Die verschiedenen Programmierparadigmen von C++
© Helmke
Clicker
Die verschiedenen Programmierparadigmen von C++
© Helmke
Algorithmen -- Fortsetzung
list<int> vec;
for (int i=1; i<13; ++i) {
vec. push_front( i );
}
Der Anwender eines Algorithmus muss selbst darauf achten, dass das
übergebene Ende vom Anfang aus erreichbar ist, d.h. die Iteratoren
müssen zumindest zum gleichen Container gehören.
Ausgabe?
1. 12 12 11
2. 1 1 2
3. 13 12 12
4. 12 11 12
Die Bereiche der Algorithmen werden immer als halboffenene Intervalle
ausgewertet: [anfang, ende) bedeutet anfang...ende-1.
Beispiel:
// vec sei mit Zahlen von 1 bis 20 gefüllt
vector<int>::iterator pos5 = find(vec.begin(), vec.end(), 5);
vector<int>::iterator pos13 = find(vec.begin(), vec.end(), 13);
cout << * max_element(vec.begin(), vec.end());
cout << * max_element(vec.begin()++, vec.end());
cout << * max_element(++vec.begin(), vec.end());
// 12 wird ausgegeben, da Intervall [pos5, pos13 [ bearbeitet wird
cout << *max_element(pos5, pos13);
// 13 wird ausgegeben.
cout << *max_element(pos5, ++pos13);
Ergebnis:
_-__ 12 12 11 ___1 1 2 __ 13 12 12 __ 12 11 12
WS 2015/16
V 4.01; © Hon. Prof. Helmke
28
Die verschiedenen Programmierparadigmen von C++
© Helmke
Clicker: Algorithmen auch für Arrays
int arr[20];
for (int i=0; i<20; ++i) {
arr[i] = i * i;
}
WS 2015/16
Die verschiedenen Programmierparadigmen von C++
array<int, 20> arr; // int arr[20];
for (int i=0; i<20; ++i) {
arr[i] = i * i;
}
Ausgabe?
1. 19 8 10
2. 361 64 100
3. 361 64 Fehler
4. 361 100 11
© Helmke
Ausgabe?
1. 19 8 10
2. 361 64 100
3. 361 64 Fehler
4. 361 100 11
cout << * max_element(&arr[4], & arr[20]);
cout << * find(&arr[4], & arr[10], 64);
cout << * find(&arr[4], & arr[10], 144);
Ergebnis:
__ 19 8 10 _-_ 361 64 100 __ 361 64 Fehler _361 100 11
V 4.01; © Hon. Prof. Helmke
29
Clicker: Algorithmen auch für STL-Arrays
cout << * max_element(&arr[4], & arr[20]);
cout << * find(&arr[4], & arr[10], 64);
cout << * find(&arr[4], & arr[10], 144);
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Ergebnis:
__ 19 8 10 _-_ 361 64 100 __ 361 64 Fehler _361 100 11
30
WS 2015/16
V 4.01; © Hon. Prof. Helmke
31
Die verschiedenen Programmierparadigmen von C++
© Helmke
Algorithmen – Beispiel: Definition von max_element
Trauen Sie sich auch zu find selbst als Template zu implementieren
1. Versteh nur Bahnhof
2. Ja, mit Blick auf die Folie
3. Sollte ohne Hilfe (evtl. nicht so schön) klappen
ForwardIter result = first;
while (++first != last) {
if (*result < *first) {
result = first;
Trauen Sie sich zu,min_element selbst als Template
}
zu implementieren
}
1. Versteh nur Bahnhof
return result;
2. Ja, mit Blick auf die Folie
}
3. Sollte ohne Hilfe (evtl. nicht so schön) klappen
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
Ergebnis:
___ 1
__ 2
32
© Helmke
__ 3
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
Dann mal los!
Algorithmus remove
int arr[20];
for (int i=0; i<20; ++i) {
arr[i] = i * i;
}
#include <iostream>
#include <list>
#include<algorithm>
using namespace std;
int main() {
list<int> cont;
for (int i=1; i<6; ++i) { cont.push_back(i); }
cout << * MeinMinElement(&arr[4], & arr[20]);
arr[14] = -4;
cout <<" " << * MeinMinElement(&arr[0], & arr[20]);
template <class ForwardIter>
ForwardIter max_element(ForwardIter first, ForwardIter last) {
if (first == last) return first;
ForwardIter result = first;
while (++first != last) {
if (*result < *first) {
result = first;
}
}
return result;
33
© Helmke
cout << "Liste vor remove :"; // Ausgabe ist nun 1 2 3 4 5
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// 3 mit remove entfernen
list<int>::iterator new_end = remove(cont.begin(), cont.end(), 3);
Gewünschte Ausgabe
16 -4
// Ausgabe ist nun 1 2 4 5 5
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
}
}
WS 2015/16
© Helmke
Clicker-Auswertung
template <class ForwardIter>
ForwardIter max_element(ForwardIter first, ForwardIter last) {
if (first == last) return first;
WS 2015/16
Die verschiedenen Programmierparadigmen von C++
V 4.01; © Hon. Prof. Helmke
34
WS 2015/16
V 4.01; © Hon. Prof. Helmke
35
Die verschiedenen Programmierparadigmen von C++
© Helmke
Algorithmus remove (2)
Die verschiedenen Programmierparadigmen von C++
© Helmke
Elementfunktion erase
Warum wird ein Element nicht wirklich gelöscht?
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe sei vorher 1 2 3 4 5
Algorithmen können die Container nicht wirklich verkleinern oder
vergrößern, weil ihnen der Container selbst nicht übergeben wird,
sondern nur Iteratoren, die irgendwo in den Container zeigen, z.B.
// 3 mit remove entfernen
cont.erase(remove(cont.begin(), cont.end(), 3), cont.end());
list<int>::iterator new_end = cont. end();
--new_end; --new_end;
remove(cont.begin(), new_end, 3);
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe ist nun 1 2 4 5
Damit kann remove auf keinen Fall das end von cont verändern.
Woher soll es das auch kennen.
Dieses können nur Elementfunktionen, z.B.
erase(cont::iterator a, cont::iterator e);
Alle Elemente im Intervall [a..e) werden wirklich gelöscht.
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
36
© Helmke
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
Löschen mit find
remove ist aber mächtiger
list<int> cont;
for (int i=1; i<6; ++i) {
cont.push_back(i);
}
cont. push_back(3); cont. push_back(3);
list<int> cont; // Container-List fuer int
for (int i=1; i<6; ++i) { cont.push_back(i); }
cont. push_front(3);
cont. push_front(3);
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe ist nun 3 3 1 2 3 4 5
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe ist nun 1 2 3 4 5 3 3
cont.erase(new_end, cont.end());
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe ist nun Liste nach erase :1 2 4 5
cont.erase(iter3, iter3p1);
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe ist nun Liste nach erase :1 2 4 5 3 3
V 4.01; © Hon. Prof. Helmke
© Helmke
list<int>::iterator new_end = remove(cont.begin(), cont.end(), 3);
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
// Ausgabe ist nun 1 2 4 5 3 4 5
list<int>::iterator iter3= find(cont.begin(), cont.end(), 3);
list<int>::iterator iter3p1=iter3;
++iter3p1;
WS 2015/16
37
38
WS 2015/16
V 4.01; © Hon. Prof. Helmke
39
Die verschiedenen Programmierparadigmen von C++
© Helmke
Die verschiedenen Programmierparadigmen von C++
Clicker
Clicker
list<int> cont;
2. Ausgabe?
for (int i=1; i< 6; ++i) {
1. 1 2 3 4 5
cont. push_back( i );
2. 1 2 4 5 5
}
3. 1 2 4 5
// Ausgabe ist 1 2 3 4 5
4. 1 2 4 5 3
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
remove(cont.begin(), cont.end(), 3);
// 2. Ausgabe???
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
list<int> cont;
for (int i=1; i< 6; ++i) {
cont. push_back( i );
}
// Ausgabe ist 1 2 3 4 5
Ergebnis:
__ 1 2 3 4 5 _--_ keine Ausgabe __ 1 2 4 5 __ 1 2 4 5 3
V 4.01; © Hon. Prof. Helmke
40
Die verschiedenen Programmierparadigmen von C++
© Helmke
Clicker
WS 2015/16
V 4.01; © Hon. Prof. Helmke
Die verschiedenen Programmierparadigmen von C++
41
© Helmke
Komponente::entferneLieferant
list<int> cont;
for (int i=1; i< 6; ++i) {
cont. push_back( i );
}
void Komponente:: entferneLieferant(const Lieferant& li ) {
vector<Lieferant>::iterator iter =
find(container.begin(). container.end(), li);
if (iter != container.end()) {
vector<Lieferant>::iterator hlp = iter;
++iter;
container.erase(iter, hlp);
};
}
2. Ausgabe?
1. 1 2 3 4 5
2. 1 2 4 5 5
3. 1 2 4 5
4. 1 2 4 5 3
// Ausgabe ist 1 2 3 4 5
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
cont.erase(remove(cont.begin(), cont.end(), 3), cont.end() );
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
void Komponente:: entferneLieferant(const Lieferant& li ) {
container.erase(
remove(container.begin(), container.end(), li),
container.end());
}
Ergebnis:
__ 1 2 3 4 5 __ 1 2 4 5 5 _--_ 1 2 4 5 __ 1 2 4 5 3
WS 2015/16
2. Ausgabe?
1. 1 2 3 4 5
2.
3. 1 2 4 5
4. 1 2 4 5 3
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
remove(cont.begin(), cont.end(), 3 );
cont.erase(cont.begin(), cont.end());
copy(cont.begin(), cont.end(), ostream_iterator<int>(cout, " "));
Ergebnis:
__ 1 2 3 4 5 _--_ 1 2 4 5 5 __ 1 2 4 5 __ 1 2 4 5 3
WS 2015/16
© Helmke
V 4.01; © Hon. Prof. Helmke
42
WS 2015/16
V 4.01; © Hon. Prof. Helmke
43