Programmieren in C++ Überblick 1. Einführung und Überblick 2. Klassen und Objekte: Datenkapselung 3. Erzeugung und Vernichtung von Objekten 4. Ad-hoc Polymorphismus 6. Templates und generische Programmierung 7. Standard Template Library 8. Vererbung 9. Klassenbeziehungen 10. Fehlerbehandlung 11. Namensräume und Typinformation zur Laufzeit 12. Dateien und Ströme 13. Zusammenfassung objektorientierter Merkmale Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 261 Programmieren in C++ Überblick: 5.1 Einleitung 5.4 for_each Iteratoren Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 262
5.1 Einleitung Behälter (container) Klassen Enthalten andere Objekte (echte Kopie) oder nur Zeiger darauf (ursprüngliche Identität bleibt erhalten) Wichtiges und häufig benötigtes Konstrukt der objektorientierten Programmierung Beispiele: Stacks, Listen, Bäume, Arrays (Vektoren), etc. Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 263 5.1 Einleitung Behälter (container) Klassen Häufig benötigt man eine Möglichkeit, individuelle Inhalte eines Containers zu besuchen, d.h. sequentiell darauf zuzugreifen Eine Möglichkeit dazu ist ein sog. Iterator, mit Hilfe dessen man ein Element eines Containers nach dem anderen besuchen und ggf. manipulieren kann Auch ein Iterator ist eine Abstraktion: Er wird so realisiert, dass der Benutzer über die Art und Weise der internen Speicherung und die Art des Zugriffs auf das nächste Element nicht Bescheid wissen muss Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 264
Programmieren 2 C++ Überblick: 5.1 Einleitung 5.4 for_each Iteratoren Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 265 Sequentieller Zugriff auf alle Elemente Üblicherweise verwendet man dazu eine Schleife: // Besuche sequentiell jedes a[i] und tue etwas damit for (i = 0; i < size; ++i) sum += a[i]; Das könnte auch anders implementiert werden: for (j = size-1; j >= 0; --j) sum += a[j]; Generell funktioniert ein solcher Zugriff nach dem Schema: bis kein weiteres Element mehr vorhanden ist sum += nächstes Element Man könnte das nächste Element in einem Zeiger zurückliefern Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 266
Iterator über Elemente eines Vektors Erweitern wir unsere Vector Klasse (mit int Elementen): class Vector public: typedef int* iterator; // Typ Vector::iterator, Zeiger auf int explicit Vector(int n = 1); Vector(const Vector& v); Vector(const int a[], int n); ~Vector() delete [] bp; iterator begin() const return bp; iterator end() const return bp + size; int ub() const return size - 1; // upper bound int& operator[](int i); // range checked Vector& operator=(const Vector& v); private: iterator bp; // base pointer int size; // Anzahl Elemente ; Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 267 Iterator über Elemente eines Vektors (fortgesetzt) class Vector public: typedef int* iterator; // Typ Vector::iterator, Zeiger auf int iterator begin() const return bp; iterator end() const return bp + size; private: iterator bp; // base pointer int size; // Anzahl Elemente ; begin() und end() liefern Grenzen des Vektors in Form von Zeigern auf das erste und (size+1)-te also eins hinter dem letzten Element zurück Nach diesem Muster funktionieren auch entsprechende Klassen der STL Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 268
Hauptprogramm: Vector v(n); // Der Vektor als Container Vector::iterator p; // Zeiger vom richtigen Elementtyp int sum = 0; for (p = v.begin(); p!= v.end(); ++p) sum += *p; // p muss dereferenziert werden Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 269 Iterator über Elemente eines Vektors Nach diesem Muster können leicht weitere Schleifen über den gesamten Vektor realisiert werden Man muss sich um Anfangs- oder Ende-Index keine Gedanken machen: ostream& operator<<(ostream& out, const Vector& v) Vector::iterator p; for (p = v.begin(); p!= v.end(); ++p) out << *p << '\t'; out << endl; return out; Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 270
Iterator über Elemente eines Vektors (fortgesetzt) istream& operator>>(istream& in, Vector& v) Vector::iterator p; for (p = v.begin(); p!= v.end(); ++p) in >> *p; return in; Hier wurden die Input- und Output-Operatoren überschrieben, so dass sich ein Vektor leicht ein- und ausgeben lässt Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 271 Programmieren 2 C++ Überblick: 5.1 Einleitung 5.4 for_each Iteratoren Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 272
Klasse List für eine doppelt verkettete Liste Schnittstelle ähnlich der gleichnamigen Klasse in der STL Rückwärtsverkettung erleichtert bestimmte Operationen, wie Löschen eines Elementes Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 273 Klassendeklaration (1/5) class List public: // Vorwärts Deklarationen struct Listelem; class Iterator; friend Iterator; // Konstruktoren List() :h(0), t(0) // Leere Liste List(size_t n_elements, char c); // Init mit c List(const List& x); // Init mit Liste x // Destruktor ~List() release(); Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 274
Klassendeklaration (2/5) // Liefere Head und Tail Adresse Iterator begin()const return h; Iterator end()const return t; // Füge am Head hinzu und lösche dort void push_front(char c); void pop_front(); // Liefer Head und Tail Daten char& front() return h -> data; char& back() return t -> data; // Prüfe auf leer und gib Liste frei bool empty()const return h == 0; void release(); // Überlade << Operator für Liste friend ostream& operator<<(ostream& out, List& x); Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 275 Klassendeklaration (3/5) private: listelem *h, *t; // Head und Tail struct listelem // Typ des Listenknoten char data; listelem *next, *prev; // Konstruktor für Listenknoten listelem(char c, listelem* n, listelem* p) :data(c), next(n), prev(p) ; Struktur listelem stellt Listenknoten dar enthält ein char als eigentliches Datenelement und die Vorwärts- und Rückwärts-Zeiger. Besitzt einen Konstruktor zum einfachen Initialisieren Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 276
Klassendeklaration (4/5) Als Bestandteil der List Klasse wird eine Iterator Klasse eingebunden: List::Iterator Instanzen dieser Klasse zeigen auf einen (aktuellen) Knoten innerhalb der Liste // im Deklarationsbereich der Klasse List class Iterator public: // Konstruktor Iterator(listelem* p = 0) :ptr(p) Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 277 Klassendeklaration (5/5) // Überlade ++ und - Prä- und Postfix Iterator operator++(); Iterator operator--(); Iterator operator++(int); Iterator operator--(int); // Überlade -> Operator listelem* operator->() return ptr; ; // Überlade Dereferenzierung char& operator*() return ptr -> data; // Konversion operator listelem*() return ptr; private: listelem* ptr; // Aktuelles listelem oder 0 ; // Ende class Iterator ; // Ende class List Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 278
Implementierung der Methoden der Klasse List (1/4) // Initialisiere mit konstantem c List::List(size_t n_elements, char c) assert(n_elements > 0); h = t = 0; for(size_t i = 0; i < n_elements; ++i) push_front(c); // Initialisiere mit anderer Liste x List::List(const List& x) List::Iterator r = x.begin(); // Iterator durch x h = t = 0; // Liste ist leer while (r!= 0) push_front(*r++); // Deref. Iterator, dann inkr. Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 279 Implementierung der Methoden der Klasse List (2/4) // Lösche Inhalt der Liste void List::release() while (h!= 0) pop_front(); // Überlade << Operator für Liste ostream& operator<<(ostream& out, List& x) List::Iterator p = x.begin(); // liefert x.h out << "List = ("; while (p!= 0) out << *p << ","; ++p; cout << ")\n"; return out; // liefert ein char& // schalte Iterator weiter Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 280
Implementierung der Methoden der Klasse List (3/4) // Füge Element am Anfang der Liste an void List::push_front(char c) listelem* temp = new listelem(c, h, 0); // Neuer Knoten if (h!= 0) // Liste war nicht leer h -> prev = temp; h = temp; else h = t = temp; // Liste war leer Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 281 Implementierung der Methoden der Klasse List (4/4) // Lösche Element am Anfang der Liste void List::pop_front() listelem* temp; if (h!= 0) // Liste war nicht leer temp = h; if (t == h) // Nur 1 Element in Liste t = 0; h = 0; // Liste wird leer else h = h -> next; // Head Element ausketten h -> prev = 0; delete temp; // Das ist der neue Head Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 282
Implementierung der Methoden der Klasse Iterator (1/2) List::Iterator List::Iterator::operator++() assert(ptr!= 0); ptr = ptr -> next; return *this; List::Iterator List::Iterator::operator++(int) assert(ptr!= 0); Iterator temp = *this; ptr = ptr -> next; return temp; Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 283 Implementierung der Methoden der Klasse Iterator (2/2) List::Iterator List::Iterator::operator--() assert(ptr!= 0); ptr = ptr -> prev; return *this; List::Iterator List::Iterator::operator--(int) assert(ptr!= 0); Iterator temp = *this; ptr = ptr -> prev; return temp; Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 284
Hauptprogramm (1/2) #include <iostream> #include "list2.h" int main() List *l, *l1; char c; // Zeiger auf zwei Listen l = new List; // Erzeuge Instanz cout << endl << "---- Listen Demo-Programm ----" << endl; for (c = 'a'; c <= 'z'; ++c) l->push_front(c); cout << "a-z gepushed" << endl; cout << "Lies vom Anfang der Liste sequentiell:" << endl; List::Iterator it = l->begin(); for (; it!= 0; ++it) cout << *it; cout << endl; // Dereferenziere Iterator: // Liefert Listenelementwert Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 285 Hauptprogramm (2/2) cout << "Lies vom Ende der Liste rückwärts:" << endl; it = l->end(); for (; it!= 0; --it) cout << *it; cout << endl; cout << endl << "Gib Liste über << aus:" << endl; cout << *l; // Übergib Referenz der Liste cout << endl << "Kopie in umgek. Reihenfolge:" << endl; l1 = new List(*l); // Kopie in umgekehrter Reihenfolge cout << *l1; cout << endl << "Fertig!" << endl; delete l; delete l1; return 0; Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 286
Programmieren 2 C++ Überblick: 5.1 Einleitung 5.4 for_each Iteratoren Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 287 5.4 for_each Iteratoren Andere Methode, Iteratoren zu verwenden: Weiterschalten in einer Funktion for_each verbergen Dieser Funktion muss eine andere Funktion übergeben werden, die dann für jeden Iteratorschritt aufgerufen wird Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 288
5.4 for_each Iteratoren for_each Funktion: typedef void (*funktion)(char&); // Zeiger auf Funktion void for_each(list::iterator anfang, List::Iterator ende, funktion f) while(anfang!= ende) f(*anfang++); f(*ende); //.. und noch das letzte Element void gibaus(char& c) cout << c; // Beispiel-Funktion for_each(l1->begin(), l1->end(), gibaus); Prof. Dr. Björn Dreher Liste MI Programmieren in C++ 289