Sie sind auf Seite 1von 194

Lsungen zum Buch C++, UML und Design Patterns

Programmierung

Helmut Herold, Michael Klar, Susanne Klar

Lsungen zum Buch C++, UML und Design Patterns


Grundlagen und Praxis der Objektorientierung

An imprint of Pearson Education


Mnchen Boston San Francisco Harlow, England Don Mills, Ontario Sydney Mexico City Madrid Amsterdam

Bibliograsche Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliograe; detaillierte bibliograsche Daten sind im Internet ber <http://dnb.ddb.de> abrufbar Die Informationen in diesem Produkt werden ohne Rcksicht auf einen eventuellen Patentschutz verffentlicht. Warennamen werden ohne Gewhrleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit grter Sorgfalt vorgegangen. Trotzdem knnen Fehler nicht vollstndig ausgeschlossen werden. Verlag, Herausgeber und Autoren knnen fr fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung bernehmen. Fr Verbesserungsvorschlge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulssig.

Fast alle Hardware- und Softwarebezeichnungen und weitere Stichworte und sonstige Angaben, die in diesem Buch verwendet werden, sind als eingetragene Marken geschtzt. Da es nicht mglich ist, in allen Fllen zeitnah zu ermitteln, ob ein Markenschutz besteht, wird das -Symbol in diesem Buch verwendet. Umwelthinweis: Dieses Produkt wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie zum Schutz vor Verschmutzungen ist aus umweltvertrglichen und recyclingfhigen PE-Material.

10 9 8 7 6 5 4 3 2 1 07 06 05 ISBN 3-8273-2267-7

2005 Addison-Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Strae 10-12, D-81829 Mnchen/Germany Alle Rechte vorbehalten Einbandgestaltung: Marco Lindenbeck, webwo GmbH (mlindenbeck@webwo.de) Lektorat: Sylvia Hasselbach, shasselbach@pearson.de Korrektorat: Werner Siedenburg Herstellung: Elisabeth Prmm, epruemm@pearson.de
A Satz: L TEX

Belichtung, Druck und Bindung: Bercker Graph. Vertrieb, Kevelaer Printed in Germany

Inhaltsverzeichnis
1 Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++) 1.1 Signum-Funktion sgn() als Makro und als inline-Funktion 1.2 Falsche und richtige Default-Parameterlisten 1.3 berladen von Funktionen 1.4 Vertauschen mit call-by-reference 1.5 berladen von Funktionen mit unterschiedlichen Vektoren 1.6 Eingabe und formatierte Ausgabe 1.7 Zhlen von Zeichen in Dateien 1.8 Primzahlen mit dem Sieb des Eratosthenes 1.9 Sortieren von Dateien 1.10 Default-Parameter und berladen von Funktionen 1.11 Arrayzugriffe mit traditioneller Modultechnik 1.12 Rotieren eines Arrays 2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++) 2.1 Funktionen in Strukturen Erster Schritt zur Objektorientierung 2.1.1 Eine Kreis-Struktur 2.1.2 Ausen von Namenskonikten 2.1.3 Konstruktoren (1. bung) 2.1.4 Konstruktoren (2. bung) 2.1.5 Konstruktoren und Destruktoren (1. bung) 2.1.6 Konstruktoren und Destruktoren (2. bung) 2.1.7 Programm, das nur Deklarationen enthlt 2.1.8 Zugriff mit und ohne Scope-Operator 2.1.9 Konstruktoren und Arrays 2.1.10 Array-Verwaltung mit Funktionen in Strukturen 2.1.11 Taschenrechner mit Intervallarithmetik 2.1.12 Rechnen mit deutschen Kommazahlen 2.1.13 Peano-Axiome fr natrliche Zahlen 2.1.14 Game of Life 2.2 Einfhrung in die Objektorientierung 2.2.1 Aussagen zur Objektorientierung 2.2.2 OO-Modelle der realen Welt 2.2.2.1 Tiere mit und ohne Beine 11 11 12 12 12 13 14 15 16 17 18 19 20 21 22 22 23 23 24 24 25 26 26 27 27 28 29 30 30 32 32 32 32

Inhaltsverzeichnis
2.2.2.2 Die C-Datentypen 2.2.2.3 Unterschiedliche Menschen Klassen und Objekte 2.3.1 Klassen die objektorientierte Variante zu C-Strukturen 2.3.1.1 Konstruktoren 2.3.1.2 Allgemeine Aussagen zu C++ 2.3.1.3 Initialisierung von Membervariablen 2.3.1.4 Methoden einer Klasse 2.3.1.5 Gre von Objekten 2.3.1.6 Rechner fr Mehrwertsteuer 2.3.1.7 Ein Zeitenrechner 2.3.1.8 Rechner fr sehr grosse Zahlen 2.3.2 Klassen und Objekte in UML 2.3.2.1 Klassen- und Objektdiagramm zu Monopoly 2.3.2.2 Realisierung einer Queue (Warteschlange) 2.3.3 Kopieren und Zuweisen von Objekten 2.3.3.1 Kopieren und Zuweisen von Personendaten 2.3.3.2 Kopieren und Zuweisen von Kontos 2.3.3.3 Realisierung einer Klasse CDoubleArray 2.3.3.4 Konstruktoren, Destruktoren, Kopierkonstruktor und Zuweisungsoperator 2.3.3.5 Kopierkonstruktor und Zuweisungsoperator 2.3.4 Konstante Memberfunktionen und Objekte 2.3.4.1 Eine Kreis-Klasse 2.3.4.2 Eine Klasse fr Bitoperationen 2.3.5 Statische Klassenelemente und Objekte 2.3.5.1 Vergabe und Lschen von Kundennummern 2.3.5.2 Falsches Zhlen von Objekt-Instanzen 2.3.5.3 Auto im Parkhaus sucht sich freien Platz Mehrere Klassen bzw. Objekte im Zusammenspiel 2.4.1 Aufteilung in Klassen 2.4.1.1 Realisierung eines Bruchrechners 2.4.1.2 Realisierung eines Paralleladdierers 2.4.1.3 Gemeinsamer Termin 2.4.1.4 Objektkopien 2.4.1.5 Sequenzdiagramme zum Monopoly-Spiel 2.4.1.6 Kollaborationsdiagramme zum Monopoly-Spiel 2.4.2 Initialisierung von Membervariablen 2.4.2.1 Das dHondtsche Hchstzhlverfahren 2.4.2.2 Kstners Kubikkilometer fr alle Menschen 2.4.2.3 Geldscheine stapeln 2.4.3 Beziehungen zwischen Objekten 2.4.3.1 Ein Vieleck 2.4.3.2 Ein Polygonzug 2.4.3.3 Assoziationen zum Monopoly-Spiel 2.4.3.4 Mastermind gegen den Computer 33 33 34 34 34 34 34 34 34 35 36 37 39 39 39 42 42 43 44 48 49 49 49 50 52 52 53 53 54 54 54 57 59 62 63 64 65 65 67 67 68 68 70 71 75

2.3

2.4

Inhaltsverzeichnis
2.4.3.5 Realisierung eines Taschenrechners Befreundete Funktionen und Klassen 2.4.4.1 Addieren von Brchen 2.4.4.2 Array von ungekrzten und gekrzten Brchen 2.4.4.3 Viele fragwrdige Freunde

7
80 90 90 90 91 93 94 94 94 94 96 98 99 102 102 102 102 102 102 103 103 103 104 105 109 109 109 113 113 114 115 115 115 117 120 121 121 124 127 128 131 131 131 132 133

2.4.4

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions) 3.1 Vererbung 3.1.1 Einfache Vererbung in C++ 3.1.1.1 berschreiben von Funktionen 3.1.1.2 Konstruktor-/Destruktoraufrufe 3.1.1.3 Linie in Grakbibliothek 3.1.1.4 Klassenmodell zu Autos, Dreirdern usw. 3.1.1.5 Erweitern des Klassenmodells zu Fahrzeugen 3.1.1.6 Ableiten eines Lkws von einem Auto 3.1.2 Virtuelle Methoden und Polymorphismus 3.1.2.1 Speicherbedarf bei virtuellen Funktionen 3.1.2.2 Speicherbedarf bei abgeleiteten Klassen 3.1.2.3 Ein Suppenteller in Franken 3.1.2.4 Franken und Berliner 3.1.2.5 Rechnen mittels virtueller Methoden 3.1.2.6 Virtuelle und nicht-virtuelle Destruktoren 3.1.2.7 Tierhierarchie 3.1.2.8 Parkplatz mit Polymorphismus 3.1.2.9 Polymorpher Taschenrechner 3.1.3 Abstrakte Klassen 3.1.3.1 Dummy-Methoden in Basisklasse 3.1.3.2 Zoo mit zuflligen Tieren 3.1.4 Mehrfachvererbung und virtuelle Basisklassen 3.1.4.1 Ehe abgeleitet von Mensch ber Mann/Frau 3.1.4.2 Hai als eischfressender Fisch 3.2 berladen von Operatoren 3.2.1 Ein Uhrzeit-Rechner 3.2.2 Bruchrechner mit berladenen Operatoren 3.2.3 Arrayrechner mit berladenen Operatoren 3.2.4 Rechner fr Dezimal- und Dualzahlen 3.2.5 Implizites Casting 3.2.6 Realisierung von Mengenoperationen ber ein Array 3.2.7 Matrizen durch berladen des Funktionsoperators 3.2.8 Array mit negativen und nicht ganzzahligen Indizes 3.2.9 Rechner fr sehr grosse Zahlen 3.3 Templates 3.3.1 Funktionstemplate zum Vertauschen von zwei Objekten 3.3.2 Unterschiedliche Klassen zu einem Klassentemplate 3.3.3 Ein Stack-Klassentemplate 3.4 Exceptions (Ausnahmebehandlung)

8
3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 4

Inhaltsverzeichnis
Stack-Unwinding Geschachtelte try-catch-Blcke Eigene terminate()-Funktion Vordenierte Standard-Exceptions Exception-Spezikationen Overow- und Underow-Exceptions in einem Stack 133 133 133 133 133 134 135 135 135 136 136 138 138 139 146 146 147 147 147 148 149 150 151 152 152 152 153 154 154 155 157 158 159 161 162 163 165 167 167 168 170 172 175

Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger) 4.1 Standard Template Library (STL) 4.1.1 Sortierte Namensliste 4.1.2 Das Josephus-Spiel 4.1.3 Kubikzahlen ber Polyas Sieb 4.1.4 Print-Container 4.1.5 Umsatzberechnung (Maps in einer Map) 4.1.6 Geburtstagsverwaltung 4.2 Laufzeit-Typinformationen (RTTI) 4.2.1 Identizieren von Objekttypen Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster) 5.1 Entwurfsprinzipien 5.1.1 Lehrer-Kreide-Tafel 5.1.2 Realisierung der NLS-Untersttzung in C++ 5.1.3 Segelboot 5.1.4 Ente 5.1.5 Anwendung der abstrakten Fabrik auf GUI-Elemente 5.2 Objektorientierte Vorgehensweise 5.2.1 Hotelbuchung 5.2.2 Haftpichtschaden 5.2.3 Essen bestellen, verzehren und bezahlen 5.3 Entwurfsmuster (Design Patterns) 5.3.1 Einsatz einer abstrakten Fabrik 5.3.2 Unterschiedliche Erbauer als Stack und als Queue 5.3.3 Proxy (Simulation eines File-Puffers) 5.3.4 Adapter fr deutsches Zeitformat in das amerikanische 5.3.5 Vermeiden von Klassen-Explosion mit Dekorierer-Muster 5.3.6 Iterator (Rckwrtiges Traversieren eines Binrbaums) 5.3.7 Beobachter-Muster fr verschiedene Zahlenkonvertierer 5.3.8 Zustands-Muster zu vorgegebenen Zustandsautomaten 5.3.9 Mementos zur gleichzeitigen Verwaltung mehrerer Spieler Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt) 6.1 Zufllige RGB-Farben 6.2 Ziehen von Lottozahlen 6.3 Synchronisierte Anzeige verschiedener Zahlensysteme 6.4 Rasterung in einer Zeichenche 6.5 Grasches Mastermind-Spiel gegen den Computer

Inhaltsverzeichnis
7 Lsungen zum Anhang A 7.1 Exceptions in Konstruktoren 7.2 Exceptions versus setjmp()/longjmp() 8 Lsungen zum Anhang B 8.1 Schnittstellen 8.1.1 Schnittstelle ISprechweise fr unterschiedliche Dialekte 8.1.2 Eine allgemein verwendbare verkettete Liste 8.2 Assoziative Arrays durch berladen des Indexoperators 8.2.1 Binrbaum mittels eines assoziativen Arrays 8.3 Smart- und Referenz-Pointer 8.3.1 Lager-Verwaltung mit Referenz-Pointern Index

9
179 179 180 181 181 181 181 185 185 186 186 193

Kapitel 1
Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++)
1.1 Signum-Funktion sgn() als Makro und als inline-Funktion
Das Programm signum.cpp wrde folgende Ausgabe liefern:
sgn(z1) = 0 [Makro-Realisierung ist falsch] sgn(z2) = -1

Die Makro-Realisierung ist hier falsch, da bei der Textersetzung der bergebene Parameter ++z1 zweimal eingesetzt und damit auch flschlicherweise zweimal inkrementiert wird. Wrde man dieses Programm nur durch den Prprozessor schicken, wie z. B. unter Linux/Unix mit cpp, so kann man dies sehr schn erkennen: > cpp signum.cpp
......... ......... inline int sgnFkt(int z) return (z > 0) - (z < 0);

int main(void) { int z1 = -2, z2 = -2; cout << "sgn(z1) = " << (++z1 > 0) - (++z1 < 0) << endl; cout << "sgn(z2) = " << sgnFkt(++z2) << endl; }

11

12

1 Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++)

1.2

Falsche und richtige Default-Parameterlisten

void f1(int x, int y, int c = 0); // erlaubt void f2(int x, int y = 0, int z = 0); // erlaubt void f3(int x, int y = 0, int z); // nicht erlaubt, da nur am Ende mglich void f4(int i=0, ...); // nicht erlaubt: Default-Param. mit variablen Parametern

1.3

berladen von Funktionen

1. Anweisungsfolge
long fkt(int b, int y=10) { } // nicht erlaubt (aufgrund des Defaultparam.) long fkt(int b) { } // .........................................

2. Anweisungsfolge
int fkt(int a, int b) { } double fkt(double a, double b) { } .... y = fkt( 251, 12 ); // Richtiger Aufruf (der ersten fkt()-Funktion)

y = fkt( 4.2, 6.8 ); // Richtiger Aufruf (der zweiten fkt()-Funktion) y = fkt( 10, 2.7 ); // <-- Fehlerhafter Aufruf

3. Anweisungsfolge
long f(int a, int b) { } // nicht erlaubt: Rckgabetyp zhlt nicht zur Signatur char f(int a, int b) { } // ...................................................

1.4

Vertauschen mit call-by-reference

Programm 1.1 swap.cpp: Vertauschen von int-, double- und char *-Variablen
void swap(int &a, int &b) { // Vertauschen von zwei int-Variablen int h = a; a = b; b = h; } void swap(double &a, double &b) { // Vertauschen von zwei double-Variablen double h = a; a = b; b = h; } void swap(char* &a, char* &b) { // Vertauschen von zwei char-Zeiger char *h = a; a = b; b = h; }

12

1.5 berladen von Funktionen mit unterschiedlichen Vektoren

13

1.5 berladen von Funktionen mit unterschiedlichen Vektoren


Programm 1.2 vectoradd.cpp: berladen von Funktionen mit unterschiedlichen Vektoren
int add(vector<int>v) { int sum = 0; cout << v[0]; for (unsigned i=1; i < v.size(); i++) cout << " + " << v[i]; for (unsigned i=0; i < v.size(); i++) sum += v[i]; return sum; } double add(vector<double>v) { double sum = 0;

cout << v[0]; for (unsigned i=1; i < v.size(); i++) cout << " + " << v[i]; for (unsigned i=0; i < v.size(); i++) sum += v[i]; return sum; } string add(vector<string>v) { string sum; cout << v[0]; for (unsigned i=1; i < v.size(); i++) cout << " + " << v[i]; for (unsigned i=0; i < v.size(); i++) sum += v[i]; return sum; }

13

14

1 Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++)

1.6

Eingabe und formatierte Ausgabe

Programm 1.3 schuh.cpp: Einfache Ein- und Ausgabe


#include <iostream> #include <iomanip> #include <cstring> using namespace std; #define ZEICH_MAX #define BEZ_COL #define GR_COL struct schuh_struct { char m_bez[ZEICH_MAX]; char m_abt; int // m: Maenner, f: Frauen, k: Kinder 20 ZEICH_MAX 5 // max. Zeichen fr Schuhbezeichnung (incl. \0) // Breite der Spalte fr Bezeichung // Breite der Spalte fr Grsse

m_groesse;

} schuhe[10];

void einlesen(int i) { cout << "\nBezeichnung: "; cin.get(schuhe[i].m_bez, ZEICH_MAX); if (strlen(schuhe[i].m_bez)==ZEICH_MAX-1) // bei evtl. lngerer Bezeichnung --> cin.ignore(100, \n); // restl. Zeichen aus Eingabe entf.

cout << "Schuhabteilung: (m)aenner, (f)rauen, (k)inder: "; cin >> schuhe[i].m_abt; cout << "Groesse: "; cin >> schuhe[i].m_groesse;

} void ausgeben(int i) { cout << setiosflags(ios::left) << setfill(.)

<< setw(BEZ_COL) << schuhe[i].m_bez << setiosflags(ios::right) << setfill( ) << setw(GR_COL) << " } int main(void) { char int weiter=j; i; << schuhe[i].m_groesse

" << schuhe[i].m_abt << resetiosflags(ios::right) << endl;

cout << "*** Eingabe von max. 10 Schuhartikeln: " << endl; for (i=0; i < 10 && weiter == j; i++) { einlesen(i); cout << "--> Noch einen Artikel eingeben (j/n): "; cin >> weiter; cin.ignore(); } cout << endl << "*** Formatierte Ausgabe aller Schuhartikel:" << endl; for (int j=0; j < i ;j++) ausgeben(j); } // Return aus Eingabepuffer lschen

14

1.7 Zhlen von Zeichen in Dateien

15

1.7 Zhlen von Zeichen in Dateien


Programm 1.4 zeichzaehl.cpp: Zhlen von Zeichen in Dateien
#include #include #include #include <vector> <fstream> <iomanip> <iostream>

using namespace std;

int {

main(int argc, char *argv[]) unsigned char x = 0; zeich;

vector<int> zaehl(128);

for (int i=1; i < argc; i++) { ifstream eing(argv[i]); // ffnen der zu lesenden Datei if (eing) // konnte Datei erfolgreich geffnet werden while (eing.get(zeich)) zaehl[int(zeich)]++; } if (argc > 1) for (unsigned z=1; z < zaehl.size(); z++) if (isprint(char(z))) cout << char(z) << " " << setw(3) << zaehl[z] << ((++x % 8) ? " | cout << endl; } " : "\n");

15

16

1 Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++)

1.8

Primzahlen mit dem Sieb des Eratosthenes

Programm 1.5 primsieb.cpp: Primzahlen mit dem Sieb des Eratosthenes


#include #include #include <vector> <iomanip> <iostream>

using namespace std;

int main(void) { unsigned long i, j, n, z=0; vector<bool> array;

cout << "Primzahlen bis wohin: "; cin >> n;

for (i=1; i<=n ; i++) array.push_back(true); for (i=2; i<=n ; i++) if (array[i]) for (j=2*i ; j<=n ; j += i) array[j] = false; for (i=2; i<=n ; i++) if (array[i]) cout << setw(8) << i << ( (++z%10==0) ? "\n" : "" ); cout << endl; return 0; }

16

1.9 Sortieren von Dateien

17

1.9 Sortieren von Dateien


Programm 1.6 sortfile.cpp: Sortieren von Dateien
#include #include #include #include <string> <vector> <fstream> <iostream>

using namespace std;

int main(int argc, char *argv[]) { if (argc != 3) { cerr << "usage: " << argv[0] << " quelldatei zieldatei" << endl; exit(1); } string zeile; vector<string> v; ifstream eing(argv[1]); // ffnen der zu lesenden Datei ofstream ausg(argv[2]); // ffnen der zu schreibenden Datei

if (eing && ausg) // konnten beide Dateien erfolgreich geffnet werden while (getline(eing, zeile)) v.push_back(zeile);

for (unsigned i = 0; i < v.size()-1; i++) for (unsigned j = i+1; j < v.size(); j++) if (v[i] > v[j]) { string h = v[i]; v[i] = v[j]; v[j] = h; } for (unsigned z = 0; z < v.size(); z++) ausg << v[z] << endl; }

17

18

1 Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++)

1.10

Default-Parameter und berladen von Funktionen

Programm 1.7 konvert.cpp: Default-Parameter und berladen von Funktionen


#include <iostream> #include <cstring> #include <cctype>

using namespace std;

void zaehl( int start=1, int ende=5, int basis=10 ) { int x[100];

cout << "---- Zaehle von " << start << " bis " << ende << " im " << basis << "er-System" << endl << " for (int i=start; i<=ende; i++) { int z=i, n=0; while (z>0) { x[n++] = z % basis; z /= basis; } for (int j=n-1; j>=0; j--) cout << char(x[j]<10 ? x[j]+0 : x[j]-10+A); cout << ", "; } cout << endl; } void zaehl( char *string, char zeich ) { int z = 0; ";

for (unsigned i=0; i<strlen(string); i++) if (tolower(string[i]) == tolower(zeich)) z++; cout << "---- Das Zeichen " << zeich << " kommt im String \"" << string << "\" " << z << " mal vor" << endl; } int main(void) { zaehl(); zaehl( 3, 9, 2 ); zaehl( 20, 30, 16 ); zaehl( 2 );

zaehl( "Westernleder", e ); zaehl( "Abraham", a ); }

18

1.11 Arrayzugriffe mit traditioneller Modultechnik

19

1.11

Arrayzugriffe mit traditioneller Modultechnik

Programm 1.8 array.cpp: Implementierung des Array-Automaten in traditioneller Modul-Technik


#include <iostream> #include <iomanip> #include <cstring> #include <cstdlib> #include <ctime> #include "array.h" using namespace std; //.... Lokal nur in diesem Modul sichtbare Funktion (static) static void tausch( double &a, double &b ) { double h = a; a = b; b = h; }

void initArray( array &a, char *name, int anzahl ) { srand( time(NULL) ); // Startwert des Zufallszahlengenerators zufllig setzen a.sortiert a.anzahl = false; = anzahl;

a.meinArray = new double[anzahl]; a.name = new char[strlen(name)+1]; strcpy( a.name, name ); for (int i=0; i<a.anzahl; i++) a.meinArray[i] = double(rand())/rand(); // Neue Form des Castings } void fuelleArray( array &a ) { a.sortiert = false; for (int i=0; i<a.anzahl; i++) a.meinArray[i] = double(rand())/rand(); // Neue Form des Castings } void sortiereArray( array &a, bool aufwaerts ) { for (int i=0; i<a.anzahl-1; i++) // Bubble-Sort

for (int j=i+1; j<a.anzahl; j++) if ( ( aufwaerts && a.meinArray[i] > a.meinArray[j]) || (!aufwaerts && a.meinArray[i] < a.meinArray[j]) tausch( a.meinArray[i], a.meinArray[j] ); a.sortiert = true; } void gibausArray( array &a ) { if (a.anzahl == 0) return; cout << "--------------------------------------------- " << a.name << " ----" << endl; cout << " Groesse des Arrays: " << a.anzahl << endl; cout << " cout << " Array sortiert: " << (a.sortiert ? "ja" : "nein") << endl; " << setiosflags(ios::fixed) << setprecision(2); )

for (int i=0; i<a.anzahl; i++) cout << a.meinArray[i] << " " ; cout << endl; }

19

20

1 Lsungen zu Teil I (Nicht-objektorientierte Erweiterungen in C++)

1.12

Rotieren eines Arrays

Programm 1.9 arrayrotate.cpp: Rotieren eines Arrays mit Referenzparameter und Referenzrckgabe
arrayStruct &rotiereArray( arrayStruct &a, int n=1, bool rechtsShift=true ) { int i, j, y[groesse];

if (n<0 || n>=groesse) return a;

if (rechtsShift) { for (i=groesse-n, j=0; i<groesse; i++, j++) y[j] = a.x[i]; for (i=groesse-1; i>=n; i--) a.x[i] = a.x[i-n]; for (i=0; i<n; i++) a.x[i] = y[i]; } else { for (i=0; i<n; i++) y[i] = a.x[i]; for (i=n; i<groesse; i++) a.x[i-n] = a.x[i]; for (i=groesse-n, j=0; i<groesse; i++, j++) a.x[i] = y[j]; } return a; }

20

Kapitel 2
Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

21

22

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.1

Funktionen in Strukturen Erster Schritt zur Objektorientierung

2.1.1 Eine Kreis-Struktur


Programm 2.1 kreis.cpp: Eine Kreis-Struktur
......... struct Kreis { public: Kreis(double r=1) { setzeRadius(r); Kreis(Kreis& k) }

{ setzeRadius(k.getRadius()); }

void setzeRadius(double r) { radius = r; umfang = 2*radius*PI; flaeche = radius*radius*PI; } void setzeUmfang(double u) { umfang radius = u;

= umfang / (2*PI); flaeche = radius*radius*PI; } void setzeFlaeche(double f) { flaeche radius umfang } = f; = sqrt(flaeche / PI); = 2*radius*PI;

double getRadius(void) double getUmfang(void)

{ return radius; { return umfang;

} }

double getFlaeche(void) { return flaeche; }

void print(void) { cout << setiosflags(ios::left) << "r=" << setw(8) << radius

<< "f=" << setw(10) << flaeche << "u=" << setw(10) << umfang << endl; }

private: double radius, umfang, flaeche; static const double PI = 3.14159; }; ........

22

2.1 Funktionen in Strukturen Erster Schritt zur Objektorientierung

23

2.1.2 Ausen von Namenskonikten


Bei diesem Programmausschnitt haben Parameter und Membervariablen gleiche Namen, was zu einer falschen Programmverhalten fhrt. Um die Membervariablen von den Parameternamen zu unterscheiden, gibt es zwei Mglichkeiten: Voranstellen von this-> vor den Membervariablen
struct Kreis { ............. void setzeRadius(double radius) { this->radius = radius; umfang = 2*radius*PI; flaeche = radius*radius*PI; } void setzeUmfang(double umfang) { this->umfang radius = umfang;

= umfang / (2*PI);

flaeche = radius*radius*PI; } ............. };

Voranstellen des Strukturnamens mit doppelten Doppelpunkt Kreis:: vor den Membervariablen
struct Kreis { ............. void setzeRadius(double radius) { Kreis::radius = radius; umfang = 2*radius*PI; flaeche = radius*radius*PI; } void setzeUmfang(double umfang) { Kreis::umfang radius = umfang;

= umfang / (2*PI);

flaeche = radius*radius*PI; } ............. };

2.1.3 Konstruktoren (1. bung)


Konstruktor: (0, 0) Konstruktor: (1, 0) Konstruktor: (1, 2) Konstruktor: (4, 5) Konstruktor: (0, 0)

23

24

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.1.4 Konstruktoren (2. bung)


Konstruktor global ...Am Anfang von main() Konstruktor mainLokal1 Konstruktor no Name Konstruktor mainLokalStatic ...Am Anfang von funk() Konstruktor funkLokal Konstruktor funkLokalStatic ...Am Ende von funk() Konstruktor dynamisch (Rckgabe von Zeiger) Destruktor funkLokal In main() nach 1. Aufruf von funk() ...Am Anfang von funk() Konstruktor funkLokal ...Am Ende von funk() Konstruktor dynamisch (Rckgabe von Zeiger) Destruktor funkLokal In main() nach 2. Aufruf von funk() Destruktor dynamisch (Rckgabe von Zeiger) Konstruktor temporaer Destruktor temporaer ...Am Ende von main() Destruktor no Name Destruktor mainLokal1 Destruktor funkLokalStatic Destruktor mainLokalStatic Destruktor global

2.1.5 Konstruktoren und Destruktoren (1. bung)


Programm dualbyte.cpp gibt Folgendes aus:
BBBBBBBB BYTE(1) BBBBBBBB BYTE(2) BBBBBBBB BYTE(3) --------

byte(3) bbbbbbbb add() byte(2) bbbbbbbb ....... byte(2) bbbbbbbb byte(1) bbbbbbbb

Die Ausgabe der ersten beiden Zeilen wird durch die Konstruktoraufrufe bei den beiden folgenden Denitionen bewirkt:

24

2.1 Funktionen in Strukturen Erster Schritt zur Objektorientierung

25

byte b1 = 1, b2 = 2;

Die folgende Ausgabe:


BBBBBBBB BYTE(3) --------

byte(3) bbbbbbbb

wird durch den lokalen Block bewirkt:


{ //... Lokaler Block byte b3(3); // Anlegen eines temporren byte-Objekts cout << "--------\n"; // temporres byte-Objekt wird mit Verlassen des Blocks wieder zerstrt

Mit dem Verlassen dieses Blocks wird automatisch der Destruktor zu b3 aufgerufen. Danach wird mit
b1.add(b2);

die Methode add() der Strukturvariablen b1 aufgerufen. In dieser Methode wird dann der String add() ausgegeben. Viel wichtiger ist hier aber nun, dass zur Strukturvariablen b2 eine Kopie auf dem Stack erstellt wird. Mit dem Verlassen der Methode add() wird dann automatisch der Destruktor zu dieser Kopie aufgerufen, so dass zunchst folgendes ausgegeben wird:
byte(2) bbbbbbbb

Nachdem in main() eine Reihe von Punkten ausgegeben wurde, wird das Programm beendet und somit werden automatisch die Destruktoren der beiden noch existierenden Strukturvariablen b2 und b1 aufgerufen, was folgende Ausgabe nach sich zieht:
byte(2) bbbbbbbb byte(1) bbbbbbbb

2.1.6 Konstruktoren und Destruktoren (2. bung)


Programm konstrdestr.cpp gibt Folgendes aus:
Konstruktor: (0, 0) Konstruktor: (1, 0) Konstruktor: (1, 2) Konstruktor: (4, 5) Destruktor : (4, 5) Konstruktor: (0, 0) Destruktor : (0, 0) Destruktor : (1, 2) Destruktor : (0, 0) Destruktor : (4, 5)

25

26

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.1.7 Programm, das nur Deklarationen enthlt


In main() benden zwar keine Ausgabeanweisungen, aber es werden eine Reihe von Strukturvariablen angelegt, in deren Konstruktoren und Destruktoren etliche Ausgabeanweisungen enthalten sind. Programm konstrdestr2.cpp liefert deshalb die folgende Ausgabe:
..Programm wird geladen ...Programm wird ausgefhrt ...Programm wird ausgefhrt ...Programm wird ausgefhrt Bruch: 1/2 erzeugt Bruch: 0/1 erzeugt Bruch: 3/4 erzeugt Bruch: 4/1 erzeugt Bruch: 4/1 entfernt Bruch: 0/1 entfernt Bruch: 1/2 entfernt ..Programm wird beendet; Adieu

Erluterung zu dieser Ausgabe: Zunchst wird der Konstruktor der globalen Strukturvariablen (OO-Sprechweise: Objektes) meinProg durchlaufen. Dann folgen die lokalen Objekte der Funktion main(). Werden beim Anlegen keine Parameter angegeben, so werden die entsprechenden Defaultparameter verwendet. Der Bruch b4(4,1) wird innerhalb eines eigenen lokalen Blockes deniert. Daher wird am Ende des Blockes dann der Destruktor durchlaufen. Die Destruktoren der Brche b2(1,2) und b3(0,1) werden am Ende von main() bei der Anweisung return 0 durchlaufen. Der Bruch b1(3,4), der dynamisch angelegt wurde, wird nie mit delete zerstrt (sehr unschn nur zur Demonstration!); daher wird dessen Destruktor nie durchlaufen. Nachdem main() beendet wurde, werden die globalen Variablen zerstrt und damit der Destruktor von meinProg durchlaufen.

2.1.8 Zugriff mit und ohne Scope-Operator


Programm scopestruct.cpp liefert die folgende Ausgabe:

x (main lokal1) = 1 x (global) = 10 x (main lokal2) = 2 x (global) = 20 x (main lokal1) = 1 x (main global) = 20 x (Strukt) = 1, x (global) = 21, x (Strukt) = 2, x (global) = 22,

26

2.1 Funktionen in Strukturen Erster Schritt zur Objektorientierung

27

2.1.9 Konstruktoren und Arrays


Programm konstrarray.cpp liefert die folgende Ausgabe:
Konstruktor: (0, 0) Konstruktor: (0, 0) Konstruktor: (0, 0) | | Complex z[3]; |

Konstruktor: (3, 4) <--- Complex *k1 = new Complex(3, 4); Konstruktor: (5, 0) <--- Complex *k2 = new Complex(5); Destruktor : (3, 4) <--- delete k1; Destruktor : (0, 0) Destruktor : (0, 0) Destruktor : (0, 0) | | Beim Verlassen von main: Destruktoren zum Array |

2.1.10 Array-Verwaltung mit Funktionen in Strukturen


Programm 2.2 arraystruct.cpp: Implementierung des Array-Automaten mit Funktionen in Strukturen
#include <iostream> #include <iomanip> #include <cstring> #include <cstdlib> #include <ctime> #include "arraystruct.h" using namespace std;

array::array( char *name, int anzahl ) { srand( time(NULL) ); // Startwert des Zufallszahlengenerators zufllig setzen sortiert = false; this->anzahl = anzahl; meinArray = new double[anzahl]; this->name = new char[strlen(name)+1]; strcpy( this->name, name ); for (int i=0; i<anzahl; i++) meinArray[i] = double(rand())/rand(); // Neue Form des Castings } void array::fill(void) { sortiert = false;

for (int i=0; i<anzahl; i++) meinArray[i] = double(rand())/rand(); // Neue Form des Castings } void array::sort(bool aufwaerts) { for (int i=0; i<anzahl-1; i++) // Bubble-Sort

for (int j=i+1; j<anzahl; j++) if ( ( aufwaerts && meinArray[i] > meinArray[j]) || (!aufwaerts && meinArray[i] < meinArray[j]) tausch( meinArray[i], meinArray[j] ); sortiert = true; } )

27

28

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

void array::print(void) { if (anzahl == 0) return; cout << "--------------------------------------------- " << name << " ----" << endl; cout << " Groesse des Arrays: " << anzahl << endl; cout << " cout << " Array sortiert: " << (sortiert ? "ja" : "nein") << endl; " << setiosflags(ios::fixed) << setprecision(2);

for (int i=0; i<anzahl; i++) cout << meinArray[i] << " " ; cout << endl; }

2.1.11

Taschenrechner mit Intervallarithmetik

Programm 2.3 intervall.cpp: Implementierung zum Taschenrechner mit Intervallarithmetik


#include "intervall.h"

void Intervall::sub(Intervall iv) { low -= iv.getHigh();

high -= iv.getLow(); } void Intervall::mult(Intervall iv) { a[0] = low a[1] = low * iv.getLow(); * iv.getHigh();

a[2] = high * iv.getLow(); a[3] = high * iv.getHigh(); sort(); low = a[0];

high = a[3]; } void Intervall::divi(Intervall iv) { a[0] = low a[1] = low / iv.getLow(); / iv.getHigh();

a[2] = high / iv.getLow(); a[3] = high / iv.getHigh(); sort(); low = a[0];

high = a[3]; } void Intervall::sort(void) { for (int i=0; i<3; i++) for (int j=i+1; j<4; j++) if (a[i] > a[j]) { double h = a[i]; a[i] = a[j]; a[j] = h; } }

28

2.1 Funktionen in Strukturen Erster Schritt zur Objektorientierung

29

2.1.12 Rechnen mit deutschen Kommazahlen


Programm 2.4 komma.cpp: Die Struktur KommaZahl zum Rechnen mit deutschen Kommazahlen
struct KommaZahl { public: KommaZahl(char *z) { macheString(zahl = macheDouble(z)); }

void add(char *z) { macheString(zahl += macheDouble(z)); } void add(KommaZahl z) { macheString(zahl += z.getDouble()); }

void sub(char *z) { macheString(zahl -= macheDouble(z)); } void sub(KommaZahl z) { macheString(zahl -= z.getDouble()); }

void mul(char *z) { macheString(zahl *= macheDouble(z)); } void mul(KommaZahl z) { macheString(zahl *= z.getDouble()); } void div(char *z) { macheString(zahl /= macheDouble(z)); } void div(KommaZahl z) { macheString(zahl /= z.getDouble()); }

double getDouble(void) { return zahl; } char *getString(void) { return str; }

private: double zahl; char str[100];

double macheDouble(char *z) { strcpy(str, z);

// konvertiert z in double-Zahl

char *zgr = strchr(str, ,); if (zgr != NULL) *zgr = .; return strtod(str, NULL); } void macheString(double z) { sprintf(str, "%g", z); char *zgr = strchr(str, .); if (zgr != NULL) *zgr = ,; } }; // konvertiert z in deutsche Schreibweise

29

30

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.1.13

Peano-Axiome fr natrliche Zahlen

Programm 2.5 peano.cpp: Die Struktur Peano


struct Peano { public: Peano(unsigned zahl) { z = zahl; } Peano add(Peano x) { return (x.z==0) ? z : succ().add(x.pred()); } Peano sub(Peano x) { return (x.z==0) ? z : pred().sub(x.pred()); } Peano mul(Peano x) { return (x.z==0) ? 0 : add(mul(x.pred())); Peano pot(Peano x) { return (x.z==0) ? 1 : mul(pot(x.pred())); unsigned get(void) { return z; } private: Peano succ(void) { return Peano(z+1); } } }

Peano pred(void) { return (z==0) ? Peano(0) : Peano(z-1); } unsigned z; };

2.1.14

Game of Life

Programm 2.6 life.cpp: Game of Life


struct feld { public: //... Konstruktor (erzeugt Felder mit hoehe x breite Zellen) feld( int hoehe, int breite ) { this->breite = breite; this->hoehe = hoehe; n = 0;

lifeFeld = new zelle * [hoehe]; hilfFeld = new zelle * [hoehe];

// hoehe zelle-Zeiger anlegen // hoehe zelle-Zeiger anlegen

for (int i=0; i<hoehe; i++) { // breite zelle-Var. pro hoehe-Zeiger lifeFeld[i] = new zelle [breite]; hilfFeld[i] = new zelle [breite]; } } int getHoehe(void) { return hoehe; }

int getBreite(void) { return breite; } void set( int i, int j, bool lebt) { lifeFeld[i][j].setZelle( lebt ); } bool get( int i, int j) void naechsteGeneration( void ) { for (int i=0; i<hoehe; i++) for (int j=0; j<breite; j++) { int lebtNachbarn = getLebtNachbarn( i, j ); if (lebtNachbarn <= 1 || lebtNachbarn >=4) hilfFeld[i][j].setZelle( false ); else if (lebtNachbarn == 3 ) hilfFeld[i][j].setZelle( true ); else hilfFeld[i][j].setZelle( lifeFeld[i][j].getZelle() ); } { return lifeFeld[i][j].getZelle(); }

30

2.1 Funktionen in Strukturen Erster Schritt zur Objektorientierung

31

tausch( lifeFeld, hilfFeld ); } void ausgabe(void) { int i, j;

n++;

cout << n << ". Generation" << endl << "+"; for (j=0; j<breite; j++) cout << "-"; cout << "+" << endl; for (i=0; i<hoehe; i++) { cout << "|"; for (j=0; j<breite; j++) cout << (get(i, j) ? * : ); cout << "|" << endl; } cout << "+"; for (j=0; j<breite; j++) cout << "-"; cout << "+" << endl; } private: int int breite, hoehe; n; // Nummer der Generation

zelle **lifeFeld; // Zweidim. Array von zelle-Elementen zelle **hilfFeld; // Zweidim. Array von zelle-Elementen (Hilfsfeld) int getLebtNachbarn( int i, int j ) { int summe = 0; for (int x=i-1; x<=i+1; x++) for (int y=j-1; y<=j+1; y++) { int x2=x, y2=y; if (x<0) x2 = hoehe-1; else if (x>=hoehe) x2 = 0; if (y<0) y2 = breite-1; else if (y>=breite) y2 = 0; if (lifeFeld[x2][y2].getZelle()) summe++; } if (lifeFeld[i][j].getZelle()) summe--; return summe; } void tausch( zelle** &a, zelle** &b ) { zelle **h = a; a = b; b = h; } }; int main(void) { ..... }

31

32

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.2

Einfhrung in die Objektorientierung

2.2.1 Aussagen zur Objektorientierung


Nachfolgend sind die richtigen Aussagen fett gedruckt: 1. Ein Objekt ist ein Exemplar einer Klasse. 2. Eine Klasse ist ein Objekt. 3. Bei der Vererbung erbt die bergeordnete Klasse alle Eigenschaften der Unterklasse. 4. Ein Objekt ist eine Klasse. 5. Eine Unterklasse erbt nur bestimmte, vom Benutzer whlbare Eigenschaften von ihrer Oberklasse. 6. Ein Objekt ist eine Instanz einer Klasse. 7. Eine Unterklasse ist eine Klasse, die durch Vererbung aus einer anderen Klasse entsteht. 8. Das Neue an der Objektorientierung ist die klare Trennung zwischen Daten und Funktionen, so dass beliebige Funktionen jederzeit auf globale Daten zugreifen knnen. 9. Eine Unterklasse erbt alle Eigenschaften von ihrer Oberklasse. 10. Eine Klasse ist ein Exemplar eines Objekts. 11. Objektorientierung ermglicht die Wiederverwendbarkeit von Software.

2.2.2 OO-Modelle der realen Welt


2.2.2.1 Tiere mit und ohne Beine Abbildung 2.1 zeigt die Lsung zu dieser Aufgabenstellung.
Lebewesen
<<instance of>>

"Mein Haustier"

Vierbeiner

Zweibeiner

Beinloses Tier

Hund
<<instance of>> <<instance of>>

Katze
<<instance of>>

Vogel

Mensch

Schlange

Fisch

<<instance of>>

<<instance of>>

Bello

Rex

Mausi

"Mein Kater"

"Ringel, meine Schlange"

Hai

Abbildung 2.1: Objektorientiertes Modell zu Tiere mit und ohne Beine

32

2.2 Einfhrung in die Objektorientierung

33

2.2.2.2 Die C-Datentypen Abbildung 2.2 zeigt die Lsung zu dieser Aufgabenstellung.
Datentyp

Ganzzahl

Gleitpunktzahl

numerisch

alphanumerisch

double

float

<<instance of>>

short

int

char

pi

<<instance of>>

<<instance of>>

zaehler

zeichen

Abbildung 2.2: Objektorientiertes Modell zu den C-Datentypen

2.2.2.3 Unterschiedliche Menschen Abbildung 2.3 zeigt die Lsung zu dieser Aufgabenstellung.
Mensch
<<instance of>>

"Hans Meier"

Seefahrer

Nobelpreistrger

Politiker

<<instance of>>

Sindbad

Pirat

Literatur

Physik

amerik. Prsident

Bundeskanzler

<<instance of>>

<<instance of>>

<<instance of>> <<instance of>> <<instance of>>

"H. Hesse" "A. Einstein"

Lincoln

Nixon

Adenauer

Abbildung 2.3: Objektorientiertes Modell zu Unterschiedliche Menschen

33

34

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3

Klassen und Objekte

2.3.1 Klassen die objektorientierte Variante zu C-Strukturen


2.3.1.1 Konstruktoren richtig ist: 1,2,3 Erluterung: Ein Objekt einer Klasse kann nur angelegt werden, wenn der Konstruktor public ist. Damit punkt1 angelegt werden kann, muss ein Konstruktor mit zwei int-Parametern existieren. Als Konsequenz muss nun aber auch der Standardkonstruktor neu deniert werden, damit punkt2 angelegt werden kann. 2.3.1.2 Allgemeine Aussagen zu C++ richtig ist: 2 zu 1: Ist lediglich eine Konvention, hat aber nichts mit dem C++-Compiler zu tun. zu 3: Inline-Funktionen sind nur public, wenn sie im public-Teil angegeben sind. zu 4: Eine Klasse kann als Typen auch andere Klassen enthalten. zu 5: Von anderen Klassen knnen nur die Methoden aufgerufen werden, die im public-Teil angegeben sind. 2.3.1.3 Initialisierung von Membervariablen richtig ist: 3; alles andere ist Unsinn. 2.3.1.4 Methoden einer Klasse richtig ist: 3 und 5; alles andere ist Unsinn. 2.3.1.5 Gre von Objekten Programm objgroesse.cpp gibt z. B. Folgendes aus:
sizeof(hundert) = 8 sizeof(tausend) = 8

Diese Ausgabe erklrt sich dadurch, dass jedes Objekt seine eigenen Membervariablen besitzt, was hier ein double-Zeiger mit 4 Bytes (knnten auch abhngig vom Prozessor mehr sein) und eine unsigned-Variable mit 4 Bytes sind, so dass jedes Objekt 8 Bytes Speicherplatz fr die Datenelemente belegt, aber keinerlei Speicherplatz fr die Memberfunktionen. Auch wird natrlich nicht der mit new angeforderte Speicherplatz mitgezhlt, da dieser ja nicht zu den Objekten gehrt, denn in den Objekten bendet sich lediglich ein Zeiger auf diesen allozierten Speicherplatz.

34

2.3 Klassen und Objekte

35

2.3.1.6 Rechner fr Mehrwertsteuer


Programm 2.7 mwst.cpp: Die Klasse CMwst
class CMwst { public: CMwst(double betrag, kennung typ=ohneMwst, double prozent=16) { set(betrag, typ, prozent); } void set(double betrag, kennung typ=ohneMwst, double prozent=16) { m_prozent = prozent / 100; switch (typ) { case mwst: m_mwst m_netto = betrag; = betrag / m_prozent;

m_brutto = m_netto + m_mwst; break; case ohneMwst: m_netto m_mwst = betrag;

= betrag * m_prozent; m_brutto = m_netto + m_mwst; break; case mitMwst: m_brutto = betrag; m_netto m_mwst break; default: cerr << "Unerlaubter Konstruktor-Aufruf" << endl; break; } } double getMwst(void) double getNetto(void) double getBrutto(void) { return m_mwst; { return m_netto; { return m_brutto; } } } = betrag / (1 + m_prozent); = m_brutto - m_netto;

double getProzent(void) { return m_prozent*100; } void print(void) { cout << setiosflags(ios::fixed) << setprecision(2) << setw(10) << m_netto << " (" << setw(3) << setw(6) << setw(8) } private: double m_mwst, m_netto, m_brutto, m_prozent; }; << m_prozent*100 << "% Mwst: " << m_mwst << ") ---> Brutto: " << m_brutto << endl;

35

36

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3.1.7 Ein Zeitenrechner


Programm 2.8 zeitrechner.cpp: Implementierung der Klasse CZeitRechner
static void gibZeitAus(int tage, int std, int min, int sek, char *str) { int zeit_in_sek = (((tage * 24) + std) * 60 + min) * 60 + sek; cout << setw(22) << str << setw(4) << setw(4) << tage << " Tage, " << setw(4) << std << " Std, " << min << " Min, " << setw(4) << sek << " Sek "

<< " = " << setw(10) << zeit_in_sek << " Sek" << endl << setfill(-) << setw(80) << "-" << setfill( ) << endl; } void CZeitRechner::set(int tage, int std, int min, int sek, bool ausgabe) { m_tage = tage; m_std = std; m_min = min; m_sek = sek;

m_zeit_in_sek = (((m_tage * 24) + m_std) * 60 + m_min) * 60 + m_sek; sek_in_zeit(); // um evtl. ausserhalb liegende Std, Min, Sek zu korrigieren if (ausgabe) print(); } void CZeitRechner::add(int tage, int std, int min, int sek, bool ausgabe) { if (ausgabe) { print(); gibZeitAus(tage, std, min, sek, "+ "); } m_zeit_in_sek += (((tage * 24) + std) * 60 + min) * 60 + sek; sek_in_zeit(); // um evtl. ausserhalb liegende Std, Min, Sek zu korrigieren if (ausgabe) print(); } void CZeitRechner::add(CZeitRechner& zeit, bool ausgabe) { add(zeit.getTage(), zeit.getStunden(), zeit.getMinuten(), zeit.getSekunden(), ausgabe); } void CZeitRechner::sub(int tage, int std, int min, int sek, bool ausgabe) { if (ausgabe) { print(); gibZeitAus(tage, std, min, sek, "- "); } m_zeit_in_sek -= (((tage * 24) + std) * 60 + min) * 60 + sek; sek_in_zeit(); // um evtl. ausserhalb liegende Std, Min, Sek zu korrigieren if (ausgabe) print(); } void CZeitRechner::sub(CZeitRechner& zeit, bool ausgabe) { sub(zeit.getTage(), zeit.getStunden(), zeit.getMinuten(), zeit.getSekunden(), ausgabe); } void CZeitRechner::mult(double faktor, bool ausgabe) { if (ausgabe) { print(); cout << "....multipl. mit " << faktor << ":" << endl; } m_zeit_in_sek = int((m_zeit_in_sek*faktor)+0.5);

36

2.3 Klassen und Objekte

37

sek_in_zeit(); // um evtl. ausserhalb liegende Std, Min, Sek zu korrigieren if (ausgabe) print(); } void CZeitRechner::print(void) { cout << setw(20) << m_name << ": " << setw(4) << setw(4) << m_tage << " Tage, " << setw(4) << m_std << " Std, " << m_min << " Min, " << setw(4) << m_sek << " Sek "

<< " = " << setw(10) << m_zeit_in_sek << " Sek" << endl; } void CZeitRechner::sek_in_zeit(void) { int sek = m_zeit_in_sek; m_tage = sek / m_tage_sek; m_std m_min m_sek } = sek / m_std_sek; = sek / 60; = sek % 60; sek %= m_tage_sek; sek %= m_std_sek;

2.3.1.8 Rechner fr sehr grosse Zahlen


Programm 2.9 bigadd.cpp: Implementierung der Klasse CZahl
#include "bigadd.h" //......................................................................... set void CZahl::set(char *wert) { int l = strlen(wert)-1;

for (int i=maxSt; i>=0; i--, l--) m_zahl[i] = (l>=0 && isdigit(wert[l])) ? wert[l]-0 : 0; if (wert[0] == -) // wenn negativer Wert, dann zweierKompl(); } //......................................................................... add void CZahl::add(CZahl zahl2) { int ueberLauf = 0; for (int i=maxSt; i>=0; i--) { int wert = m_zahl[i] + zahl2.m_zahl[i] + ueberLauf; m_zahl[i] = wert % 10; ueberLauf = wert / 10; } } //........................................................................ mult void CZahl::mult(CZahl zahl2) { int i, j; // Zweierkomplement bilden

CZahl ergeb = "0", help1(*this), help2(zahl2); if (m_zahl[0] == 9) // wenn negativer Wert, dann

help1.zweierKompl(); // Zweierkomplement bilden if (help2.m_zahl[0] == 9) // wenn negativer Wert, dann help2.zweierKompl(); // Zweierkomplement bilden

37

38

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

for (i=0; i<=maxSt && help1.m_zahl[i] == 0; i++) ; // sucht erste relevante Ziffer // bildet Multiplikation durch Verfahren

for (j=i; j<=maxSt; j++) {

unsigned zaehler = help1.m_zahl[j]; // nach, wie Mensch multipliziert CZahl zwischenZahl("0"); while (zaehler > 0) { zwischenZahl.add(help2); zaehler--; } ergeb.schiebeLinks(); ergeb.add(zwischenZahl); } if (m_zahl[0] != zahl2.m_zahl[0]) ergeb.zweierKompl(); *this = ergeb; }

//....................................................................... print void CZahl::print(void) { int i, j, z=0;

CZahl help(*this); if (m_zahl[0] == 9) { help.zweierKompl(); cout << "-"; } for (i=0; i<=maxSt && help.m_zahl[i] == 0; i++) ; // sucht erste relevante Ziffer // wenn Wert 0, dann explz. Ausgabe // wenn negativer Wert, dann // dann positiver Wert fr Ausgabe

if (i>maxSt)

cout << "0"; for (j=i; j<=maxSt; j++) { cout << (int)help.m_zahl[j]; if (++z%68==0) cout << "\\" << endl; } } //................................................................. zweierKompl CZahl& CZahl::zweierKompl(void) { for (int i=0; i<=maxSt; i++) // Einerkomplement durch Differenz m_zahl[i] = 9 - m_zahl[i]; // der Ziffern zu 9

add("1"); // Zweierkomplement = Einerkomplement + 1 return *this; } //................................................................ schiebeLinks void CZahl::schiebeLinks(void) { for (int i=0; i<=maxSt-1; i++) m_zahl[i] = m_zahl[i+1]; m_zahl[maxSt] = 0; }

38

2.3 Klassen und Objekte

39

2.3.2 Klassen und Objekte in UML


2.3.2.1 Klassen- und Objektdiagramm zu Monopoly Abbildung 2.4 zeigt ein einfaches Klassen-/Objektdiagramm zur Klasse Strasse eines Monopoly-Spiels.
schlossallee Strasse m_Name: String m_Kaufpreis: int m_Mietpreis: int m_AnzahlHaeuser: int = 0 + getName(): String + getKaufpreis(): int + getMietpreis(): int + getHaeuserzahl(int zahl): bool + addneueHaeuser(int zahl): bool + verkauft(): bool + kaufen(): bool

<<

nc insta

e of>

>

m_Name = "Schlossallee" m_Kaufpreis = 4000 m_Mietpreis = 500 m_AnzahlHaeuser = 2

<<

ins

tan

ce

of>

>

museumstrasse m_Name = "Museumstrasse" m_Kaufpreis = 2200 m_Mietpreis = 180 m_AnzahlHaeuser = 0

Alternative Darstellung der ObjektKlassenbeziehung:


schlossallee:Strasse m_Name = "Schlossallee" m_Kaufpreis = 4000 m_Mietpreis = 500 m_AnzahlHaeuser = 2 museumstrasse:Strasse m_Name = "Museumstrasse" m_Kaufpreis = 2200 m_Mietpreis = 180 m_AnzahlHaeuser = 0

Abbildung 2.4: Klassen-/Objektdiagramm zur Klasse Strasse eines Monopoly-Spiels

2.3.2.2 Realisierung einer Queue (Warteschlange) Abbildung 2.5 zeigt das Klassendiagramm zur Klasse CQueue. und Abbildung 2.6 das Zustandsdiagramm zur Klasse CQueue.
CQueue
m_entries[0..m_iMax]: char* m_iMax: int=0 m_iCount: int=0 {0 <= m_iCount <= m_iMax} m_iFirst: int=0 {0 <= m_iFirst <= m_iMax} m_iLast: int=0 {0 <= m_iLast <= m_iMax}

+ isEmpty(): bool + isFull(): bool + Put(name: char*): bool + Get(name: char*, maxLen: int): bool + Contents()
Abbildung 2.5: Klassendiagramm zur Klasse CQueue

39

40

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)


Put() [m_iCount < m_iMax]

Get() [m_iCount > 0] Put()

leer

Get() [m_iCount=0]

fllend

Put() [m_iCount = m_iMax]

Get() [m_iCount = m_iMax]

voll
Abbildung 2.6: Zustandsdiagramm zur Klasse CQueue

Programm 2.10 queue.cpp: Implementierung einer Queue


#include <iostream> #include <iomanip> #include <cctype> #include <cstring> #include "queue.h"

using namespace std;

//................................................. Konstruktor ........... CQueue::CQueue(int iMaxEntries) { m_iLast = m_iMax = m_iFirst = m_iCount = 0;

m_entries = new char*[iMaxEntries+1]; m_iMax = iMaxEntries; for (int i=0; i<m_iMax; i++) m_entries[i] = 0; }

//................................................. Konstruktor ........... CQueue::~CQueue() { for (int i=0; i<m_iMax; i++) if (m_entries[i]) delete [] m_entries[i]; delete [] m_entries; }

40

2.3 Klassen und Objekte

41

//......................................................... Put ........... bool CQueue::Put(char *name) { if (isFull()) return false; m_entries[m_iLast] = new char[strlen(name)+1]; strcpy(m_entries[m_iLast], name);

if (m_iLast < m_iMax) m_iLast++; else if (m_iLast == m_iMax) m_iLast=0; m_iCount++; return true; } //......................................................... Get ........... bool CQueue::Get(char *name, int maxLen) { if (isEmpty()) return false; strncpy(name, m_entries[m_iFirst], maxLen); name[maxLen-1] = 0; // 0-Byte am Ende sicherstellen

delete m_entries[m_iFirst]; // Name aus Queue lschen m_entries[m_iFirst] = 0;

if (m_iFirst < m_iMax) m_iFirst++; else if (m_iFirst == m_iMax) m_iFirst=0; m_iCount--; return true; } //.................................................... Contents ........... void CQueue::Contents() { int i, j;

if (isEmpty()) cout << "...... Wartezimmer ist leer ...." << endl; else if (m_iLast > m_iFirst) { for (i=m_iFirst, j=1; i<m_iLast; i++, j++) cout << setw(2) << j << ". " << m_entries[i] << endl; } else { for (i=m_iFirst, j=1; i<=m_iMax; i++, j++) cout << setw(2) << j << ". " << m_entries[i] << endl; for (i=0; i<m_iLast; i++, j++) cout << setw(2) << j << ". " << m_entries[i] << endl; } }

41

42

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3.3 Kopieren und Zuweisen von Objekten


2.3.3.1 Kopieren und Zuweisen von Personendaten
Programm 2.11 personcopy.cpp: Die Klasse CPerson
class CPerson { public: CPerson(const char *name = 0, unsigned alter = 0) { if ( name != 0 && (m_name = new char[strlen(name)+1]) ) strcpy(m_name, name); else m_name = 0; m_alter = alter; }

CPerson(const CPerson& copyObj) { m_name = 0;

//................. Kopierkonstruktor

*this = copyObj; // Aufruf des Zuweisungsoperators }

CPerson& operator= (const CPerson& rechts) { //i..... Zuweisungsoperator m_alter = rechts.m_alter; if (m_name) delete[] m_name; if (rechts.m_name) { if ( (m_name = new char[strlen(rechts.m_name)+1]) ) strcpy(m_name, rechts.m_name); } else m_name = 0; return *this; } void aendereName(const char *name) { if (m_name) delete[] m_name; if ( (m_name = new char[strlen(name)+1]) ) strcpy(m_name, name); } void aendereAlter(unsigned alter) { m_alter = alter; }

void print() { cout << m_name << " (" << m_alter << ")" << endl; }

private: char unsigned }; *m_name; m_alter;

42

2.3 Klassen und Objekte

43

2.3.3.2 Kopieren und Zuweisen von Kontos


Programm 2.12 kontocopy.cpp: Die Klasse CKonto
class CKonto { public: CKonto(const char *nam=0, unsigned n=0, double stand=0) { //.... Konstruktor copy(nam, n, stand); } CKonto(const CKonto& k) { copy(k.m_name, k.m_nr, k.m_stand); } // Copy-Konstr. CKonto& operator=(const CKonto& rechts) { //... Zuweisungsoperator if (this != &rechts) { destroy(); copy(rechts.m_name, rechts.m_nr, rechts.m_stand); } return *this; } ~CKonto() { destroy(); } //... Destruktor void aendere(double betrag=0, unsigned nr=0, const char *neuName=0) { if (neuName != 0) { if (m_name) delete [] m_name; if ( (m_name = new char[strlen(neuName)+1]) ) strcpy(m_name, neuName); else m_name = 0; } if (nr) m_nr = nr; if (betrag) m_stand += betrag; } void print(void) { cout << m_name << " (" << m_nr << "): " << m_stand <<endl; } private: char *m_name; unsigned m_nr; double m_stand;

void copy(const char *nam, unsigned n, double stand) { m_nr = n; m_stand = stand; if (nam != 0 && (m_name=new char[strlen(nam)+1])) strcpy(m_name, nam); else m_name = 0; } void destroy(void) { if (m_name) delete [] m_name; } };

43

44

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3.3.3 Realisierung einer Klasse CDoubleArray Abbildung 2.7 zeigt das Klassendiagramm zur Klasse CDoubleArray.

CDoubleArray
m_max: int m_array[0..m_max1]: double

+ Set(index: int, value: double): bool + Get(index: int, value: &double): bool + GetLen(): int
Abbildung 2.7: Klassendiagramm zur Klasse CDoubleArray

Programm 2.13 doublearray.cpp: Implementierung und Test der Klasse CDoubleArray


#include #include #include <iostream> <iomanip> <cstring>

using namespace std;

//=========================================================== class CDoubleArray { public: CDoubleArray(int max, const char* name) { cout << ".............Konstr. fr " << name << "(" << max << ")" << endl; m_array = new double[max]; m_max = m_array ? max : 0; strcpy(m_name, name); } bool Set(int i, double value) { if (!m_array || i >= m_max || i < 0) return false; m_array[i] = value; return true; } bool Get(int i, double& value) { if (!m_array || i >= m_max || i < 0) return false; value = m_array[i]; return true; } int GetLen() { return m_max; }

44

2.3 Klassen und Objekte

45

//.......... Kopierkonstruktor & Zuweisungsoperator CDoubleArray(const CDoubleArray& array) { strcpy(m_name, "noName"); cout << ".............kopiere: " << m_name << "<---" <<array.m_name <<endl; m_array = NULL; m_max *this } CDoubleArray& operator=(const CDoubleArray& array) { if (m_array) // bisher in Anspruch genommenen Speicher freigeben = 0; = array; // Aufruf des Zuweisungsoperators

delete[] m_array; if (array.m_max) { // m_array ist nicht leer // Neuen Speicher (m_max) allozieren und m_array kopieren if ( (m_array = new double[array.m_max]) ) { for(int i=0; i < array.m_max; i++) m_array[i] = array.m_array[i]; m_max = array.m_max; } else m_max = 0; } else { // array ist leeres Arrayobjekt m_array = 0; m_max } cout << "............." << m_name << "(" << m_max << ") = " = 0;

<< array.m_name << "(" << array.m_max << ")" << endl; return *this; } private: double *m_array; int m_max; char }; //=========================================================== int main(void) { cout << endl << "_________ " << "CDoubleArray reihe(5, eihe << endl; r )" CDoubleArray reihe(5, "reihe"); double wert, i; // Ergebnis entspricht Wert des linken Operanden, // der durch aktuelles Objekt reprsentiert wird.

// Grsse des Arrays (maximaler Index = m_max-1)

m_name[100]; // Name des jeweiligen Arrays

// Reihenwerte in Array ablegen for (i=0; i < reihe.GetLen(); i++) if (!reihe.Set(int(i), 1/(i+1))) cerr << "Fehler: sollte eigenlich nicht mglich sein" << endl; // Versuch, an unerlaubte Speicherstellen etwas schreiben: // Fehlermeldungen sollten ausgegeben werden.

if (!reihe.Set(-50, 3.1415)) cerr << "kann nicht an Index -50 schreiben" << endl; if (!reihe.Set(10, 1.2345)) cerr << "kann nicht an Index 10 schreiben" << endl;

45

46

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

// Ausgabe der Reihe-Zahlen (bewusst Grenzen berschreiten) cout << " reihe:" << endl;

for (i=-1; i<=reihe.GetLen(); i++) if (reihe.Get(int(i), wert)) cout << setw(7) << int(i) << ": " << setprecision(2) << setiosflags(ios::fixed) << wert << endl; else cerr << setw(7) << int(i) << ": Unerlaubter Index" << endl;

// Test auf Objektkopien // ====================== cout << endl << "_________ " << "CDoubleArray reiheCopy = reihe" << endl; CDoubleArray reiheCopy = reihe; // Kopierkonstruktor

if (!reiheCopy.Set(3, 12345.67)) cerr << "reiheCopy: kann nicht an Index 3 schreiben" << endl; for (i=0; i < reiheCopy.GetLen(); i++) if (reiheCopy.Get(int(i), wert)) cout << setw(7) << int(i) << ": " << setprecision(2) << wert << endl; else cerr << setw(7) << int(i) << ": Unerlaubter Index" << endl; cout << endl << "_________ " << "CDoubleArray reiheZuweis(2, eiheZuweis r )" << endl; CDoubleArray reiheZuweis(2, "reiheZuweis"); // Zuweisungsoperator // (bewusst weniger Eintrge)

cout << endl << "_________ " << "reiheZuweis = reihe" << endl; reiheZuweis = reihe;

cout << "

reiheZuweis:" << endl;

for (i=0; i < reiheZuweis.GetLen(); i++) if (reiheZuweis.Get(int(i), wert)) cout << setw(7) << int(i) << ": " << setprecision(2) << wert << endl; else cerr << setw(7) << int(i) << ": Unerlaubter Index" << endl;

cout << "

reihe:" << endl;

for (i=0; i < reihe.GetLen(); i++) if (reihe.Get(int(i), wert)) cout << setw(7) << int(i) << ": " << setprecision(2) << wert << endl; else cerr << setw(7) << int(i) << ": Unerlaubter Index" << endl; }

46

2.3 Klassen und Objekte

47

Programm 2.13 (doublearray.cpp) liefert die folgende Ausgabe:


_________ CDoubleArray reihe(5, "reihe") .............Konstr. fr reihe(5) kann nicht an Index -50 schreiben kann nicht an Index 10 schreiben reihe: -1: Unerlaubter Index 0: 1.00 1: 0.50 2: 0.33 3: 0.25 4: 0.20 5: Unerlaubter Index

_________ CDoubleArray reiheCopy = reihe .............kopiere: noName<---reihe .............noName(5) = reihe(5) 0: 1.00 1: 0.50 2: 0.33 3: 12345.67 4: 0.20

_________ CDoubleArray reiheZuweis(2, "reiheZuweis") .............Konstr. fr reiheZuweis(2)

_________ reiheZuweis = reihe .............reiheZuweis(5) = reihe(5) reiheZuweis: 0: 1.00 1: 0.50 2: 0.33 3: 0.25 4: 0.20 reihe: 0: 1.00 1: 0.50 2: 0.33 3: 0.25 4: 0.20

47

48

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3.3.4 Konstruktoren, Destruktoren, Kopierkonstruktor und Zuweisungsoperator


Programm 2.14 klasse.cpp: Die Implementierung der Klasse CKlasse
#include #include <iostream> <cstring>

using namespace std; class CKlasse { public: CKlasse(char* name) { //........................................ Konstruktor cout << "Konstruktor CKlasse(" << name << ")" << endl << endl; if ( (m_name = new char[strlen(name)+1]) ) strcpy(m_name, name); } ~CKlasse() { //.................................................. Destruktor cout << "Destruktor CKlasse(" << m_name << ")" << endl; if (m_name) { // Speicherplatz fr m_name wurde alloziert delete[] m_name; m_name = NULL; } } CKlasse(const CKlasse& k) { //............................ Kopierkonstruktor cout << "Kopierkonstruktor: Klasse(" << k.m_name << ")" << endl; m_name = NULL; // Membervariablen initialisieren, was Konstr.-Aufgabe ist

*this = k; // Aufruf des Zuweisungsoperators } CKlasse& operator=(const CKlasse& k) { //................ Zuweisungsoperator if (m_name) delete[] m_name; if (k.m_name) { // k ist nicht leer

if ( (m_name = new char[strlen(k.m_name)+1]) ) strcpy(m_name, k.m_name); } else m_name = NULL; // k ist ein leeres Stringobjekt

cout << "Zuweisungsoperator: x = " << k.m_name << endl << endl; return *this; // Ergebnis entspricht Wert des linken Operanden, // der durch das aktuelle Objekt reprsentiert wird } void setName(char *name) { if (m_name) { // Speicherplatz fr m_name wurde alloziert delete[] m_name; if ( (m_name = new char[strlen(name)+1] )) strcpy(m_name, name); } } private: char *m_name; }; int main(void) /* .... */

48

2.3 Klassen und Objekte

49

2.3.3.5 Kopierkonstruktor und Zuweisungsoperator Programm copyzuwkonstr.cpp liefert die folgende Ausgabe:
1: 42 Standard-Konstruktor 2: 42 Kopier-Konstruktor 3: 42 Kopier-Konstruktor .....vor Aufruf von func() 4: 42 Kopier-Konstruktor .....am Anfang von func() 5: 36 Standard-Konstruktor 6: 42 Zuweisungsoperator .....am Ende von func() 7: 42 Zuweisungsoperator 6: 42 Destruktor 4: 42 Destruktor .....nach Aufruf von func() 3: 42 Destruktor 2: 42 Destruktor 7: 42 Destruktor | | | (fr b3 aus Zeile 44) (fr b2 aus Zeile 43) (fr b1 aus Zeile 42 bzw. Zeile 47) | | | Zeile 47: Zeile 47: Zeile 47: b1 = func(b2) (fr lokales b) (fr temporre Kopie von b2] | | Zeile 36: Zeile 37: CZahl b(__LINE__); b = z; | Zeile 47: func(b2) [temporre Kopie von b2] | | | Zeile 42: Zeile 43: Zeile 44: CZahl b1(__LINE__); CZahl b2 = b1; CZahl b3(b1);

2.3.4 Konstante Memberfunktionen und Objekte


2.3.4.1 Eine Kreis-Klasse
Programm 2.15 kreisconst.cpp: Implementierung zur Klasse Bitmap fr Bitoperationen
......... // siehe Programm 2.1 (auf Seite 22 double getRadius(void) const { return radius; double getUmfang(void) const { return umfang; } }

double getFlaeche(void) const { return flaeche; }

void print(void) const { cout << setiosflags(ios::left) << "r=" << setw(8) << radius

<< "f=" << setw(10) << flaeche << "u=" << setw(10) << umfang << endl; } ......... // siehe Programm 2.1 (auf Seite 22

49

50

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3.4.2 Eine Klasse fr Bitoperationen


Programm 2.16 bitmap.cpp: Implementierung zur Klasse Bitmap fr Bitoperationen
#include #include #include <iostream> <iomanip> "bitmap.h"

using namespace std;

Bitmap::Bitmap() { m_maxBits = sizeof(m_bitWort) * 8; Zero(); } void Bitmap::SetBits(int bitstoset[], int num) { for (int i=0; i<num; i++) SetBit(bitstoset[i]); } void Bitmap::SetBit(int bitNr) { if ((bitNr < 0) || (bitNr >= m_maxBits)) return; m_bitWort |= 1 << bitNr; } void Bitmap::ClearBit(int bitNr) { if ((bitNr < 0) || (bitNr >= m_maxBits)) return; m_bitWort &= ~(1 << bitNr); } void Bitmap::SetAs(int bitNr, int setting) { if (setting == 0) ClearBit(bitNr); else SetBit(bitNr); } int Bitmap::TestBit(int bitNr) const { if ((bitNr < 0) || (bitNr >= m_maxBits)) return 0; return int(bool((m_bitWort & (1 << bitNr)))); } void Bitmap::FlipBit(int bitNr) { if ((bitNr < 0) || (bitNr >= m_maxBits)) return; m_bitWort ^= 1 << bitNr; } void Bitmap::Invert(void) { m_bitWort = ~m_bitWort; } Bitmap Bitmap::And(const Bitmap& other) const { Bitmap b; b.m_bitWort = m_bitWort & other.m_bitWort; return b;

50

2.3 Klassen und Objekte

51

} Bitmap Bitmap::Or(const Bitmap& other) const { Bitmap b; b.m_bitWort = m_bitWort | other.m_bitWort; return b; } Bitmap Bitmap::Xor(const Bitmap& other) const { Bitmap b; b.m_bitWort = m_bitWort ^ other.m_bitWort; return b; } Bitmap Bitmap::Not(void) const { Bitmap b; b.m_bitWort = ~m_bitWort; return b; } bool Bitmap::Equals(const Bitmap& other) const { return m_bitWort == other.m_bitWort; } int Bitmap::Count(void) const { int count = 0, j=1; for (int i=0; i<m_maxBits; i++) { if (m_bitWort & j) count++; j = j << 1; } return count; } void Bitmap::Print(const char *str) const { if (strlen(str) > 0) { int i, l = strlen(str); for (i=0; i <= 70-l; i++) cout << "."; cout << " " << str << endl << " |"; for (i=0; i<m_maxBits; i+=8) cout << " " << setiosflags(ios::left) << setw(8) << i << " |"; cout << endl; } cout << " | "; for (int i=0; i<m_maxBits; i++) { cout << ((m_bitWort >> i) & 1); if (i%8 == 7) cout << " | "; } cout << endl; }

51

52

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.3.5 Statische Klassenelemente und Objekte


2.3.5.1 Vergabe und Lschen von Kundennummern Die Eindeutigkeit der Kundennummer wird im einfachsten Falle ber einen Zhler gewhrleistet, der als Klassenattribut realisiert ist und bei jedem Anlegen inkrementiert wird. Beim Lschen des Kunden wird seine Nummer einfach auf 0 gesetzt. Dass dadurch Lcken entstehen, ist nur bei einem Kundenstamm relevant, der mehr als 232 1 Kunden umfasst. Enthlt die Generierung der Kundennummer mehr Intelligenz (z.B. Beinhaltung des Datums), so ist natrlich darber nachzudenken, ob die Generierung nicht aus der Klasse CKunde in eine eigene Klasse ausgelagert wird. ber die Methode GetKdnr() kann ermittelt werden, ob es sich bei dem vorliegenden Objekt noch um einen gltigen Kunden handelt (kdnr != 0). Abbildung 2.8 zeigt das Klassendiagramm zur Klasse CKunde und Programm 2.17 die Realisierung der Klasse CKunde. Im Klassendiagramm von Abbildung 2.8 ist m_lfnr unterstrichen, weil es sich hier um ein Klassenattribut handelt. Dass es sich bei der Methode GetNaechstNr() um eine statische Memberfunktion handelt, kann man ebenfalls daran erkennen, dass sie unterstrichen ist.
CKunde
m_kdnr: int=0 m_lfnr: unsigned long=0 Anlegen(): void Loeschen(): void GetKdnr(): unsigned long GetNaechstNr(): unsigned long

Abbildung 2.8: Klassendiagramm zur Klasse CKunde

Programm 2.17 kunde.cpp: Die Klasse CKunde mit Klassenattributen


class CKunde { public: CKunde() void Loeschen() void Anlegen() { m_kdnr = 0; { m_kdnr = 0; } }

{ m_kdnr = ++m_lfnr; } }

unsigned long GetKdnr() { return m_kdnr;

unsigned long static GetNaechstNr() private: unsigned long m_kdnr;

return m_lfnr+1;

// stat. Memberfunk.

static unsigned long m_lfnr; // statisches Datum }; unsigned long CKunde::m_lfnr=0; // Initialisieren des statisches Datum int main(void) { ... }

52

2.3 Klassen und Objekte

53

2.3.5.2 Falsches Zhlen von Objekt-Instanzen Das Problem in Programm statcount.cpp ist, dass beim Anlegen von obj2 zweimal der Kopierkonstruktor aufgerufen wird: einmal fr bergebenes CZaehlArgument und ein weiteres mal, um das Objekt obj2 mit dem Rckgabewert von fkt() zu initialisieren. Da kein eigener Kopierkonstruktor vorhanden ist, wird der Standard-Kopierkonstruktor aufgerufen, und dieser zhlt natrlich nicht die statische Membervariable obj_zaehler hoch. Um dieses Problem zu beheben, msste man einen eigenen Kopierkonstruktor zur Verfgung stellen, wie es der folgende Ausschnitt aus Programm statcount2.cpp zeigt:
9 CZaehl(const CZaehl& h) { obj_zaehler++; print("Kopierkonstruktor"); }

Dann wrde sich das Programm richtig verhalten, wie die folgende Ausgabe zeigt:
Konstruktor: nach Anlegen von obj1: Kopierkonstruktor: Argument x innerhalb von fkt(): Kopierkonstruktor: Destruktor : nach fkt()-Aufruf: Destruktor : Destruktor : 1 1 2 2 3 2 2 1 0

2.3.5.3 Auto im Parkhaus sucht sich freien Platz


Programm 2.18 parkhaus.cpp: Die Klasse CAuto_in_Parkhaus mit Klassenattributen
class CAuto_in_Parkhaus { public: CAuto_in_Parkhaus(int id) { int p; while (s_besetzt[p=rand()%PLAETZE] != 0) ; s_besetzt[p] = m_kennung = id; m_platz = p; } ~CAuto_in_Parkhaus() { s_besetzt[m_platz] = 0; } static int private: int m_kennung, m_platz; }; int CAuto_in_Parkhaus::s_besetzt[PLAETZE]; // ganzes Array wird mit 0 initial. s_besetzt[PLAETZE];

void belegungs_anzeige(int zeit) { cout << setw(2) << zeit << ":"; for (int i=0; i<PLAETZE; i++) cout << (CAuto_in_Parkhaus::s_besetzt[i] ? "x" : "."); } int main(void) { ... }

53

54

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.4

Mehrere Klassen bzw. Objekte im Zusammenspiel

2.4.1 Aufteilung in Klassen


2.4.1.1 Realisierung eines Bruchrechners Diese Aufgabenstellung soll mit zwei Klassen realisiert werden: Eine, die das Bruchrechnen bernimmt, und eine, welche die Ein- und Ausgabe der Brche bernimmt. Bei genauerer berlegung macht dies auch Sinn, denn das Rechnen mit den Brchen hat nichts mit der Ein- bzw. Ausgabe der Brche zu tun. Fr das Bruchrechner-Beispiel haben wir zwei Klassen vorgesehen: CBruch: Diese Klasse ist fr die Speicherung des aktuellen Zhlers und Nenners verantwortlich und bietet ber ihre Methoden ihre Bruchrechen-Fhigkeiten an. CBruchIO: Diese Klasse ist fr die Ein- und Ausgabe eines Bruches zustndig. Diese Einfhrung einer eigenen Klasse CBruchIO fr die Ein- und Ausgabe hat den Vorteil, dass wir nur diese Klasse bzw. die Methoden dieser Klasse ndern mssen, wenn wir die Ein- und Ausgabe eines Bruches anders gestalten mchten, wie z. B. bei einer Umstellung der Ein- und Ausgabe von einer Textkonsole auf eine grasche Ein- und Ausgabe in speziellen Windows. Die restlichen Klassen des Programms (hier nur eine) knnen dabei unverndert bernommen werden. Programm 2.19 und Programm 2.20 zeigen die Schnittstelle und die Implementierung der Klasse CBruch.
Programm 2.19 bruch.h: Schnittstelle fr die Klasse CBruch
#ifndef BRUCH_H #define BRUCH_H //===================================================================== // Aufgabe von CBruch: Realisierung eines mathematischen Bruches

//---------------------------------------------------------------------class CBruch { public: CBruch(); CBruch(int z, int n); int int getZaehler(void) const { return m_Zaehler; } getNenner(void) const { return m_Nenner; }

void setBruch(int z,int n); void add(CBruch& b); void sub(CBruch& b); void mul(CBruch& b); void div(CBruch& b); void kuerzen(); private: int m_Nenner, m_Zaehler; int ggT(int n, int m) { return (m==0) ? n : ggT(m, n%m); } }; #endif

54

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

55

Programm 2.20 bruch.cpp: Implementierung der Klasse CBruch


#include "bruch.h" { setBruch(0, 1); }

CBruch::CBruch()

CBruch::CBruch(int z, int n) { setBruch(z, n); } void CBruch::setBruch(int z, int n) { if (n > 0) { m_Zaehler = z; m_Nenner } } void CBruch::add(CBruch& b) { m_Zaehler = m_Zaehler * b.getNenner() + b.getZaehler() * m_Nenner; m_Nenner *= b.getNenner(); kuerzen(); } void CBruch::sub(CBruch& b) { // Subtraktion = Addition mit negativen Bruch b.setBruch(- b.getZaehler(), b.getNenner()); add(b); } void CBruch::mul(CBruch& b) { m_Nenner *= b.getNenner(); m_Zaehler *= b.getZaehler(); kuerzen(); } void CBruch::div(CBruch& b) { // Division = Multiplikation mit Kehrbruch m_Nenner *= b.getZaehler(); m_Zaehler*= b.getNenner(); kuerzen(); } void CBruch::kuerzen(void) { int ggTeiler = ggT(m_Zaehler, m_Nenner); = n;

m_Zaehler /= ggTeiler; m_Nenner } /= ggTeiler;

Programm 2.21 und Programm 2.22 zeigen die Schnittstelle und die Implementierung der Klasse CBruchIO.
Programm 2.21 bruchio.h: Schnittstelle fr die Klasse CBruchIO
#ifndef #define #include BRUCHIO_H BRUCHIO_H "bruch.h" Ein-/Ausgabe fr die Klasse CBruch

class CBruchIO { //... Aufgabe von CBruchIO: public: void read(CBruch& in) const; void print(CBruch& out) const; }; #endif

55

56

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.22 bruchio.cpp: Implementierung der Klasse CBruchIO


#include #include #include "bruch.h" "bruchio.h" <iostream>

using namespace std; void CBruchIO::read(CBruch& in) const { char strich; int z, n; //.......... Einlesen eines CBruch

// Dummy fr Bruch-Strich

cin >> z >> strich >> n; cin.ignore(100, \n); in.setBruch(z, n); } void CBruchIO::print(CBruch& out) const { //.......... Ausgeben eines CBruch cout << out.getZaehler() << " / " << out.getNenner(); }

Programm 2.23 bruchmain.cpp: Realisierung der main-Funktion


#include #include #include <iostream> "bruch.h" "bruchio.h"

using namespace std; int main(void) { CBruch char b1, b2; wahl;

CBruchIO bio; cout << " << " << " << " << " << " << " Bruchrechner mit folgenden Operationen" << endl ======================================" << endl + * / e : Addieren" << endl : Subtrahieren" << endl : Multiplizieren" << endl : Dividieren" << endl : Ende" << endl << endl;

cout << "Geben Sie Ihren ersten Bruch ein (wie z.B. 3/15): "; bio.read(b1); while (1) { cout << "Operation: "; cin >> wahl; cin.ignore(100, \n); if (wahl == e) break; cout << "Nchster Bruch: "; switch (wahl) { case +: case -: case *: case /: default: } cout << "-------------------" << endl << " = "; bio.read(b2); b1.add(b2); break; bio.read(b2); b1.sub(b2); break; bio.read(b2); b1.mul(b2); break; bio.read(b2); b1.div(b2); break; break;

bio.print(b1); cout << endl << "-------------------" << endl; } }

56

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

57

2.4.1.2 Realisierung eines Paralleladdierers


CParallelAdd 15 1 m_dezimal: unsigned long + add(zahl1: char[], zahl2: char[]) + print() 1

CVollAdd m_s: int m_u: int + exec(a: int, b: int, u: int) + getU(): int + getS(): int

CHalbAdd 1 m_s: int m_u: int + exec(a: int, b: int) + getU(): int + getS(): int

Abbildung 2.9: Klassendiagramm zum Paralleladdierer

Programm 2.24 paraadd.h: Deklaration der Klassen des Paralleladdierers


#include #include <iostream> <cstring>

using namespace std; const static int max_stellen=16; //... 16 Stellen class CHalbAdd { public: void exec(int a, int b) { m_s = a ^ b; m_u = a & b; } int int getS(void) const { return m_s; } getU(void) const { return m_u; }

private: int }; class CVollAdd { public: void exec(int a, int b, int u) { m_s = (a ^ b) ^ u; m_u = ((a ^ b) & u) | (a & b); } int int getS(void) const { return m_s; } getU(void) const { return m_u; } m_s, m_u;

private: int }; class CParallelAdd { public: void add(const char zahl1[], const char zahl2[]); void print(void) const; private: CHalbAdd CVollAdd unsigned long }; m_ha; // Halbaddierer m_s, m_u;

m_va[max_stellen-1]; // n-1 Volladdierer m_dezimal;

57

58

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.25 paraadd.cpp: Implementierung der Klassen des Paralleladdierers


#include "paraadd.h"

void CParallelAdd::add(const char zahl1[], const char zahl2[]) { char int dual1[max_stellen], dual2[max_stellen]; i, j, uebertrag;

memset(dual1, 0, max_stellen); // dual1 und dual2 auf 0 setzen memset(dual2, 0, max_stellen); // ............................

m_dezimal = strtoul(zahl1, NULL, 2) + strtoul(zahl2, NULL, 2); // Summe (dez.)

//... zahl1 und zahl2 umgekehrt und numerisch in dual1 und dual2 ablegen for (i=strlen(zahl1)-1, j=0; i>=0 && j<max_stellen; i--, j++) if ( (dual1[j] = zahl1[i]-0) != 0 && dual1[j] != 1) { cerr << "In 1. Zahl unerlaubte Dualziffer: " << zahl1[j] << endl; exit(1); } for (i=strlen(zahl2)-1, j=0; i>=0 && j<max_stellen; i--, j++) if ( (dual2[j] = zahl2[i]-0) != 0 && dual2[j] != 1) { cerr << "In 2. Zahl unerlaubte Dualziffer: " << zahl2[j] << endl; exit(1); }

//... letzten beiden Ziffern der Zahlen mit Halbaddierer addieren lassen m_ha.exec(dual1[0], dual2[0]); uebertrag = m_ha.getU(); // bertrag erfragen

//... restlichen Ziffern der Zahlen mit Volladdierern addieren lassen for (i=1; i<max_stellen; i++) { m_va[i-1].exec(dual1[i], dual2[i], uebertrag); uebertrag = m_va[i-1].getU(); } }

void CParallelAdd::print(void) const { //... Summenziffern von den einzelnen Volladdierern erfragen und ausgeben for (int i=max_stellen-2; i >= 0; i--) cout << m_va[i].getS(); cout << m_ha.getS(); //... Summenziffer vom Halbaddierer erfragen und ausgeben cout << " = " << m_dezimal << " (10)"<< endl; }

58

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

59

2.4.1.3 Gemeinsamer Termin


Programm 2.26 intarray.cpp: Implementierung der Klasse CIntArray
#include "intarray.h" CIntArray::CIntArray(int max) //.................................... Konstruktor { m_array = new int[max]; m_max = m_array ? max : 0; } CIntArray::CIntArray(const CIntArray& array) //............... Kopierkonstruktor { m_array = 0; m_max = 0; // Aufruf des Zuweisungsoperators

*this = array; }

CIntArray& CIntArray::operator=(const CIntArray& array) //... Zuweisungsoperator { if (m_array) delete[] m_array; if (array.m_max && (m_array = new int[array.m_max]) ) { for (int i=0; i < array.m_max; i++) m_array[i] = array.m_array[i]; m_max = array.m_max; } else { m_array = 0; m_max = 0; } return *this; } bool CIntArray::set(int i, int value) //.................................... set { if (!m_array || i >= m_max || i < 0) return false; m_array[i] = value; return true; } bool CIntArray::get(int i, int& value) //................................... get { if (!m_array || i >= m_max || i < 0) return false; value = m_array[i]; return true; }

59

60

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.27 terminfindermain.cpp: main()-Funktion zum Finden eines gemeinsamen Termins


#include #include #include <iostream> "intarray.h" "terminfinder.h"

using namespace std; enum werktage { MO, DI, MI, DO, FR };

char* tagStr[5] = {"MO","DI","MI","DO","FR"}; const int max_tage = sizeof(tagStr) / sizeof(*tagStr); void auswertung(char* str, CTerminFinder& find) { CIntArray m_best = find.getBestTermine(); cout << "Beste Termine fr " << str << ": "; int tag; for (int i=0; i < m_best.getLen(); i++) if (m_best.get(i,tag)) cout << tagStr[tag] << " "; cout << endl; } int main(void) { CTerminFinder kegeln(max_tage), skat(max_tage); kegeln.streichen(MO); // Hugo ist Kegler und Skat-Spieler kegeln.streichen(MI); // und kann Montags und Mittwochs nie skat.streichen(MO); skat.streichen(MI); kegeln.streichen(DI); // Fritz ist nur Kegler und kann Dienstags nicht skat.streichen(DI); // Anna spielt nur Skat und kann am skat.streichen(MI); // Dienstag und Mittwoch nicht skat.freigeben(DI); // ... Anna kann nun doch Dienstags auswertung("Kegeln", kegeln); // Auswertung auswertung("Skat", skat); }

Programm 2.28 terminfinder.h: Die Klasse CTerminFinder


#include "intarray.h"

class CTerminFinder { public: CTerminFinder(int anzahl); ~CTerminFinder() { if (m_termin) delete[] m_termin; } bool streichen(int dayNr); bool freigeben(int dayNr); CIntArray getBestTermine(); private: int *m_termin; int m_anzahl; }; // moegliche Termine // Anzahl moegl. Termine

60

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

61

Programm 2.29 terminfinder.cpp: Implementierung der Klasse CTerminFinder


#include <iostream> #include "terminfinder.h" CTerminFinder::CTerminFinder(int anzahl) { m_termin = new int[anzahl]; m_anzahl = m_termin ? anzahl : 0; for (int i=0; i < m_anzahl; i++) m_termin[i]=0; } bool CTerminFinder::streichen(int dayNr) { if (m_termin && dayNr < m_anzahl) { m_termin[dayNr]--; return true; } return false; } bool CTerminFinder::freigeben(int dayNr) { if (m_termin && dayNr < m_anzahl) { m_termin[dayNr]++; return true; } return false; } CIntArray CTerminFinder::getBestTermine() { int i, max = m_termin[0],

index = 0, cnt = 1;

for (i=1; i < m_anzahl; i++) // max. Wert finden und zhlen wie viele davon if (m_termin[i] > max) { max = m_termin[i]; index=i; cnt=1; } else if (m_termin[i]==max) cnt++; // die besten Termine in Array mit CIntArray bestTermine(cnt); // passender Grsse kopieren cnt = 0; for (i=index; i < m_anzahl; i++) if (m_termin[i] == max) bestTermine.set(cnt++, i); return bestTermine; }

61

62
2.4.1.4 Objektkopien

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.30 person2.h: Die Klasse CPerson mit char*-Attribut (Schnittstelle)


#include <iostream> using namespace std; //============================================================================== // Aufgabe von CPerson: Reprsentation einer Person, die Anliegen vortragen kann //-----------------------------------------------------------------------------class CPerson public: CPerson(char *anliegen = 0); CPerson& CPerson::operator=(const CPerson& p); //...... Zuweisungsoperator CPerson(const CPerson& p) { //.......................... Kopierkonstruktor m_Anliegen = 0; *this = p; } void anliegenVortragen() { cout << m_Anliegen << endl; } private: char *m_Anliegen; }; {

Programm 2.31 person2.cpp: Die Klasse CPerson mit char*-Attribut (Implementierung)


#include #include <iostream> "person2.h"

char *begleit = "Ich bin nur zum Hndchen halten mit gekommen "; CPerson::CPerson(char *anliegen) { if (!anliegen) anliegen = begleit; m_Anliegen = new char[strlen(anliegen) + 1]; if (m_Anliegen) strcpy(m_Anliegen, anliegen); } CPerson& CPerson::operator=(const CPerson& p) { //.. Zuweisungsoperator if (m_Anliegen) delete[] m_Anliegen; if (p.m_Anliegen) m_Anliegen = new char[strlen(p.m_Anliegen) + 1]; else m_Anliegen = new char[strlen(begleit) + 1]; if (m_Anliegen) strcpy(m_Anliegen, p.m_Anliegen ? p.m_Anliegen : begleit); else { cerr << "Speichermangel" << endl; exit(1); } return *this; }

62

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

63

2.4.1.5 Sequenzdiagramme zum Monopoly-Spiel


Hans:Spieler betreten() zahleMiete(100, besitzer) ueberweise(100) Hafenstrasse:Strasse besitzer:Spieler

Abbildung 2.10: Betreten einer Strasse, zu der Miete zu zahlen ist

Hans:Spieler

:Spielfeld

Hafenstrasse:Strasse

zahl := wuefeln()

*[zahl]vorruecken()

betreten()

[noch nicht verkauft] kaufen()

Abbildung 2.11: Wrfeln, Vorrcken und Kaufen einer Strasse

Hans:Spieler betreten()

:Gemeinschaftsfeld

:Kartenstapel

:Karte

:Bank

karte := holeKarte()

werteAus() zahle(Hans,200) ueberweise(200)

Abbildung 2.12: Betreten des Gemeinschaftsfelds mit Gewinnkarte

63

64

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.4.1.6 Kollaborationsdiagramme zum Monopoly-Spiel


betreteFeld(Hans:Spieler) 3.1: zeigeKontoStand() 3.3: zeigeKontoStand()

Westbahnhof:Bahnhof

<<parameter>> 3: [Hans nicht Besitzer] zahle(miete, besitzer)

Hans:Spieler

2: miete := ermittleMiete(zahl) 3.2: gutschrift(miete)

1: zahl := ermittleBahnhofZahl()

<<parameter>> besitzer :Spieler <<parameter>> besitzer

3.2.1: zeigeKontoStand

Abbildung 2.13: Betreten des Westbahnhofs mit Zahlen von Miete

verkaufen()

Verkaeufer:Spieler 2: create(mindestPreis) 3: destroy() :Spieler

1: verkaufsobjekt := selektiereImmobilie() 2.3.1: entferne(verkaufsobjekt)

2.3: [gebot >=mindestPreis] verkauft(verkaufsobjekt,gebot) :Immobilie <<local>> :Auktion {transient} 2.1.*: gebot := macheGebot()

2.2: [gebot>=mindestPreis, hoechstGebot] gekauft(verkaufsobjekt,gebot) <<parameter>> verkaufsobjekt:Strasse <<parameter>> Kaeufer:Spieler

2.2.1: fgeHinzu(verkaufsobjekt) :Immobilie

Abbildung 2.14: Auktion zum Verkaufen einer Strasse

64

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

65

2.4.2 Initialisierung von Membervariablen


2.4.2.1 Das dHondtsche Hchstzhlverfahren
Programm 2.32 dhondt.cpp: Das dHondtsche Hchstzhlverfahren
#include <iostream> #include <iomanip> #include <cctype> using namespace std;

char *partei[] = { "ZTU", "SPT", "FTB", "Greens", "PTS" }; const int parteiZahl = sizeof(partei)/sizeof(partei[0]); //--------------------------------------------------------------- Klasse CPartei class CPartei { public: CPartei(char *name, float stimmen) : m_name(new char[strlen(name)+1]), m_stimmen(stimmen), m_geteiltStim(stimmen), m_sitze(0), m_teiler(1) { strcpy(m_name, name); } char *getName(void) float getGesamtStim(void) { return m_name; { return m_stimmen; } } }

float getGeteiltStim(void) { return m_geteiltStim; void int private: char *m_name; float m_stimmen, m_geteiltStim, m_teiler; int }; //--------------------------------------------------------------- Klasse CdHondt class CdHondt { public: CdHondt(int sitzZahl) : m_gesamtSitze(sitzZahl), m_gesamtStimmen(0) { float stimmen; cout << "Geben Sie die abgegebenen Stimmen pro Partei ein" << endl; for (int i=0; i<parteiZahl; i++) { cout << partei[i] << ": "; cin >> stimmen; cin.ignore(100, \n); m_partei[i] = new CPartei(partei[i], stimmen); m_gesamtStimmen += stimmen; } } m_sitze; setSitz(void) getSitze(void)

{ m_sitze++; m_geteiltStim = m_stimmen/++m_teiler; } { return m_sitze; }

65

66

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

void berechne(void) { char zeich; cout << endl << "Soll jede einzelne Sitzzuteilung ausgegeben werden (j/n) ? "; cin.get(zeich); cin.ignore(100, \n); bool detailAusgabe = tolower(zeich) == j; cout << endl; int vergebeneSitze = 0; while (vergebeneSitze < m_gesamtSitze) { int max = 0; for (int i=1; i<parteiZahl; i++) if (m_partei[i]->getGeteiltStim() > m_partei[max]->getGeteiltStim()) max = i; ++vergebeneSitze; if (detailAusgabe) cout << endl << "...Sitz " << vergebeneSitze << " geht an: " << m_partei[max]->getName() << " (" << setprecision(0) << setiosflags(ios::fixed) << m_partei[max]->getGeteiltStim() << ")"; m_partei[max]->setSitz(); } } void gibAus(void) { cout << endl << endl << "Sitzverteilung:" << endl << endl << setw(13) << "Partei | " << setw(8) << "Sitze | " << setw(23) << "Prozent (Sitze) | " << setw(22) << "Prozent (Stimmen) |" << endl << "-----------+-------+----------------------+" "----------------------|" << endl; for (int i=0; i<parteiZahl; i++) cout << setw(10) << m_partei[i]->getName() << setw(5) << " | "

<< m_partei[i]->getSitze() << " | "

<< setw(20) << setprecision(2) << (float)m_partei[i]->getSitze()/(float)m_gesamtSitze*100 << " | " << setw(20) << setprecision(2) << m_partei[i]->getGesamtStim()/m_gesamtStimmen*100 <<" | " <<endl; } private: CPartei *m_partei[parteiZahl]; int m_gesamtSitze; float }; int main(void) { int sitz_zahl; m_gesamtStimmen;

cout << "Sitzverteilung nach dHondt" << endl << "===========================" << endl << endl << endl << "Wieviele Sitze sind zu vergeben: "; CdHondt sitzVerteilung(sitz_zahl); sitzVerteilung.berechne(); sitzVerteilung.gibAus(); } cin >> sitz_zahl;

66

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

67

2.4.2.2 Kstners Kubikkilometer fr alle Menschen


Programm 2.33 kaestner.cpp: Kstners Kubikkilometer fr alle Menschen
#include #include <iostream> <iomanip>

using namespace std;

class CBerech { public: CBerech(float l, float b, float t) : kubik_km_in_cm(1e3 * 1e3 * 1e3 * 1e6), mrd(1e9), mensch_volumen(l*b*t), mensch_zahl(kubik_km_in_cm / mensch_volumen) { ..... } ..... }; .....

2.4.2.3 Geldscheine stapeln Die Initialisierung der in einer Initialisierungsliste angegebenen Membervariablen in der Reihenfolge statt, in der sie innerhalb der Klasse deklariert sind, und nicht in der Reihenfolge, in der sie in der Initialisierungsliste angegeben sind. Um den Fehler im Programm geldstap.cpp zu beheben, mssten die Deklarationen in den Zeilen 21 bis 23 wie folgt vertauscht werden:
Programm 2.34 geldstap.cpp: Genderte Reihenfolge der Deklarationen
23 24 25 float scheinDicke; float scheinZahl; float hoehe;

Nun liefert das Programm geldstap.cpp auch die richtigen Ergebnisse: Welche Scheine sollen zum Stapeln verwendet werden: 10 Welcher Betrag soll mit diesen Scheinen gestapelt werden: 1000000
Mit 10 Euro-Scheine werden 1000000 Euro gestapelt Der Stapel wre hoch: 1500.00 cm = 15.00 m = 0.02 km

Welche Scheine sollen zum Stapeln verwendet werden: 5

Welcher Betrag soll mit diesen Scheinen gestapelt werden: 1e10

Mit 5 Euro-Scheine werden 10000000000 Euro gestapelt Der Stapel wre hoch: 30000000.00 cm = 299999.99 m = 300.00 km

67

68

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.4.3 Beziehungen zwischen Objekten


2.4.3.1 Ein Vieleck Ein Vieleck besteht aus einzelnen Punkten, d.h. es bietet sich fr CVieleck ein Array aus Punkten (CPunkt) an. Genau genommen muss es sich um ein dynamisches Array handeln, dessen Gre beim Konstruktor angegeben wird. Was hat das Vieleck davon, dass es ein Array aus Punkten verwendet und nicht etwa zwei int-Arrays (fr jeweils x- und y-Wert)? Die Struktur ist zum einen bersichtlicher, zum anderen kann der Punkt auch Funktionalitt enthalten, die das Vieleck dann nutzen kann, etwa dass der Punkt sich selbst darstellen kann; bei CVieleck::zeichnen() kann dann einfach CPunkt::show() aufgerufen werden. Welche Methoden bei CVieleck und CPunkt allerdings in der Praxis tatschlich Sinn machen, hngt vom jeweiligen Anwendungsfall ab. Abbildung 2.15 zeigt das Klassendiagramm zum Vieleck. Hierbei ist erkennbar, dass es sich bei der Assoziation zu CEigenschaften um eine Komposition handelt, was auch Sinn macht, da mit dem Zerstren eines Vielecks auch seine eingestellte Rand- und Fllfarbe zerstrt werden muss.
1

CEigenschaften
1 randFarbe fuellFarbe

CVieleck
1 3..*

CPunkt

Abbildung 2.15: Klassendiagramm zu einem Vieleck

Programm 2.35 punkt.h: Die Klasse CPunkt


#ifndef PUNKT_H #define PUNKT_H

#include <iostream> using namespace std;

class CPunkt { public: CPunkt(int x=0, int y=0) : m_x(x), m_y(y) { } ~CPunkt() { } void show() { cout << "P(" << m_x << ", " << m_y << ")"; } private: int m_x; int m_y; }; #endif

68

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

69

Programm 2.36 vieleck.cpp: Die Klassen CEigenschaften und CVieleck


class CEigenschaften // reprsentiert Rand- und Fllfarbe { public: CEigenschaften(farbe rand, farbe fuell) : m_randFarbe(rand), m_fuellFarbe (fuell) { } void print(void) { cout << "Randfarbe = " << farbName[m_randFarbe]

<< ", Fllfarbe = " << farbName[m_fuellFarbe] << endl; } private: farbe m_randFarbe, m_fuellFarbe; }; class CVieleck // reprsentiert ein Vieleck { public: CVieleck(char *name, int anzahl, farbe rand=schwarz, farbe fuell=weiss) : m_eigenschaft(rand, fuell) { m_punkt = new CPunkt[anzahl]; m_anzahl = m_punkt ? anzahl : 0; strcpy(m_name, name); } ~CVieleck() { // Destruktor

if (m_punkt) delete [] m_punkt; } bool set(int pos, const CPunkt& pkt) { if (!m_punkt || pos >= m_anzahl || pos < 0) return false; m_punkt[pos] = pkt; return true; } void zeichnen(void) { cout << m_name << ":" << endl << " m_eigenschaft.print(); cout << " Punkte: "; ";

for (int i=0; i < m_anzahl; i++) { m_punkt[i].show(); cout << " } cout << endl; } private: int CPunkt m_anzahl; *m_punkt; // Anzahl der Punkte // eigentliche Punkte ";

CEigenschaften m_eigenschaft; // Member-Variable (Kompositon) char }; m_name[100];

69

70

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.4.3.2 Ein Polygonzug Abbildung 2.16 zeigt das Klassendiagramm zum Polygonzug. Programm 2.35 (punkt.h) auf Seite 68 zeigt die Realisierung der Klasse CPunkt und Programm 2.37 zeigt die Realisierung der Klassen CListElem und CPolygon.
CPolygon
+ insert(CPunkt pkt) + zeichnen() 1 1 Listenanfang 1

CListElem
+ zeichneAlle() 1

Nachfolger 1

CPunkt

Abbildung 2.16: Klassendiagramm zu einem Polygon

Programm 2.37 polygon.cpp: Die Klassen CListElem und CPolygon


class CListElem { public: CListElem(CListElem *Next, CPunkt pkt) : m_punkt(pkt), m_Next(Next) { } ~CListElem() { // Nachfolger wird automat. mit ListenElement selbst gelscht if (m_Next) delete m_Next; // Wenn Nachfolger existiert, ihn lschen } void zeichneAlle(void) { if (m_Next) m_Next->zeichneAlle(); // m_next (rekursiv) Zeichnen auftragen m_punkt.show(); // Das jeweilige Element (Rekursion!) zeichnet sich nun cout << endl; } private: CPunkt m_punkt;

CListElem *m_Next; }; class CPolygon { public: CPolygon() : m_Head(NULL) { } // Liste als leer kennzeichnen ~CPolygon() { if (m_Head) delete m_Head; } bool insert(CPunkt pkt) { // Neuen Knoten am Beginn der Liste einfuegen CListElem *pNew = new CListElem(m_Head, pkt); if (pNew) m_Head = pNew; return pNew != 0; } void zeichnen() { if (m_Head) m_Head->zeichneAlle(); } private: CListElem *m_Head; }; // Kopf der Liste // Auftrag zum Zeichnen an 1. Knoten

70

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

71

2.4.3.3 Assoziationen zum Monopoly-Spiel Abbildung 2.17 zeigt die entsprechenden Klassendiagramme.
Klassendiagramm 1
nimmt teil Spiel

Teilnehmer

Spieler
Besitzer

besitzt befindet sich auf Strasse

Klassendiagramm 2
1

es existieren 8 davon
22 1 1

MonopolySpiel

Strasse

Strassengruppe

Klassendiagramm 3
Strassengruppe
8 1 2..3

Strasse
22

1 1

MonopolySpiel

Klassendiagramm 4
MonopolySpiel 1
2..6 Teilnehmer

Spieler

Klassendiagramm 5
40

Spielfeld

Spielbrett
16

Ereigniskarte

16

Gemeinschaftskarte

Abbildung 2.17: Klassendiagramme zum Monopoly-Spiel

71

72

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.38 monopclass1.cpp: C++-Programm zum Klassendiagramm 2


#include <iostream> #include <iomanip> using namespace std;

class CStrassengruppe { public: CStrassengruppe(int i) : m_gruppNr(i) { } int getNr(void) { return m_gruppNr; } private: int m_gruppNr; }; class CStrasse { public: CStrasse() : m_gruppe(0) { } void setNr(int i) { m_nr = i; int getNr(void) }

{ return m_nr; }

void setGruppe(CStrassengruppe *gruppe) { m_gruppe = gruppe; } CStrassengruppe *getGruppe(void) { return m_gruppe; } private: int m_nr;

CStrassengruppe *m_gruppe; // Fr Kommunikation mit CStrassengruppe }; class CMonopolyspiel { public: CMonopolyspiel(CStrassengruppe *strGruppe[]) { for (int i=0; i<22; i++) { m_strasse[i].setNr(i); m_strasse[i].setGruppe(strGruppe[i%8]); } } void printStrassen(void) { for (int i=0; i<22; i++) cout << setw(2) << m_strasse[i].getNr() << " (" << m_strasse[i].getGruppe()->getNr() << ")" << ((i%8 != 7) ? ", " : "\n"); cout << endl; } private: CStrasse }; int main(void) { CStrassengruppe *strGruppe[8]; // Anlegen von 8 Strassengruppen for (int i=0; i<8; i++) strGruppe[i] = new CStrassengruppe(i); CMonopolyspiel spiel(strGruppe); spiel.printStrassen(); } m_strasse[22]; // Strassen als Komposition

72

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

73

Programm 2.39 monopclass2.cpp: C++-Programm zum Klassendiagramm 3


#include <iostream> #include <iomanip> using namespace std;

class CStrassengruppe { public: CStrassengruppe() : m_strZahl(0) { for (int i=0; i<4; i++) m_strasse[i] = -1; } void setNr(int i) { m_gruppNr = i; } int getNr(void) { return m_gruppNr; }

void setStrasseNr(int i) { m_strasse[m_strZahl++] = i; } int getStrasseNr(int i) { return m_strasse[i]; }

private: int m_gruppNr; int m_strZahl; int m_strasse[4]; // Gruppe kennt ihre Strassen; Abbruch bei [4] = -1 };

class CStrasse { public: CStrasse() { } void setNr(int i) { m_nr = i; int getNr(void) }

{ return m_nr; }

void setGruppeNr(int i) { m_gruppe = i; } int getGruppeNr(void) { return m_gruppe; }

private: int m_nr; int m_gruppe; // Strasse kennt ihre Gruppe };

class CMonopolyspiel { public: CMonopolyspiel() { int i; for (i=0; i<8; i++) m_strGruppe[i].setNr(i); for (i=0; i<22; i++) { m_strasse[i].setNr(i); m_strGruppe[i%8].setStrasseNr(i); m_strasse[i].setGruppeNr(i%8); } }

73

74

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

void printStrassen(void) { for (int i=0; i<22; i++) cout << setw(2) << m_strasse[i].getNr() << " (" << m_strGruppe[m_strasse[i].getGruppeNr()].getNr() << ")" << ((i%8 != 7) ? ", " : "\n"); cout << endl; }

void printStrassenGruppen(void) { cout << "--------- Strassengruppen:" << endl; for (int i=0; i<8; i++) { int j = 0, str; cout << setw(5) << m_strGruppe[i].getNr() << ": "; while ( (str = m_strGruppe[i].getStrasseNr(j++)) >= 0) cout << setw(2) << m_strasse[str].getNr() << ", "; cout << endl; } }

private: CStrasse m_strasse[22]; // Strassen als Komposition

CStrassengruppe m_strGruppe[8]; // Strassengruppen als Komposition };

int main(void) { CMonopolyspiel spiel;

spiel.printStrassen(); spiel.printStrassenGruppen(); }

74

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

75

2.4.3.4 Mastermind gegen den Computer Zunchst sollte man sich folgende berlegungen oder hnliche machen: Finden der Klassen durch Betrachtung der Realitt Beim Spiel Mastermind gibt es in der Realitt einen Spieler, der rt (CRater), einen Spieler, der sich die Zahl berlegt (CAufgabenSteller), und das Spielbrett (CSpielBrett als Kommunikationsplattform zwischen diesen beiden. Finden erster Methoden fr das Spielbrett Da keine Historie mitgefhrt werden muss, interessiert beim Spielbrett nur die aktuell gesetzte Kombination, was durch ein Array realisiert werden kann, das die einzelnen Ziffern enthlt. Nun um diese Kombination zu setzen, muss das Spielbrett der Klasse CRater eine Methode setKombination() anbieten. Umgekehrt muss das Spielbrett der Klasse CAufgabenSteller eine Methode getKombination() anbieten, damit diese Klasse die aktuell gesetzte Kombination erfragen kann, wenn Sie sie bewerten muss. In unserem Fall wre das nicht notwendig, da der Spieler ja am Bildschirm die aktuelle Kombination sieht, aber in Hinsicht auf sptere Erweiterungen scheint es Sinn zu machen, diese Methode anzubieten. Allerdings wird sich herausstellen, dass die Klasse CRater die Methode getKombination() bentigt, um beim Streichen ungltiger Kombination die aktuell gesetzte Kombination zu erfragen. In jedem Fall muss die Klasse CAufgabenSteller ihre Bewertung dem Spielbrett mitteilen, so dass die Klasse CSpielBrett Methoden wie setBullen() und setKuehe() anbieten muss. Um die aktuell gesetzten Khe und Bullen zu erfragen, muss das Spielbrett der Klasse CRater die Methoden getBullen() und getKuehe() zur Verfgung stellen. Finden erster Methoden fr den Rater und Aufgabensteller Nun bei genauerer Betrachung erkennt man, dass immer der Rater durch Angeben einer mglichen Kombination eine Raterunde einleitet. Deshalb empehlt sich fr die Klasse CRater eine Methode wie z.B. nextRateRunde(), in der der diese Klasse eine neue Kombination ermittelt, die sie dann auch auf dem Spielbrett setzt. Die Klasse CAufgabenSteller muss nun diese Kombination bewerten, also muss diese Klasse z.B. eine Methode wie werteAus() anbieten, in der die aktuell auf dem Spielfeld gesetzte Kombination durch Setzen von Bullen und Khen bewertet wird. Somit knnen wir ein erstes prinzipielles Klassendiagramm aufstellen, wie es in Abbildung 2.18 gezeigt ist. Abbildung 2.19 zeigt dann ein Sequenzdiagramm zum Mastermind-Spiel. Bedingt durch das Aufstellen des Sequenzdiagramms kann dann auch detaillierteres Klassendiagramm gefunden werden, wie es in Abbildung 2.20 gezeigt ist. Programm 2.40 zeigt dann die Realisierung des Mastermind-Spiels.

75

76

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

CRater
+ nextRateRunde()

CSpielBrett
m_kombination: char[] m_bullen: int m_kuehe: int + setKombination() + getKombination(): char[] + setBullen(int b) + setKuehe(int k) + getBullen(): int + getKuehe(): int

CAufgabenSteller
+ werteAus()

Abbildung 2.18: Erstes Klassendiagramm zum Mastermind-Spiel

main()

:CRater

:CSpielbrett

:CAufgabeSteller

r := nextRateRunde() * antw := moeglLoesung(kombi) getKombination() getBullen() getKuehe()

[antw=false] streicheKombination() setKombination()

[r=true] w := werteAus() setBullen() setKuehe()

while (r && !w)


[r=false] getKombination() [else] Fehler

Abbildung 2.19: Sequenzdiagramm zum Mastermind-Spiel

CRater
m_rateNr: int=0 m_stellenZahl: int m_kombiZahl: int m_kombi: char ** + nextRateRunde() moeglLoesung(char *kombi): bool

CSpielBrett
m_kombination: char[] m_bullen: int m_kuehe: int + setKombination() + getKombination(): char[] + setBullen(int b) + setKuehe(int k) + getBullen(): int + getKuehe(): int

1 CAufgabenSteller
m_stellenZahl: int + werteAus()

Abbildung 2.20: Detaillierteres Klassendiagramm zum Mastermind-Spiel

76

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

77

Programm 2.40 moo.cpp: Implementierung des Mastermind-Spiels


#include <iostream> #include <cstring> #include <cmath>

using namespace std;

//................................................................. CSpielBrett class CSpielBrett { public: CSpielBrett(int stellenZahl) : m_kombination(new char[stellenZahl+1]) { } void setBullen(int b) { m_bullen = b; void setKuehe(int k) int int getBullen(void) getKuehe(void) { m_kuehe = k; } }

{ return m_bullen; } { return m_kuehe; }

void setKombination(int nr, char *versuch) { strcpy(m_kombination, versuch); cout << "Mein " << nr << ". Rateversuch: " << versuch << endl; } char *getKombination() { return m_kombination; } private: int m_bullen, m_kuehe; char *m_kombination; };

//...................................................................... CRater class CRater { public: CRater(int stellenZahl, CSpielBrett *spielBrett) : m_rateNr(0), m_stellenZahl(stellenZahl), m_kombiZahl((int)::pow(10, stellenZahl)), m_letzteKombiNr(0), m_kombi(new char*[m_kombiZahl]), m_spielBrett(spielBrett), m_temp1(new char[stellenZahl+1]), m_temp2(new char[stellenZahl+1]) { for (int i=0; i<m_kombiZahl; i++) { // alle mgl. Kombinationen erzeugen m_kombi[i] = new char[stellenZahl+1]; sprintf(m_kombi[i], "%0*d", stellenZahl, i); } }

77

78

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

bool nextRateRunde(void) { if (++m_rateNr != 1) //... Beim 1.Mal wird einfach 1.Kombination genommen for (int i=0; i<m_kombiZahl; i++) if (m_kombi[i] != NULL && !moeglLoesung(m_kombi[i])) { delete m_kombi[i]; m_kombi[i] = NULL; } for (int i=m_letzteKombiNr; i<m_kombiZahl; i++) //... Finden 1. mgl. Lsung if (m_kombi[i] != NULL) { m_spielBrett->setKombination(m_rateNr, m_kombi[m_letzteKombiNr=i]); return true; } return false; } //... Streichen unmgl. Lsungen

private: int int int int m_rateNr; m_stellenZahl; m_kombiZahl; m_letzteKombiNr;

char **m_kombi; CSpielBrett *m_spielBrett; char *m_temp1, // Hilfsvariablen *m_temp2; //.... Folgende Funktion streicht alle Kombinationen, bei denen die aktuell //.... gesetzte Zahl nicht die gleichen Khe und Bullen ergbe, //.... da es sich bei diesen Kombinationen nicht um die Lsung handeln kann. bool moeglLoesung(char *kombi) { int i, k = 0, b = 0;

strcpy(m_temp1, m_spielBrett->getKombination()); strcpy(m_temp2, kombi); for (i=0; i<m_stellenZahl; i++) //.... Bullen zhlen if (m_temp1[i] == m_temp2[i]) { b++; m_temp1[i] = m_temp2[i] = 0; } for (i=0; i<m_stellenZahl; i++) //.... Khe zhlen for (int j=0; j<m_stellenZahl; j++) if (m_temp1[i] != 0 && m_temp2[j] != 0 && m_temp1[i]==m_temp2[j]) { k++; m_temp1[i] = m_temp2[j] = 0; } return (m_spielBrett->getBullen() == b) && (m_spielBrett->getKuehe() == k); } };

78

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

79

//............................................................ CAufgabenSteller class CAufgabenSteller { public: CAufgabenSteller(int stellenZahl, CSpielBrett *spielBrett) : m_stellenZahl(stellenZahl), m_spielBrett(spielBrett) { } bool werteAus(void) { int kuehe, bullen; cout << " Bullen: "; cin >> bullen;

if (bullen == m_stellenZahl) return true; //.... Erraten cout << " Kuehe: "; cin >> kuehe;

m_spielBrett->setBullen(bullen); m_spielBrett->setKuehe(kuehe); return false; //.... Noch nicht erraten } private: int m_stellenZahl;

CSpielBrett *m_spielBrett; }; //....................................................................... main int main(int argc, char *argv[]) { int stellZahl; bool ratenMoegl; if (argc != 2) { //.................. Auswerten der Kommandozeilen-Argumente cerr << "Falscher Aufruf;" << endl << " exit(1); } if ( (stellZahl=atoi(argv[1])) <= 0) { cerr << "Parameter 1 keine pos. Zahl" << endl; exit(1); } cout << "Das Spiel Moo" << endl //..................... Kurze Spielanleitung << << "===============" << endl "Denken Sie sich eine beliebige Kombination aus " << stellZahl richtiger Aufruf: " << argv[0] << " stellenzahl" << endl;

<< " Ziffern aus" << endl << "Ich werde versuchen Sie zu erraten" << endl; spielBrett(stellZahl); //................. Das Spiel laeuft rater(stellZahl, &spielBrett);

CSpielBrett CRater

CAufgabenSteller aufgabenSteller(stellZahl, &spielBrett); while ((ratenMoegl = rater.nextRateRunde()) && !aufgabenSteller.werteAus()) ; if (!ratenMoegl) cout << endl << ".... Irgendwo haben Sie mir falsche Information gegeben;" << endl << ".... Deswegen kann ich keine Lsung finden" <<endl <<endl; else cout << endl << ".... Die Lsung ist also: " << spielBrett.getKombination() << endl << endl; }

79

80

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.4.3.5 Realisierung eines Taschenrechners


CTastatur
+ getNextTaste() : int

1 1

CRechner
+ exec()

COperation
1

1 1

+ rechne(int zeich) 1 + korrigiere(int zeich) + getAktiv(): char 1 + setInaktiv() + isOperator(int zeich) : bool

CYReg
m_wert[30] : char + set(float wert) + get() : float

1
Nachfolger

1 1 1 1

1 1

CAnzeige
+ show()

1 1

CXReg
m_wert[30] : char m_zustand: enum state m_altZustand: enum state + set(float wert) + get() : float + taste(int zeich)

1 1

COperator
m_zeich: char=operatoren[i] m_aktiv: bool=false + berechne(float y, float x) : float + getOperator() : int + setAktiv(int zeich) + setInaktiv(int zeich) + getAktiv() : bool
Wert wird in Registern als String gespeichert. bergeben und zurckgegeben wird der Wert jedoch als floatWert

enum state { ausgangsZustand, zahlZustand, berechZustand, korrigierZustand, clearZustand }; m_zustand=aktueller Zustand; m_altZustand=vorheriger Zustand

Abbildung 2.21: Klassendiagramm zum Taschenrechner

ausgangsZustand
entry / set(0) entry / m_yReg>set(0) entry / m_op>setInaktiv()

Ziffer Dezimalpunkt Taste C

zahlZustand
entry / m_wert[0] = 0 Fge Ziffer an m_wert an Ziffer ^m_yReg>set(get()) Dezimalpunkt ^m_yReg>set(get())

Ziffer Dezimalpunkt

Rechen zeichen
er De lpu nk t

Taste C

ff Zi

a zim

clearZustand
entry / set(0)

berechZustand
Taste C entry / m_op>rechne(zeich) Rechenzeichen

Dezimalpunkt^m_yReg>set(get())

Ziffer^m_yReg>set(get())

Taste C

korrigierZustand
entry / m_op>korrigiere(zeich)

Rechen zeichen

Abbildung 2.22: Zustandsdiagramm zur Methode taste(zeichen) des X-Registers

80

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

81

:CRechner exec()

:CTastatur

:CXReg

:CYReg

:COperator

:COperation

:CAnzeige

z:=getNextTaste() z=1 taste(1)

show() get() get() a:=getAktiv() getAktiv()

z:=getNextTaste() z=4 taste(4)


Ist entspr. Operator aktiv, wird sein Zeichen zurckgegeben, sonst wird COperation::getAktiv() rekursiv aufgerufen

x: 1 y: 0 aktiv: _

show() get() get() a:=getAktiv() getAktiv()

z:=getNextTaste() z=+ taste(+) antw:=isOperator(+) a:=getOperator() [a=+]true [antw=true]rechne(+) a:=getAktiv() a=false setAktiv(+)

x:14 y: 0 aktiv: _

Rekursiver Aufruf von rechne() zum Durchlaufen der Operatorenliste

show() get() get() a:=getAktiv() getAktiv()

z:=getNextTaste() z=7 taste(7) wert:=get() set(wert)

x:14 y:0 aktiv: +

show() get() get() a:=getAktiv() getAktiv()

x:7 y:14 aktiv:+

Abbildung 2.23: Sequenzdiagramm zur Eingabe 14+7/3=; 1. Teil

81

82

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

:CRechner

:CTastatur

:CXReg

:CYReg

:COperator

:COperation

:CAnzeige

z:=getNextTaste() z=/ taste(/) antw:=isOperator(/) a:=getOperator() isOperator(/) true { / wird in Operatorenliste gefunden} [antw=true]rechne(/) a:=getAktiv() a=true {fr +} y:=get() y=14 x:=get() x=7 erg:=berechne(y,x) erg=21 set(erg) setAktiv(/) rechne(/) a:=getAktiv() a=false {fr } setAktiv(/) rechne(/) a:=getAktiv() a=false {fr *} setAktiv(/) rechne(/) a:=getAktiv() a=false {fr /} setAktiv(/)
deaktiviert +, da + ungleich / Rekursiver Aufruf von isOperator() zum Durchlaufen der Operatorenliste

deaktiviert da ungleich /

* deaktiviert da * ungleich /

/ wird aktiviert da / gleich /

show() get() get() a:=getAktiv() getAktiv()

x:21 y:14 aktiv: /

Abbildung 2.24: Sequenzdiagramm zur Eingabe 14+7/3=; 2. Teil

82

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

83

:CRechner exec()

:CTastatur

:CXReg

:CYReg

:COperator

:COperation

:CAnzeige

z:=getNextTaste() z=3 taste(3) wert:=get()

set(wert)

show() get() get() a:=getAktiv() getAktiv()

x:3 y:21 aktiv:/

z:=getNextTaste() z== taste(=) antw:=isOperator(=)

Ist entspr. Operator aktiv, wird sein Zeichen zurckgegeben, sonst wird COperation::getAktiv() rekursiv aufgerufen

a:=getOperator() isOperator(=) true { = wird in Operatorenliste gefunden} [antw=true]rechne(=) a:=getAktiv() a=false {fr +} setAktiv(=) rechne(=) a:=getAktiv() a=false {fr } setAktiv(=) rechne(=) a:=getAktiv() a=false {fr *} setAktiv(=) rechne(=) a:=getAktiv() a=true {fr /} y:=get() y=21 x:=get() x=3 erg:=berechne(y,x) erg=7 set(erg) setAktiv(=) rechne(=) a:=getAktiv() a=false {fr =} setAktiv(=)
/ deaktiviert da / ungleich = deaktiviert +, da + ungleich = Rekursiver Aufruf von isOperator() zum Durchlaufen der Operatorenliste

deaktiviert da ungleich =

* deaktiviert da * ungleich =

= wird aktiviert da = gleich =

show() get() get() a:=getAktiv() getAktiv()

x:7 y:21 aktiv: =

Abbildung 2.25: Sequenzdiagramm zur Eingabe 14+7/3=; 3. Teil

83

84

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.41 (taschrech.h) zeigt die Klassendenitionen und Programm 2.42 (taschrech.cpp) die Implementierung des Taschenrechners.
Programm 2.41 taschrech.h: Klassendenitionen zum Taschrechner
#ifndef TASCHRECH_H #define TASCHRECH_H #include <cstring> #include <cctype> class COperation; // FORWARD-Deklarationen class CXReg; class CYReg;

//--------------------------------------------------------- Klasse CAnzeige class CAnzeige { public: CAnzeige(CXReg *x, CYReg *y, COperation *op) : m_xReg(x), m_yReg(y), m_op(op) { } void show(void); private: CXReg CYReg *m_xReg; *m_yReg;

COperation *m_op; };

//------------------------------------------------------------ Klasse CYReg class CYReg { public: CYReg() { m_wert[0] = 0; } void set(float wert) { sprintf(m_wert, "%g", wert); } { return atof(m_wert); }

float get(void) private: char m_wert[30]; };

//------------------------------------------------------------ Klasse CXReg class CXReg { public: CXReg(CYReg *y, COperation *op); void set(float wert) { sprintf(m_wert, "%g", wert); } float get(void) void private: CYReg *m_yReg; COperation *m_op; char m_wert[30]; enum state { ausgangsZustand, zahlZustand, berechZustand, korrigierZustand, clearZustand }; state }; m_zustand, m_altZustand; taste(int z); { return atof(m_wert); }

84

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

85

//-------------------------------------------------------- Klasse COperator class COperator { public: COperator(int i); float berechne(float y, float x); int getOperator(void) { return m_zeich; }

void setAktiv(int z) void setInaktiv(void) bool getAktiv(void) private: char m_zeich; bool m_aktiv; };

{ m_aktiv = (m_zeich == z); } { m_aktiv = false; { return m_aktiv; } }

//------------------------------------------------------- Klasse COperation class COperation { public: COperation(COperation *next, int i, CXReg *x, CYReg *y); void rechne(int z); void korrigiere(int z); char getAktiv(void); void setInaktiv(void); bool isOperator(int z); private: COperator m_operator;

COperation *m_next; CXReg *m_xReg; CYReg }; //-------------------------------------------------------- Klasse CTastatur class CTastatur { public: int getNextTaste(void) { return getchar(); } }; //--------------------------------------------------------- Klasse CRechner class CRechner { public: CRechner() : m_op (new COperation(0, 0, &m_xReg, &m_yReg)), m_anzeige(&m_xReg, &m_yReg, m_op), m_xReg(&m_yReg, m_op) { } int exec(void); private: COperation CAnzeige CXReg CYReg CTastatur }; #endif *m_op; m_anzeige; m_xReg; m_yReg; m_tastatur; *m_yReg;

85

86

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

Programm 2.42 taschrech.cpp: Implementierung des Taschrechners


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 } //------------------------------------------------------------ CXReg::taste void CXReg::taste(int z) { m_altZustand = m_zustand; switch (m_zustand) { case ausgangsZustand: if (isdigit(z) || z == .) m_zustand = zahlZustand; break; case zahlZustand: if (m_op->isOperator(z)) m_zustand = berechZustand; else if (toupper(z) == C) m_zustand = clearZustand; break; case berechZustand: case korrigierZustand: if (isdigit(z) || z == .) { m_zustand = zahlZustand; m_yReg->set(get()); } else if (toupper(z) == C) m_zustand = clearZustand; else if (m_op->isOperator(z)) m_zustand = korrigierZustand; break; case clearZustand: if (isdigit(z) || z == .) m_zustand = zahlZustand; else if (toupper(z) == C) } //------------------------------------------------------------ CXReg::CXReg CXReg::CXReg(CYReg *y, COperation *op) : m_yReg(y), m_op(op), m_zustand(ausgangsZustand) { m_wert[0] = 0; set(0); m_yReg->set(0); m_op->setInaktiv(); //---------------------------------------------------------- CAnzeige::show void CAnzeige::show(void) { cout << "-----------------------------------------" << endl << " x: " << m_xReg->get() << " << " y: " << m_yReg->get() #include #include #include <iostream> <cmath> "taschrech.h"

using namespace std;

aktiv: " << m_op->getAktiv() << endl

<< "-----------------------------------------" << endl;

86

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

87

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 } } } } } }

m_zustand = ausgangsZustand; break;

switch (m_zustand) { case ausgangsZustand: if (m_zustand != m_altZustand) { set(0); m_yReg->set(0); m_op->setInaktiv();

break; case zahlZustand: if (m_zustand != m_altZustand) m_wert[0] = 0; sprintf(m_wert, "%s%c", m_wert, z); break; case berechZustand: m_op->rechne(z); break; case korrigierZustand: m_op->korrigiere(z); break; case clearZustand: if (m_zustand != m_altZustand) set(0); break;

const char *operatoren = "+-*/="; //---------------------------------------------------- COperator::COperator COperator::COperator(int i) : m_zeich(operatoren[i]), m_aktiv(false) { } //----------------------------------------------------- COperator::berechne float COperator::berechne(float y, float x) { switch (m_zeich) { case +: return y + x; case -: return y - x; case *: return y * x; case /: return y / x; case =: return x; default: return x;

//-------------------------------------------------- COperation::COperation COperation::COperation(COperation *next, int i, CXReg *x, CYReg *y) : m_operator(i), m_next(next), m_xReg(x), m_yReg(y) { if (operatoren[i+1] != 0) { // Ende der Operatoren-Zeichenkette COperation *opNew = new COperation(m_next, i+1, x, y); if (opNew) m_next = opNew;

87

88

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 } } } } } } }

//------------------------------------------------------ COperation::rechne void COperation::rechne(int z) { if (m_operator.getAktiv()) m_xReg->set(m_operator.berechne(m_yReg->get(), m_xReg->get())); m_operator.setAktiv(z); if (m_next) m_next->rechne(z);

//-------------------------------------------------- COperation::korrigiere void COperation::korrigiere(int z) { m_operator.setAktiv(z); if (m_next) m_next->korrigiere(z);

//---------------------------------------------------- COperation::getAktiv char COperation::getAktiv(void) { if (m_operator.getAktiv()) return m_operator.getOperator(); if (m_next) return m_next->getAktiv(); return _; // keine erlaubte Operation

//-------------------------------------------------- COperation::setInaktiv void COperation::setInaktiv(void) { m_operator.setInaktiv(); if (m_next) m_next->setInaktiv();

//-------------------------------------------------- COperation::isOperator bool COperation::isOperator(int z) { if (m_operator.getOperator() == z) return true; if (m_next) return m_next->isOperator(z); return false;

//---------------------------------------------------------- CRechner::exec int CRechner::exec(void) { char zeich;

while ( (zeich = m_tastatur.getNextTaste()) != e) { m_xReg.taste(zeich); getchar(); // zum berlesen des Return-Zeichens m_anzeige.show(); } return 0;

//-------------------------------------------------------------------- main

88

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

89

149 150 151 152

int main(void) { CRechner rechner; return rechner.exec(); }

Programm 2.43 (taschrech2.cpp) zeigt, dass zur Erweiterung des Taschenrechners um den Potenzoperator im Programm 2.42 (taschrech.cpp) lediglich im String in der Zeile 75 der Potenzoperator einzufgen ist, und nach der Zeile 85 die Berechnung fr den Potenzoperator anzufgen ist. Der Rest des Programms 2.42 (taschrech.cpp) kann unverndert bernommen werden.
Programm 2.43 taschrech2.cpp: Erweiterung des Taschrechners um den Potenzoperator
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 } } const char *operatoren = "+-*/^="; //---------------------------------------------------- COperator::COperator COperator::COperator(int i) : m_zeich(operatoren[i]), m_aktiv(false) { } //----------------------------------------------------- COperator::berechne float COperator::berechne(float y, float x) { switch (m_zeich) { case +: return y + x; case -: return y - x; case *: return y * x; case /: return y / x; case ^: return pow(y, x); case =: return x; default: return x;

89

90

2 Lsungen zu Teil II (Strukturen, Klassen und Objekte in C++)

2.4.4 Befreundete Funktionen und Klassen


2.4.4.1 Addieren von Brchen Programm 2.44 zeigt das ergnzte Programm bruchadd.cpp.
Programm 2.44 bruchadd.cpp: Addieren von Brchen
class CBruch { public: CBruch( int z = 1, int n = 1 ) : m_zaehler(z), m_nenner(n) { } void add( const CBruch& zahl ) { //............ Addition eines Bruches m_zaehler = m_zaehler*zahl.m_nenner + zahl.m_zaehler*m_nenner; m_nenner *= zahl.m_nenner; kuerzen(); } friend CBruch add( const CBruch& z1, const CBruch& z2 ); // globaler friend void print( void ) { //......................... Ausgabe eines Bruches cout << m_zaehler << "/" << m_nenner << endl; } private: int m_zaehler, m_nenner; ...... }; CBruch add( const CBruch& z1, const CBruch& z2 ) { // Addition 2er Brche CBruch temp;

temp.m_zaehler = z1.m_zaehler*z2.m_nenner + z2.m_zaehler*z1.m_nenner; temp.m_nenner = z1.m_nenner * z2.m_nenner; temp.kuerzen(); return temp; }

2.4.4.2 Array von ungekrzten und gekrzten Brchen Programm 2.45 zeigt das ergnzte Programm brucharray.cpp.
Programm 2.45 brucharray.cpp: Array von ungekrzten und gekrzten Brchen
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; class CBruch { public: CBruch( int z=1, int n=1 ) : m_zaehler(z), m_nenner(n) { } friend class CBruchArray; private: int m_zaehler, m_nenner; int m_zaehlerGekuerzt, m_nennerGekuerzt; ...... };

90

2.4 Mehrere Klassen bzw. Objekte im Zusammenspiel

91

class CBruchArray { public: CBruchArray() { for (int i=0; i<20; i++) { bruchArray[i].m_nenner = rand()%100+1;

bruchArray[i].m_zaehler = rand()%100+1; bruchArray[i].kuerzen(); } } void print(void) { for (int i=0; i<20; i++) { cout << bruchArray[i].m_zaehler << "/" << bruchArray[i].m_nenner; if (bruchArray[i].m_zaehler != bruchArray[i].m_zaehlerGekuerzt) cout << " (" << bruchArray[i].m_zaehlerGekuerzt << "/" << bruchArray[i].m_nennerGekuerzt cout << endl; } } private: CBruch bruchArray[20]; }; << ")";

2.4.4.3 Viele fragwrdige Freunde Programm friendviel.cpp gibt Folgendes aus:

global_freund1: i=10 global_freund2: i=20 BFreundfkt: i=100 CFreundklasse1: i=110 CFreundklasse2: i=130

91

Kapitel 3
Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

93

94

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.1

Vererbung

3.1.1 Einfache Vererbung in C++


3.1.1.1 berschreiben von Funktionen Programm dezahexa.cpp liefert die folgende Ausgabe:
Dezimal: 10 Dezimal: 13 Hexadezimal: D

3.1.1.2 Konstruktor-/Destruktoraufrufe 1. bung (autofahr.cpp): Dieses Programm liefert die folgende Ausgabe:
KF->KA->DA->DF->

2. bung (blumen.cpp): Dieses Programm liefert die folgende Ausgabe:


Konstruktor Pflanze Konstruktor Bluetenblatt Konstruktor Bluetenblatt Konstruktor Blume Konstruktor Rose Konstruktor Pflanze Konstruktor Bluetenblatt Konstruktor Bluetenblatt Konstruktor Blume Konstruktor Rose Konstruktor Pflanze Konstruktor Bluetenblatt Konstruktor Bluetenblatt Konstruktor Blume Konstruktor Rose --------------------Alle Blumen welken... --------------------Destruktor Rose Destruktor Blume Destruktor Bluetenblatt Destruktor Bluetenblatt Destruktor Pflanze Destruktor Rose Destruktor Blume Destruktor Bluetenblatt Destruktor Bluetenblatt Destruktor Pflanze Destruktor Rose Destruktor Blume Destruktor Bluetenblatt Destruktor Bluetenblatt Destruktor Pflanze

94

3.1 Vererbung

95

Da Rosenstrauss ein Array von drei Rose-Objekte ist, muss dreimal ein Rose-Objekt erzeugt werden, wobei fr jedes dieser drei Rose-Objekte die folgende Sequenz durchlaufen wird: 1. Es wird zuerst der Konstruktor der Basisklasse durchlaufen (Planze). 2. In der Klassenhierarchie folgt dann Blume, die zuerst zwei Bltenbltter aggregiert. 3. Als Letztes in der Hierarchie kommt Rose. Es werden also zuerst die Teile konstruiert, bevor dann alles zusammengebaut wird. Die Destruktoraufrufe erfolgen in umgekehrter Reihenfolge. 3. bung (dackel.cpp): Programm dackel.cpp liefert die folgende Ausgabe:
+Tier +Bein+Bein+Bein+Bein +Hund +Dackel (Hansi)

+Tier +Bein+Bein+Bein+Bein +Hund +Dackel (Maxi)

***************** Alle Hunde bellen ***************** -Dackel (Maxi) -Hund -Bein-Bein-Bein-Bein -Tier

-Dackel (Hansi) -Hund -Bein-Bein-Bein-Bein -Tier

4. bung (abc.cpp): Programm abc.cpp liefert die folgende Ausgabe:


B (11), A (12), C, ~C, ~A (12), ~B (11),

5. bung (abcd.cpp): Programm abcd.cpp liefert die folgende Ausgabe:


Basis, A, B, Sub, C, D, Subsub, ~Subsub, ~D, ~C, ~Sub, ~B, ~A, ~Basis,

Diese Ausgabe erklrt sich dadurch, dass sowohl die Konstruktoren der Basisklassen als auch die der Membervariablen in der Reihenfolge ihrer Deklaration aufgerufen werden, und nicht in der Reihenfolge ihrer Angabe in der Initialisierungsliste.

95

96

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.1.1.3 Linie in Grakbibliothek Eine Linie lsst sich wie in Abbildung 3.1 gezeigt darstellen.
Rechteck
yd xd xm,ym xd yd xm,ym radius

Kreis
yd

Linie

xd xm,ym xd yd

Abbildung 3.1: Darstellung einer Linie ber ihren Mittelpunkt

Das Klassendiagramm der Grakbibliothek lsst sich dann wie in Abbildung 3.2 gezeigt ergnzen.
CGraphObj
# m_pos: CPunkt + setPos(p: CPunkt) + move(xd:int, yd:int) + draw() Diskriminator=Figurenform

CRechteck
m_xd: int m_yd: int + draw()

CKreis
m_radius {m_radius > 0} + draw()

CLinie
m_xd: int m_yd: int + draw()

Abbildung 3.2: Klassendiagramm zu Grakobjekten mit Linie

Programm 3.1 graphline.h: Ein um eine Linie erweiterte Grakbibliothek


#include <iostream> using namespace std;

class CPunkt { public: CPunkt(int x=0, int y=0) : m_x(x), m_y(y) { } void setPoint(CPunkt p) { m_x = p.m_x; m_y = p.m_y; } }

void movePoint(int dX=0, int dY=0) { m_x += dX; m_y += dY; void drawPoint() private: int m_x, m_y; };

{ cout << "P(x,y) = " << m_x << "," << m_y << endl; }

96

3.1 Vererbung

97

class CGraphObj { public: CGraphObj(int x=0, int y=0) { m_pos.setPoint(CPunkt(x, y)); } void setPos(CPunkt p) void move(int dX, int dY) void draw() protected: CPunkt m_pos; }; class CRechteck: public CGraphObj { public: CRechteck(int Xm=0, int Ym=0, int Xd=1, int Yd=1) : CGraphObj(Xm, Ym), // ruft zuerst Konstr. CGraphObj (Xm, Ym) auf m_xd(Xd), m_yd(Yd) { } void draw() { cout << "Rechteck(xd,yd) = " << m_xd << "," << m_yd << "; "; CGraphObj::draw(); } private: int m_xd, m_yd; }; class CKreis: public CGraphObj { public: CKreis(int Xm=0, int Ym=0, int radius=1) : CGraphObj(Xm, Ym), // ruft zuerst Konstr. CGraphObj (Xm, Ym) auf m_radius(radius) { } void draw() { cout << " Kreis(radius) = " << m_radius << "; "; { m_pos.setPoint(p); { m_pos.movePoint(dX, dY); { m_pos.drawPoint(); } } }

CGraphObj::draw(); } private: int m_radius; }; class CLinie: public CGraphObj { public: CLinie(int Xm=0, int Ym=0, int Xd=1, int Yd=1) : CGraphObj(Xm, Ym), // ruft zuerst Konstr. CGraphObj (Xm, Ym) auf m_xd(Xd), m_yd(Yd) { } void draw() { cout << " Linie(xd,yd) = " << m_xd << "," << m_yd << "; ";

CGraphObj::draw(); } private: int m_xd, m_yd; };

97

98

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.1.1.4 Klassenmodell zu Autos, Dreirdern usw. Autos, Dreirder, Fahrrder, Motorrder haben alle eine gemeinsame Eigenschaft: sie knnen alle fahren. Damit bietet sich eine gemeinsame Basisklasse, z.B. CFahrzeug an, die eine Methode fahren() enthlt. Diese Methode muss in den Unterklassen bei Bedarf berladen werden, hier bei CAuto und CFahrrad, denn sie haben spezielle Fahrgerusche. Somit erhlt man ein Klassendiagramm, wie es in Abbildung 3.3 gezeigt ist.
CFahrzeug
+ fahren()

CAuto
+ fahren()

CMotorrad

CFahrrad
+ fahren()

CDreirad

......Brumm brumm, tuet tuet.

......Kling kling.

Abbildung 3.3: Klassendiagramm zu Fahrzeugen

Programm 3.2 fahrzeuge.h: Klassenmodell zu Autos, Dreirdern usw.


#include <iostream> using namespace std;

class CFahrzeug public: CFahrzeug()

{ }

~CFahrzeug() { } void fahren() { cout << "----------------------------------" << endl << "...faehrt und faehrt und faehrt..." << endl; } }; class CAuto : public CFahrzeug public: CAuto() { } {

~CAuto() { } void fahren() { CFahrzeug::fahren(); cout << "......Brumm brumm, tuet tuet." << endl; } };

98

3.1 Vererbung

99

class CMotorrad : public CFahrzeug public: CMotorrad() { }

~CMotorrad() { } }; class CFahrrad : public CFahrzeug public: CFahrrad() { } {

~CFahrrad() { } void fahren() { CFahrzeug::fahren(); cout << "......Kling kling." << endl; } }; class CDreirad : public CFahrzeug public: CDreirad() { } {

~CDreirad() { } };

3.1.1.5 Erweitern des Klassenmodells zu Fahrzeugen Abbildung 3.4 zeigt ein mgliches Klassendiagramm zu dieser Aufgabenstellung.
CFahrzeug
+ fahren()

CRaederFahrzeug
Alle vorhandenen Rder drehen sich

m_anazhl: int + fahren()

CRad
+ drehen()

CAuto
+ fahren()

CMotorrad

CFahrrad
+ fahren()

CDreirad

Abbildung 3.4: Mgliches Klassendiagramm zu Fahrzeugen mit Rdern

Nachfolgend einige Anmerkungen zum Klassendiagramm in Abbildung 3.4: In der Realitt ist es so, dass nicht jedes Fahrzeug Rder besitzt, wie z.B. ein Schiff. Es ist daher gefhrlich, die Rder in die Basisklasse zu legen. Es ist grund-

99

100

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

stzlich empfehlenswert, die oberste Basisklasse von Attributen freizuhalten und sich hier nur auf Methoden zu beschrnken. Legt man die Rder jedoch in CAuto CDreirad usw. msste jedes dieser Fahrzeuge die Methode fahren() berladen, deren Implementierung dann aber sehr hnlich wre. Ein Kompromiss ist die Zwischenklasse CRaederFahrzeug, die das Fahren fr Fahrzeuge mit Rdern implementiert. Da die Anzahl der Rder vom Fahrzeugtyp (Auto, Dreirad, ...) abhngt, enthlt sie nur einen CRad-Zeiger. Wie viele Rder bentigt werden, wird beim Konstruktor der Unterklasse entschieden. Das Anlegen des Speichers sollte natrlich dort passieren, wo der Zeiger deniert ist, also in der Basisklasse; d.h. der Konstruktor der Unterklasse ruft den Konstruktor der Basisklasse mit einem entsprechenden Argument auf. In Abbildung 3.4 ist erkennbar, dass m_anzahl (und auch m_pRad) private und nicht protected sind, um zu unterbinden, dass die Unterklasse die Attribute der Basisklasse unkontrolliert verndern kann. Sollte in Zukunft ein Zugriff auf die Attribute ntig werden, so sollten z.B. entsprechende get()Methoden protected deniert werden. In Abbildung 3.4 werden brigens die Rder (m_pRad) in CRaederFahrzeug nicht explizit als Attribut notiert, weil dies bereits durch die Aggregationsbeziehung zwischen CRaederFahrzeug und CRad zum Ausdruck kommt. Die Argumentation Der CRad-Zeiger htte doch auch in CFahrzeug deniert werden und z.B. bei einem Schiff dann mit NULL initialisiert werden knnen, entspricht aber nicht der Realitt. Ein Schiff hat bun mal berhaupt keinerlei Vorrichtung fr Rder. Es ist stets gefhrlich, wenn man in Entwrfen Annahmen trifft, die der Realitt widersprechen. Genau an diesen Stellen gibt es meist Probleme bei zuknftigen Erweiterungen.
Programm 3.3 fahrzeuge2.h: Erweitertes Fahrzeug-Klassenmodell
#include <iostream> using namespace std; class CFahrzeug public: CFahrzeug() { } {

~CFahrzeug() { } void fahren() { cout << "----------------------------------" << endl << "...faehrt und faehrt und faehrt..." << endl; } }; class CRad { public: CRad() { }

~CRad() { } void drehen() { cout << "Rad-Drehung "; } };

100

3.1 Vererbung

101

class CRaederFahrzeug : public CFahrzeug { public: void fahren() { cout << "---------------------------------" << endl; for (int i=0; i < m_anzahl; i++) // Alle Rder drehen sich m_pRad[i].drehen(); cout << endl; } CRaederFahrzeug(int anzahl) { m_pRad = new CRad[anzahl]; m_anzahl = m_pRad ? anzahl: 0; } ~CRaederFahrzeug() { if (m_pRad) delete[] m_pRad; } protected: // Fr zuknftige mgliche Abfragen (hier nur zur Demo, um zu zeigen, // dass Zugriff auf Membervariablen nur geregelt protected erfolgen sollte) CRad* getRaeder() { return m_pRad; int private: int m_anzahl; // Anzahl der Rder }

getAnzahl() { return m_anzahl; }

CRad* m_pRad; }; class CAuto : public CRaederFahrzeug { public: CAuto() : CRaederFahrzeug(4) { } ~CAuto() { } void fahren() { CRaederFahrzeug::fahren(); cout << "......Brumm brumm, tuet tuet." << endl; } }; class CMotorrad : public CRaederFahrzeug { public: CMotorrad() : CRaederFahrzeug(2) { } ~CMotorrad() { } }; class CFahrrad : public CRaederFahrzeug { public: CFahrrad() : CRaederFahrzeug(2) { } ~CFahrrad() { } void fahren() { CRaederFahrzeug::fahren(); cout << "......Kling kling." << endl; } }; class CDreirad : public CRaederFahrzeug { public: CDreirad() : CRaederFahrzeug(3) { } ~CDreirad() { } };

101

102

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.1.1.6 Ableiten eines Lkws von einem Auto Programm 3.4 zeigt die fehlende Klasse CLkw zu dieser Aufgabenstellung.
Programm 3.4 lkw.cpp: Die Klasse CLkw
.... //...................................................................... CLkw class CLkw : public CAuto { public: CLkw(int g=0, int s=0, char const *name = "", int anhG=0) : CAuto(g, s, name), m_anhGewicht(anhG) { } void setGewicht(int gew) { m_anhGewicht = gew; }

int getGewicht(void) const { return CAuto::getGewicht() + m_anhGewicht; } private: int m_anhGewicht; }; ....

3.1.2 Virtuelle Methoden und Polymorphismus


3.1.2.1 Speicherbedarf bei virtuellen Funktionen Programm virtbytes.cpp liefert die folgende Ausgabe:
32

richtig ist also der 1. Punkt:


4 (CPunkt::m_x) + 4 (CPunkt::m_y) + 20 (CGraphObj::m_farbe) + 4 (vmp von CGraphObj)

3.1.2.2 Speicherbedarf bei abgeleiteten Klassen richtig ist Punkt 3 (sizeof(CAuto) >= sizeof(CFahrzeug)), denn jede Unterklasse enthlt zustzlich zu ihren Attributen die Attribute ihrer Basisklasse, die sie ja erbt. 3.1.2.3 Ein Suppenteller in Franken Programm virtfrank.cpp liefert die folgende Ausgabe:
Suppenteller---Suppenteller---Suppenteller---

richtig ist also der 2. Punkt, da die Methode CMensch::sprechen() nicht virtuell ist. 3.1.2.4 Franken und Berliner richtig ist: Zeile 6 und Zeile 7.

102

3.1 Vererbung

103

...... 4 class CMensch { 5 6 7 8 9 10 11 12 }; ...... } public: virtual void satz1() { cout << "Alle: Ich hole jetzt ein Taxi."; }

virtual void satz2() { cout << "Alle: Wir machen eine ganz tolle Party."; } void beide_saetze_sprechen() { satz1(); cout << endl; satz2(); cout << endl;

3.1.2.5 Rechnen mittels virtueller Methoden Programm virtrech.cpp liefert die folgende Ausgabe:
12 + 23 = 12 + 23 = 35 12 + 23 = 35

3.1.2.6 Virtuelle und nicht-virtuelle Destruktoren Programm virtdestr3.cpp liefert die folgende Ausgabe:
~Basis1 ~Sub2, ~Basis2

3.1.2.7 Tierhierarchie Programm raubtiere.cpp liefert die folgende Ausgabe:


---------------------------------Ich bin Leo, ein Lwe ...fresse andere Tiere ---------------------------------Ich bin Toni, ein Tiger ...fresse andere Tiere ---------------------------------Ich bin Kolja, ein Kojote ...fresse andere Tiere

Der Grund fr diese Ausgabe ist, dass das Array zoo[] Zeiger auf Objekte der Klasse Raubtier enthlt:
Raubtier *zoo[] = { ... };

und in der Klasse Raubtier ist nur die Methode ichBin() als virtual deklariert, so dass auch nur fr diese Methode spte Bindung stattndet, whrend fr die andere nicht virtuell deklarierte Methode iAm() frhe Bindung stattndet. Diese frhe Bindung kann dann auch nicht mehr spter in den abgeleiteten Klassen durch ein nachtrgliches berschreiben dieser Methode mit einer virtuellen Methode rckgngig gemacht werden.

103

104

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.1.2.8 Parkplatz mit Polymorphismus Programme 3.5 und Programm 3.6 zeigen eine mgliche Lsung zu dieser Aufgabenstellung.
Programm 3.5 fahrzeuge3.h: Erweitertes Fahrzeug-Klassenmodell mit virtuellen Funktionen
#include <iostream> using namespace std; //-------------------------------------------------------------------- CFahrzeug class CFahrzeug { public: CFahrzeug() { }

virtual ~CFahrzeug() { } virtual void fahren() { cout << "----------------------------------" << endl << "...faehrt und faehrt und faehrt..." << endl; } }; // ..... siehe Programm 3.3 auf Seite~100

Programm 3.6 parkplatz.cpp: Parkplatz-Verwaltung mit Polymorphismus


#include "fahrzeuge3.h" int main(void) { CAuto CFahrrad auto1, auto2; blechEsel;

CMotorrad mySuzi;

CFahrzeug* parkplatz[10]; for (int i=0; i < 10; i++) parkplatz[i] = 0;

// parkplatz fr 10 Fahrzeuge

parkplatz[0] = &blechEsel; parkplatz[2] = &auto1; parkplatz[3] = &mySuzi; parkplatz[5] = &auto2;

for (int i=0; i < 10; i++) if (parkplatz[i])

// Geschftsschluss

parkplatz[i]->fahren(); }

104

3.1 Vererbung

105

3.1.2.9 Polymorpher Taschenrechner


Programm 3.7 calculator.h: Klassendenition zum Taschenrechner
#include <iostream> #include <cmath> #include <cctype> using namespace std; class COperation; // FORWARD-Deklarationen //-------------------------------------------------------- Klasse CRegister class CRegister { public: CRegister() void { m_wert[0] = 0; set(0); }

set(float wert) { sprintf(m_wert, "%g", wert); } { return atof(m_wert); }

float get(void) protected: char m_wert[30]; };

//------------------------------------------------------------ Klasse CYReg class CYReg : public CRegister { }; //------------------------------------------------------------ Klasse CXReg class CXReg : public CRegister { public: CXReg(CYReg *y) : m_yReg(y), m_zustand(ausgangsZustand) { } void setOp(COperation *op) { m_op = op; } void private: CYReg *m_yReg; COperation *m_op; enum state { ausgangsZustand, zahlZustand, berechZustand, korrigierZustand, clearZustand }; state }; //--------------------------------------------------------- Klasse CAnzeige class CAnzeige { public: CAnzeige(CXReg *x, CYReg *y) : m_xReg(x), m_yReg(y) { } void setOp(COperation *op) { m_op = op; } void show(void); private: CXReg CYReg *m_xReg; *m_yReg; m_zustand, m_altZustand; taste(int z);

COperation *m_op; }; //------------------------------------------------------- Klasse COperation class COperation { public: COperation(CXReg *x, CYReg *y) : m_aktiv(false), m_next(NULL), m_xReg(x), m_yReg(y) { } void insert(COperation *op);

105

106

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

void rechne(int z); void setInaktiv(void); void korrigiere(int z); char getAktiv(void) { return (m_aktiv) ? m_zeich : (m_next ? m_next->getAktiv() : _); } bool COperation::isOperator(int z) { return (m_zeich == z) ? true : (m_next ? m_next->isOperator(z) : false); } protected: virtual float berechne(float y, float x) { return x; } bool char m_aktiv; m_zeich;

COperation *m_next; CXReg *m_xReg; CYReg }; //------------------------------------------------------------- Klasse CAdd class CAdd : public COperation { public: CAdd(CXReg *x, CYReg *y) : COperation(x, y) { m_zeich = +; } float berechne(float y, float x) { return y + x; } }; //------------------------------------------------------------- Klasse CSub class CSub : public COperation { public: CSub(CXReg *x, CYReg *y) : COperation(x, y) { m_zeich = -; } float berechne(float y, float x) { return y - x; } }; //------------------------------------------------------------ Klasse CMult class CMult : public COperation { public: CMult(CXReg *x, CYReg *y) : COperation(x, y) { m_zeich = *; } float berechne(float y, float x) { return y * x; } }; //------------------------------------------------------------- Klasse CDiv class CDiv : public COperation { public: CDiv(CXReg *x, CYReg *y) : COperation(x, y) { m_zeich = /; } float berechne(float y, float x) { return y / x; } }; //------------------------------------------------------------- Klasse CIst class CIst : public COperation { public: CIst(CXReg *x, CYReg *y) : COperation(x, y) { m_zeich = =; } }; //-------------------------------------------------------- Klasse CTastatur class CTastatur { public: int getNextTaste(void) { char z; cin >> z; return z; } *m_yReg;

106

3.1 Vererbung

107

}; //--------------------------------------------------------- Klasse CRechner class CRechner { public: CRechner() : m_xReg(&m_yReg), m_anzeige(&m_xReg, &m_yReg) { m_op = new CIst(&m_xReg, &m_yReg); m_op->insert(new CDiv(&m_xReg, &m_yReg)); m_op->insert(new CMult(&m_xReg, &m_yReg)); m_op->insert(new CSub(&m_xReg, &m_yReg)); m_op->insert(new CAdd(&m_xReg, &m_yReg)); m_xReg.setOp(m_op); m_anzeige.setOp(m_op); } int exec(void); private: CYReg CXReg COperation CAnzeige CTastatur }; m_yReg; m_xReg; *m_op; m_anzeige; m_tastatur;

Programm 3.8 calculator.cpp: Implementierung zum Taschenrechner


#include "calculator.h"

//---------------------------------------------------------- CAnzeige::show void CAnzeige::show(void) { cout << "-----------------------------------------" << endl << " x: " << m_xReg->get() << " << " y: " << m_yReg->get()

aktiv: " << m_op->getAktiv() << endl

<< "-----------------------------------------" << endl; }

//------------------------------------------------------------ CXReg::taste void CXReg::taste(int z) { m_altZustand = m_zustand; switch (m_zustand) { case ausgangsZustand: if (isdigit(z) || z == .) m_zustand = zahlZustand; break; case zahlZustand: if (m_op->isOperator(z)) m_zustand = berechZustand; else if (toupper(z) == C) m_zustand = clearZustand; break;

107

108

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

case berechZustand: case korrigierZustand: if (isdigit(z) || z == .) { m_zustand = zahlZustand; m_yReg->set(get()); } else if (toupper(z) == C) m_zustand = clearZustand; else if (m_op->isOperator(z)) m_zustand = korrigierZustand; break; case clearZustand: if (isdigit(z) || z == .) m_zustand = zahlZustand; else if (toupper(z) == C) m_zustand = ausgangsZustand; break; } switch (m_zustand) { case ausgangsZustand: if (m_zustand != m_altZustand) { set(0); m_yReg->set(0); m_op->setInaktiv(); } break; case zahlZustand: if (m_zustand != m_altZustand) m_wert[0] = 0; sprintf(m_wert, "%s%c", m_wert, z); break; case berechZustand: m_op->rechne(z); break; case korrigierZustand: m_op->korrigiere(z); break; case clearZustand: if (m_zustand != m_altZustand) set(0); break; } } //------------------------------------------------------ COperation::insert void COperation::insert(COperation *op) { m_aktiv = false; op->m_next = m_next; op->m_xReg = m_xReg; op->m_yReg = m_yReg; m_next = op; }

108

3.1 Vererbung

109

//------------------------------------------------------ COperation::rechne void COperation::rechne(int z) { if (m_aktiv) m_xReg->set(berechne(m_yReg->get(), m_xReg->get())); m_aktiv = (m_zeich == z); if (m_next) m_next->rechne(z); } //-------------------------------------------------- COperation::setInaktiv void COperation::setInaktiv(void) m_aktiv = false; if (m_next) m_next->setInaktiv(); } //-------------------------------------------------- COperation::korrigiere void COperation::korrigiere(int z) { m_aktiv = (m_zeich == z); if (m_next) m_next->korrigiere(z); } //---------------------------------------------------------- CRechner::exec int CRechner::exec(void) { char zeich; while ( (zeich = m_tastatur.getNextTaste()) != e) { m_xReg.taste(zeich); cin.ignore(100, \n); m_anzeige.show(); } return 0; } int main(void) { CRechner rechner; return rechner.exec(); } //..... Leeren des Eingabepuffers {

3.1.3 Abstrakte Klassen


3.1.3.1 Dummy-Methoden in Basisklasse Man msste in Programm quadkreis.cpp die Methode flaeche() in der 4. Zeile als rein virtuelle Methode deklarieren:
4 virtual double flaeche() const = 0; // rein virtuelle Methode

3.1.3.2 Zoo mit zuflligen Tieren Die Klassenhierarchie der Tiere drngt sich anhand der Ausgabe fast auf: Alle Tiere haben einen Namen. Dann sagt das Tier, welchen Lebensraum es hat, und dann von welcher konkreten Art es ist. Es macht hier Sinn, fr den Lebensraum eine eigene Klassenebene vorzusehen, statt nur eines Strings in der Basisklasse, wie z. B. eine

109

110

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

Membervariable m_strLebensort. Denn hinter dem Lebensort wird wohl mehr stecken, als ihn nur bei einer Vorstellung zu erwhnen, z .B. eine Fortbewegungsart, die dann entsprechend implementiert und berladen werden knnte. Das Anlegen der Tiere kann mit Hilfe von Polymorphismus z. B. ber eine Methode create() geschehen. Es macht hier keinen Sinn, im Zoo ein allgemeines Tier anzulegen, sondern nur konkrete Tierarten wie Hund, Katze usw. Daher ist die Methode CTier::create() abstrakt und damit auch automatisch jede abgeleitete Klasse, die diese Methode nicht implementiert. Abbildung 3.5 zeigt ein mgliches Klassendiagramm zu diesen berlegungen. In dieser Abbildung wurde aus Platzgrnden die Methode create() in den konkreten Tierklassen ohne Parameter und Rckgabewert angegeben; diese haben natrlich entsprechend CTier::create() den folgenden Prototyp:
create(char *): CTier *

CTier
{abstrakt} m_name[0..50]: char setName(name: char*) vorstellen() create(name: char*): CTier* {abstrakt} Diskriminator = Lebensort

CLandtier
{abstrakt} vorstellen()

CWassertier
{abstrakt} vorstellen()

CLufttier
{abstrakt} vorstellen()

CHund
vorstellen() create()

CKatze
vorstellen() create()

CMaus
vorstellen() create()

CWal
vorstellen() create()

CHai
vorstellen() create()

CSchmetterling
vorstellen() create()

CBiene
vorstellen() create()

CVogel
vorstellen() create()

Abbildung 3.5: Mgliches Klassendiagramm zu den Tieren im Zoo

Programm 3.9 zoo.h: Headerdatei zu den Tieren im Zoo


#include <iostream> #include <cstring> using namespace std; //...................................................... CTier (Abstrakte Klasse) class CTier { public: CTier() { strcpy(m_name, ""); } virtual ~CTier() { } void setName(char *name) { strcpy(m_name, name); } virtual void vorstellen() { cout << "Hallo, mein Name ist " <<m_name <<endl; } virtual CTier* create(char *) = 0; // rein virtuelle Methode private: char m_name[50];

110

3.1 Vererbung

111

}; //..................................................................... CLandtier class CLandtier : public CTier { public: CLandtier() { } virtual ~CLandtier() { } virtual void vorstellen() { CTier::vorstellen(); cout << "Ich lebe auf dem Land" << endl; } }; //................................................................... CWassertier class CWassertier : public CTier { public: CWassertier() { } virtual ~CWassertier() { } virtual void vorstellen() { CTier::vorstellen(); cout << "Ich lebe im Wasser" << endl; } }; //..................................................................... CLufttier class CLufttier : public CTier { public: CLufttier() { } virtual ~CLufttier() { } virtual void vorstellen() { CTier::vorstellen(); cout << "Ich lebe in der Luft" << endl; } }; //................................................................. Makro SPEZIES // // // legt eine Klasse an, die von Land-, Wasser- oder Lufttier abgeleitet ist. Ueber den Konstruktor wird der Name eines speziellen Individuums festgelegt. \ \ \ public: cname(char *name) { CTier::setName(name); } /* Default-Konstruktor fr Kreier-Reprsentant */ cname() { } void vorstellen() { oberklasse::vorstellen(); cout << "Ich bin " << whoami << endl; } \ \ \ \ \ \ \ \

#define SPEZIES(cname,oberklasse,whoami) class cname : public oberklasse {

/* Kreier-Funktion: Forward Ownership! */ \ CTier* create(char *name) { return new cname(name); } \ }; #endif

111

112

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

Programm 3.10 zoo.cpp: Implementierung zu den Tieren im Zoo


#include <stdlib.h> #include <time.h> #include "zoo.h" //................................. Namensvorgaben (werden zufllig ausgewhlt) char *namen[] = { "Wotan", "Pluto", "Maya", "Lukas", "Flipper", "Tarzan", "Kucki", "Slappi", "Fury", "Moritz" }; const int namZahl = sizeof(namen) / sizeof(*namen); //..................................... Anlegen der einzelnen Spezies (Klassen) SPEZIES(CHund, CLandtier, "ein Hund")

SPEZIES(CKatze, CLandtier, "eine Katze") SPEZIES(CMaus, CLandtier, "eine Maus")

SPEZIES(CSchmetterling, CLufttier, "ein Schmetterling") SPEZIES(CBiene, SPEZIES(CVogel, CLufttier, "eine Biene") CLufttier, "ein Vogel")

SPEZIES(CWal, CWassertier, "ein Wal") SPEZIES(CHai, CWassertier, "ein Hai") //........................................................................ main int main(void) { int i, n;

srand(time(NULL)); //... Eine Clone-Station anlegen (Von jeder Sorte 1 Reprsentant) CTier* spezies[] = { (new CHund), (new CKatze), (new CMaus), (new CSchmetterling), (new CBiene), (new CVogel), (new CWal), (new CHai) const int anzSpezies = sizeof(spezies) / sizeof(*spezies); cout << "Wie viele Tiere sollen im virtuellen Zoo sein: "; cin >> n; cout << endl; };

CTier **zoo = new CTier*[n]; // Dynamisches Anlegen des Zoos for (i=0; i<n; i++) //... Zoo mit zuflligen Tieren fllen (mit Meth. create()) zoo[i] = spezies[rand()%anzSpezies]->create(namen[rand()%namZahl]);

cout << "Die Tiere aus dem Zoo stellen sich vor:" << endl; for (int i=0; i<n; i++) { cout << "............................. " << (i+1) << ". Tier" << endl; zoo[i]->vorstellen(); } for (i=0; i<n; i++) delete zoo[i]; //... Speicher wieder freigeben for (i=0; i<anzSpezies; i++) delete spezies[i]; }

112

3.1 Vererbung

113

3.1.4 Mehrfachvererbung und virtuelle Basisklassen


3.1.4.1 Ehe abgeleitet von Mensch ber Mann/Frau
Programm 3.11 ehe.cpp: Ehe, mehrfach abgeleitet von Mensch ber Mann/Frau
#include #include <iostream> <cstring>

using namespace std; class Mensch { public: Mensch(char *name, int alter) : m_Alter(alter) { strcpy(m_Name, name); } void gibNameAlterAus(void) { cout << m_Name << ": ist " << m_Alter << " Jahre alt und hat "; } private: char m_Name[50]; int }; class Frau : public Mensch { public: Frau(char *name, int alter, bool k) : Mensch(name, alter), m_Kinder(k) { } void fraudaten() { gibNameAlterAus(); cout << (m_Kinder ? "" : "keine ") << "Kinder" << endl; } private: bool m_Kinder; }; class Mann : public Mensch { public: Mann(char *name, int alter, bool b) : Mensch(name, alter), m_Bart(b) { } void manndaten() { gibNameAlterAus(); cout << (m_Bart ? "" : "k") << "einen Bart" << endl; } private: bool m_Bart; }; class Ehe : public Frau, public Mann { public: Ehe(char *fname, int falter, bool kinder, char *mname, int malter, bool bart, int j) : Frau(fname, falter, kinder), Mann(mname, malter, bart), m_Jahre (j) {} void ehedaten() { fraudaten(); manndaten(); cout << "Sie sind seit " << m_Jahre << " Jahre verheiratet" << endl; cout << "---------------------------------------------" << endl; } m_Alter;

113

114

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

private: int m_Jahre; }; int main(void) { Ehe e1("Vera", 35, true, "Hans", 39, false, 12), e2("Kerstin", 63, true, "Otto", 66, true, 34); e1.ehedaten(); e2.ehedaten(); }

3.1.4.2 Hai als eischfressender Fisch


Programm 3.12 hai.cpp: Hai als eischfressender Fisch
#include <iostream> #include <cstring> using namespace std;

class CTier { //.......................................................... CTier protected: char m_name[20]; }; class CHund : public CTier { //........................................... CHund protected: bool m_reinrassig; }; class CFisch : virtual public CTier { //................................. CFisch protected: bool m_salzwasser; }; class CFleischfresser : virtual public CTier { //............... CFleischfresser protected: bool m_saeugetier; }; class CPflanzenfresser : public CTier { //..................... CPflanzenfresser protected: bool m_kannfliegen; }; class CHai : public CFisch, public CFleischfresser { //.................... CHai public: CHai(char *tiername) { strcpy(m_name, tiername); } void printName(void) { cout << m_name << endl; } }; int main(void) { //........................................................ main CHai h1("Killer"), h2("Torpedo"); h1.printName(); h2.printName(); }

114

3.2 berladen von Operatoren

115

3.2 berladen von Operatoren


3.2.1 Ein Uhrzeit-Rechner
Programm 3.13 uhrzeit.cpp: Klasse CUhrzeit fr den Uhrzeit-Rechner
class CUhrzeit { public: CUhrzeit(int std=0, int min=0, int sek=0) : m_sek(std*3600 + min*60 + sek) { } CUhrzeit& operator+= (const CUhrzeit& uz2) { m_sek += uz2.m_sek; return *this; } CUhrzeit& operator-= (const CUhrzeit& uz2) { m_sek -= uz2.m_sek; return *this; } const CUhrzeit operator+ (const CUhrzeit& uz2) const { return CUhrzeit(0, 0, m_sek + uz2.m_sek); } const CUhrzeit operator- (const CUhrzeit& uz2) const { return CUhrzeit(0, 0, m_sek - uz2.m_sek); } int getSek(void) const { return m_sek; } private: int m_sek; }; ostream &operator<<(ostream &os, const CUhrzeit &z) { return os << setfill(0) << setw(2) << (z.getSek() / 3600) << ":" << setw(2) << ":" << setw(2) } << (z.getSek() % 3600 / 60) << (z.getSek() % 3600 % 60) << endl; // interne Verwaltung in Sekunden (spart Fallunterscheidungen!)

3.2.2 Bruchrechner mit berladenen Operatoren


Programm 3.14 bruchop.h: Schnittstelle fr die Klasse CBruch
class CBruch { public: CBruch(long z=0, long n=1) { setBruch(z, n); } CBruch(const CBruch& b) : m_zaehler(b.m_zaehler), m_nenner(b.m_nenner) { } ~CBruch() {} void setBruch(long z=0, long n=1) { m_zaehler = z; m_nenner = (n==0)? 1 : n;} //_____________________________________________ berladene Elementoperatoren CBruch& operator= (const CBruch& b); //.................... Zuweisungsoperator //............................................................... Vorzeichen const CBruch operator- () const { return CBruch(-m_zaehler, m_nenner); }

115

116

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

const CBruch& operator+ () const { return *this; } //............................................................... Additionen const CBruch const CBruch operator+ operator+ (const CBruch& b) const; (const int& z) const { return *this + { return *this = CBruch(z);}

*this + b;} { return *this += CBruch(z);} //............................................................ Subtraktionen CBruch& operator+= (const int& z) const CBruch const CBruch operatoroperator(const CBruch& b) const { return *this + (const int& z) const { return *this + -b; -z; } }

CBruch& operator+= (const CBruch& b)

{ return *this = *this - b; } CBruch& operator-= (const int& z) { return *this = *this - z; } //......................................................... Multiplikationen const CBruch const CBruch operator* operator* (const CBruch& b) const; (const int& z) const { return *this * CBruch(z); } { return *this = *this * b; } { return *this = *this * z; }

CBruch& operator-= (const CBruch& b)

CBruch& operator*= (const CBruch& b)

CBruch& operator*= (const int& z) //............................................................... Divisionen const CBruch const CBruch operator/ operator/ (const CBruch& b) const { return *this*kehrBruch(b);} (const int& z) const { return *this*CBruch(1,z); }

{ return *this = *this / b; } { return *this = *this / z; } //................................................ Prfix/Postfix-Operatoren CBruch& operator/= (const int& z) const CBruch& operator++ () const CBruch& operator-- () const CBruch const CBruch { m_zaehler += m_nenner; return *this; { m_zaehler -= m_nenner; return *this; } } } }

CBruch& operator/= (const CBruch& b)

operator++ (int) { CBruch b = *this; operator++(); return b; operator-- (int) { CBruch b = *this; operator--(); return b;

//............................................................... Vergleiche bool CBruch::operator< (const CBruch& b) const { return (float(m_zaehler) / m_nenner) < (float(b.m_zaehler) / b.m_nenner); } bool CBruch::operator> (const CBruch& b) const { return (float(m_zaehler) / m_nenner) > (float(b.m_zaehler) / b.m_nenner); } bool operator== (const CBruch& b) const { return !(*this<b || *this>b); } bool operator!= (const CBruch& b) const { return !(*this == b); } bool operator<= (const CBruch& b) const { return (*this<b || *this==b); } bool operator>= (const CBruch& b) const { return (*this>b || *this==b); } //________________________________________________ Globale Friend-Operatoren friend const CBruch operator+ (int zahl, const CBruch& b); friend const CBruch operator- (int zahl, const CBruch& b); friend const CBruch operator* (int zahl, const CBruch& b); friend const CBruch operator/ (int zahl, const CBruch& b); private: long m_zaehler, m_nenner; int ggT(int n, int m) { return (m==0) ? n : ggT(m, n%m); } void kuerzen(void); const CBruch kehrBruch(const CBruch& b) const { return CBruch(b.m_nenner, b.m_zaehler ); } };

116

3.2 berladen von Operatoren

117

Programm 3.15 bruchop.cpp: Implementierung der Klasse CBruch


#include "bruchop.h" CBruch& CBruch::operator= (const CBruch& b) { m_zaehler = b.m_zaehler; m_nenner = b.m_nenner;

kuerzen(); return *this; } const CBruch CBruch::operator+ (const CBruch& b) const { return CBruch( m_zaehler * b.m_nenner + b.m_zaehler * m_nenner, m_nenner * b.m_nenner ); } const CBruch CBruch::operator* (const CBruch& b) const { return CBruch( m_zaehler * b.m_zaehler, m_nenner * b.m_nenner ); } void CBruch::kuerzen(void) { int ggTeiler = ggT(m_zaehler, m_nenner); m_zaehler /= ggTeiler; m_nenner /= ggTeiler;

if (m_nenner < 0) { m_zaehler = -m_zaehler; m_nenner } } //..................................................... Globale Friend-Operatoren const CBruch operator+ (int zahl, const CBruch& b) { return CBruch(zahl) + b; } const CBruch operator- (int zahl, const CBruch& b) { return CBruch(zahl) - b; } const CBruch operator* (int zahl, const CBruch& b) { return CBruch(zahl) * b; } const CBruch operator/ (int zahl, const CBruch& b) { return CBruch(zahl) / b; } = -m_nenner;

3.2.3 Arrayrechner mit berladenen Operatoren


Programm 3.16 arrayop.h: Die Klasse CArray
#ifndef ARRAYOP_H #define ARRAYOP_H #include <iostream> #include <iomanip> using namespace std;

#define BINOP_METHODE(op) \ CArray temp(m_size); \

unsigned groesse = (m_size > rechts.m_size) ? rechts.m_size : m_size; \ for (unsigned i=0; i<groesse; i++) temp[i] = m_array[i] op rechts.m_array[i]; return temp; \ \

117

118

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

class CArray { public: //............................................. Konstruktoren und Destruktoren CArray(unsigned size = 10, double wert = 0) : m_size(size) { m_array = new double[m_size]; for (unsigned i=0; i < m_size; i++) m_array[i] = wert; } CArray(const CArray& other) { copy(other); } //...... Kopierkonstruktor ~CArray() { delete m_array; m_size = 0; m_array = 0; } //... Destruktor

//............................................. berladen des Indexoperators [] double& operator[](unsigned index) { if (index < 0 || index >= m_size) { cerr << " exit(1); } return m_array[index]; } //............................. const-Memberfunktionen, da keine Vernderungen const CArray operator+ (const CArray& rechts) const { BINOP_METHODE(+) } const CArray operator- (const CArray& rechts) const { BINOP_METHODE(-) } const CArray operator* (const CArray& rechts) const { BINOP_METHODE(*) } const CArray operator/ (const CArray& rechts) const { CArray temp(m_size); ----> Ungltiger Index: " << index << endl;

unsigned groesse = (m_size > rechts.m_size) ? rechts.m_size : m_size; for (unsigned i=0; i<groesse; i++) if (rechts.m_array[i] == 0) { cerr << "Division durch 0" << endl; temp[i] = 0; } else temp[i] = m_array[i] / rechts.m_array[i]; return temp; } const CArray operator+ (double z) const { return *this + CArray(m_size, z); } const CArray operator- (double z) const { return *this - CArray(m_size, z); } const CArray operator* (double z) const { return *this * CArray(m_size, z); } const CArray operator/ (double z) const { return *this / CArray(m_size, z); } //....................................................... Zuweisungsoperatoren CArray& operator=(const CArray& rechts) { if (this != &rechts) { delete m_array; copy(rechts); } return *this; } CArray& operator+= (const CArray& rechts) { return *this = *this + rechts; } CArray& operator-= (const CArray& rechts) { return *this = *this - rechts; } CArray& operator*= (const CArray& rechts) { return *this = *this * rechts; } CArray& operator/= (const CArray& rechts) { return *this = *this / rechts; }

118

3.2 berladen von Operatoren

119

CArray& operator+= (double z) { return *this = *this + CArray(m_size, z); CArray& operator-= (double z) { return *this = *this - CArray(m_size, z); CArray& operator*= (double z) { return *this = *this * CArray(m_size, z); CArray& operator/= (double z) { return *this = *this / CArray(m_size, z);

} } } }

//........................................................... Unre Operatoren const CArray& operator+ () const { return *this; /* Vorz. + keine Wirkung */ } const CArray operator- () const { CArray temp(m_size); for (unsigned i=0; i < m_size; i++) temp.m_array[i] = -m_array[i]; return temp; } //....................................................... Vergleichsoperatoren bool operator== (const CArray& rechts) const { if (m_size != rechts.m_size) return false; for (unsigned i=0; i < m_size; i++) if (m_array[i] != rechts.m_array[i]) return false; return true; } bool operator!= (const CArray& rechts) const { return !(*this == rechts); } //.................................................. globale friend-Funktionen friend const CArray operator+ (double z, const CArray& r) { return CArray(r.m_size, z) + r; } friend const CArray operator- (double z, const CArray& r) { return CArray(r.m_size, z) - r; } friend const CArray operator* (double z, const CArray& r) { return CArray(r.m_size, z) * r; } friend const CArray operator/ (double z, const CArray& r) { return CArray(r.m_size, z) / r; } //................................................... weitere Memberfunktionen unsigned getLen(void) const { return m_size; }

friend ostream& operator<<(ostream& os, const CArray& a) { os << setiosflags(ios::fixed); for (unsigned i=0; i < a.m_size; i++) os << setw(5) << setprecision(2) << a.m_array[i] << ", "; os << endl; return os; } private: unsigned m_size; double *m_array; void copy(const CArray& other) { m_size = other.m_size;

m_array = new double[m_size]; memcpy(m_array, other.m_array, m_size*sizeof(double)); } }; #endif

119

120

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.2.4 Rechner fr Dezimal- und Dualzahlen


Programm 3.17 zeigt eine Lsung zu dieser Aufgabenstellung.
Programm 3.17 dualrech.cpp: Rechner fr Dezimal- und Dualzahlen
//------------------------------------------------------------------------ CDual class CDual { friend ostream& operator<<(ostream& os, const CDual& d) { return os << d.m_dual << " (" << d.m_dezi << ")" << endl; }

public: CDual(const CDual& d) CDual(const long w=0) { *this = d; { weiseZu(w); } }

CDual(const char *str) { weiseZu( strlen(str)<=n ? strtol(str,NULL,2) : 0); } CDual& operator= (const CDual& d) CDual& operator= (const long w) { weiseZu(d.m_dezi); return *this; } { return *this = CDual(w); } CDual& operator= (const char *str) { return *this = CDual(str); } CDual& operator+= (const CDual& d) CDual& operator+= (const long w) { weiseZu(m_dezi+d.m_dezi); return *this; } } }

{ return *this += CDual(w); CDual& operator+= (const char *str) { return *this += CDual(str); CDual operator+ (const CDual& d) CDual operator+ (const long w)

{ CDual ret = *this; return ret += d; } { return *this + CDual(w); } CDual operator+ (const char *str) { return *this + CDual(str); } operator long() const { return m_dezi; }

private: char m_dual[n+1]; long m_dezi;

void weiseZu(long w) { m_dezi = w; m_dual[n] = 0; for (unsigned i=0; i<n; i++) m_dual[n-i-1] = ((w >> i) & 1) + 0; } }; //------------------------------------------------------------------------- main int main(void) { ....... }

120

3.2 berladen von Operatoren

121

3.2.5 Implizites Casting


Programm castueb.cpp liefert folgende, wahrscheinlich unerwnschte Ausgabe:
....Leerer String (120) Hans (4) Emilia (6) Michaela (8)

In der Zeile 42:


42 CStr s1 = x,

Es war sicherlich erwnscht, dass man einen String mit dem Zeichen x initialisiert. Statt dessen wird aber der ASCII-Wert 120 der char-Konstante x verwendet, und der Konstruktor aus Zeile 13 aufgerufen. Dieser Konstruktor reserviert Speicherplatz fr einen String mit 120 Zeichen, was sicherlich nicht erwnscht war. Aufgrund des Fehlens eines geeigneten Konstruktors ndet folgendes implizite Casting statt:
s1 = CStr(x) ---> s1 = CStr(int(x)) ---> s1 = CStr(120)

Fgt man in der Klasse CStr noch folgenden Konstruktor ein:


CStr(char z) : m_len(1), m_str(new char[2]), m_gesetzt(true) { m_str[0] = z; m_str[1] = 0; }

dann wrde Programm castueb.cpp die erwartete Ausgabe liefern:


x (1) Hans (4) Emilia (6) Michaela (8)

3.2.6 Realisierung von Mengenoperationen ber ein Array


Programm 3.18 mengenarray.h: Schnittstelle fr die Klasse CMengArray
class CMenge { public: CMenge() { m_max = 0; } } } }

CMenge(const CMenge& a) { *this = a; CMenge(const int& z) { *this = z; int getMax(void) const { return m_max;

int getElement(int i) const { return m_array[i]; } //...................................................... Zuweisungsoperatoren CMenge& operator= (const CMenge& a); CMenge& operator= (const int& z);

121

122

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

//............................................................... Vereinigung const CMenge const CMenge operator+ operator+ (const CMenge& m) const; (const int& z) const { return *this + { return *this = CMenge(z); }

*this + m; } { return *this += CMenge(z); } //.............................................................. Durchschnitt CMenge& operator+= (const int& z) const CMenge const CMenge operator/ operator/ (const CMenge& m) const { return gleicheElemente(m); } (const int& z) const { return *this / { return *this = CMenge(z); }

CMenge& operator+= (const CMenge& m)

*this / m; } { return *this /= CMenge(z); } //................................................................. Restmenge CMenge& operator/= (const int& z) const CMenge operator(const CMenge& m) const { return entferneElemente(gleicheElemente(m)); } const CMenge operator(const int& z) const { return *this { return *this = CMenge(z); }

CMenge& operator/= (const CMenge& m)

*this - m; } { return *this -= CMenge(z); } //................................................................. Teilmenge CMenge& operator-= (const int& z) bool operator<=(const CMenge& m) const {return leer(*this-gleicheElemente(m));} bool operator<=(const int& z) const { return *this <= CMenge(z); } bool operator< (const CMenge& m) const { return (*this <= m) && (*this != m); } bool operator< (const int& z) const { return *this < CMenge(z); } //................................................................ Gleichheit

CMenge& operator-= (const CMenge& m)

bool operator==(const CMenge& m) const; bool operator==(const int& z) const { return *this == CMenge(z); bool operator!=(const CMenge& m) const { return !(*this == m); bool operator!=(const int& z) } }

const { return *this != CMenge(z); } //................................................. Globale friend-Funktionen

friend CMenge operator+ (const int& z, const CMenge& m); friend CMenge operator/ (const int& z, const CMenge& m); friend CMenge operator- (const int& z, const CMenge& m); friend bool operator<=(const int& z, const CMenge& m); friend bool operator< (const int& z, const CMenge& m); friend bool operator==(const int& z, const CMenge& m); friend bool operator!=(const int& z, const CMenge& m); private: int int m_array[1000]; m_max;

//........................................................ private-Funktionen void sort(void); const CMenge gleicheElemente (const CMenge& m) const; const CMenge entferneElemente(const CMenge& m) const; void bool }; entferneElement(const int pos); leer(const CMenge& m) const { return m.m_max == 0; }

122

3.2 berladen von Operatoren

123

Programm 3.19 mengenarray.cpp: Implementierung der Klasse CMengArray


#include "mengenarray.h" CMenge& CMenge::operator= (const CMenge& m) { //............ Zuweisungsoperatoren m_max = m.m_max; for (int i=0; i<m.m_max; i++) m_array[i] = m.m_array[i]; return *this; } CMenge& CMenge::operator= (const int& z) { m_max = 1; m_array[0] = z; return *this; } const CMenge CMenge::operator+ (const CMenge& m) const { //.......... Vereinigung CMenge tmp = *this; for (int i=0; i<m.m_max; i++) tmp.m_array[tmp.m_max+i] = m.m_array[i]; tmp.m_max += m.m_max; tmp.sort(); return tmp; } bool CMenge::operator==(const CMenge& m) const { //................... Gleichheit if (m_max != m.m_max) return false; for (int i=0; i<m_max; i++) if (m_array[i] != m.m_array[i]) return false; return true; } const CMenge CMenge::gleicheElemente(const CMenge& m) const { // gleicheElemente CMenge tmp = *this, gleich; for (int i=0; i<m.m_max; i++) for (int j=0; j<tmp.m_max; j++) if (m.m_array[i] == tmp.m_array[j]) { gleich += tmp.m_array[j]; tmp.entferneElement(j); } gleich.sort(); return gleich; } const CMenge CMenge::entferneElemente(const CMenge& m) const { //entferneElemente CMenge tmp = *this; for (int i=0; i<m.m_max; i++) for (int j=0; j<tmp.m_max; j++) if (m.m_array[i] == tmp.m_array[j]) tmp.entferneElement(j); tmp.sort(); return tmp;

123

124

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

} void CMenge::entferneElement(const int pos) { //................. entferneElement for (int i=pos; i<m_max-1; i++) m_array[i] = m_array[i+1]; m_max--; } void CMenge::sort(void) { //................................................ sort for (int i=0; i < m_max-1; i++) for (int j=i+1; j < m_max; j++) if (m_array[i] > m_array[j]) { int h = m_array[i]; m_array[i] = m_array[j]; m_array[j] = h; } } //..................................................... Globale friend-Funktionen CMenge operator+ (const int& z, const CMenge& m) { return CMenge(z) + m; CMenge operator/ (const int& z, const CMenge& m) { return CMenge(z) / m; CMenge operator- (const int& z, const CMenge& m) { return CMenge(z) - m; bool bool bool } } }

operator<=(const int& z, const CMenge& m) { return CMenge(z) <= m; } operator< (const int& z, const CMenge& m) { return CMenge(z) < m; }

operator==(const int& z, const CMenge& m) { return CMenge(z) == m; }

3.2.7 Matrizen durch berladen des Funktionsoperators


Programm 3.20 matrixfunk.h: Schnittstelle fr die Klasse CMatrix
#include #include <iostream> <iomanip>

using namespace std;

class CMatrix { public: CMatrix(unsigned zeilen, unsigned spalten);

unsigned getZeilen(void)

const { return m_zeilen;

unsigned getSpalten(void) const { return m_spalten; }

double& operator() (unsigned zeile, unsigned spalte); double& operator() (unsigned zeile, unsigned spalte) const; //... CMatrix& operator+= (const CMatrix& m); //........Additionen const CMatrix // ... CMatrix& operator-= (const CMatrix& m); //.....Subtraktionen const CMatrix // ... ~CMatrix(); // Destruktor operator(const CMatrix& m) const; operator+ (const CMatrix& m) const;

124

3.2 berladen von Operatoren

125

CMatrix(const CMatrix& m); CMatrix& operator= (const CMatrix& m); // ... void print(void) const; private: unsigned m_zeilen, m_spalten; double* }; m_matrix;

// Copy-Konstruktor // Zuweisungsoperator

extern ostream& operator<<(ostream& os, const CMatrix& m); #endif

Programm 3.21 matrixfunk.cpp: Implementierung der Klasse CMatrix


#include "matrixfunk.h" //................................................................ Konstruktor CMatrix::CMatrix(unsigned zeilen, unsigned spalten) : m_zeilen (zeilen), m_spalten (spalten), m_matrix (new double[zeilen * spalten]) { if (zeilen == 0 || spalten == 0) { cerr << "Matrix-Konstruktor hat Grsse 0" << endl; exit(1); } } //............................................. berladene Funktionsoperatoren inline double& CMatrix::operator() (unsigned zeile, unsigned spalte) { if (zeile > m_zeilen || spalte > m_spalten) { cerr << "Ungltiger Index bei Zugriff auf Matrix" << endl; exit(1); } return m_matrix[m_spalten*(zeile-1) + spalte-1]; } inline double& CMatrix::operator() (unsigned zeile, unsigned spalte) const { if (zeile > m_zeilen || spalte > m_spalten) { cerr << "Ungltiger Index bei Zugriff auf Matrix" << endl; exit(1); } return m_matrix[m_spalten*(zeile-1) + spalte-1]; } //............................................. berladene Additionsoperatoren CMatrix& CMatrix::operator+= (const CMatrix& m) { if (m_zeilen != m.m_zeilen || m_spalten != m.m_spalten) { cerr << "Bei Addition keine gleichen Zeilen bzw. Spalten" << endl; exit(1); } for (unsigned i=1; i<=m_zeilen; i++) for (unsigned j=1; j<=m_spalten; j++) (*this)(i,j) += m(i,j); return *this;

125

126

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

} const CMatrix CMatrix::operator+ (const CMatrix& m) const { CMatrix ret = *this; return ret += m; } //.......................................... berladene Subtraktionsoperatoren CMatrix& CMatrix::operator-= (const CMatrix& m) { if (m_zeilen != m.m_zeilen || m_spalten != m.m_spalten) { cerr << "Bei Subtraktion keine gleichen Zeilen bzw. Spalten" << endl; exit(1); } for (unsigned i=1; i<=m_zeilen; i++) for (unsigned j=1; j<=m_spalten; j++) (*this)(i,j) -= m(i,j); return *this; } const CMatrix CMatrix::operator- (const CMatrix& m) const { CMatrix ret = *this; return ret -= m; } //................................................................. Destruktor inline CMatrix::~CMatrix() { delete[] m_matrix; } //........................................................... Copy-Konstruktor inline CMatrix::CMatrix(const CMatrix& m) { m_matrix = 0; // Membervariablen initialisieren

*this = m; // Aufruf des Zuweisungsoperators }; //......................................................... Zuweisungsoperator inline CMatrix& CMatrix::operator= (const CMatrix& m) { if (m_matrix) delete[] m_matrix; if (m.m_matrix) { // m ist nicht leer

if ( (m_matrix = new double[m.m_zeilen * m.m_spalten]) ) { m_zeilen = m.m_zeilen; m_spalten = m.m_spalten; for (unsigned i=1; i<=m_zeilen; i++) for (unsigned j=1; j<=m_spalten; j++) (*this)(i,j) = m(i,j); } else { cerr << "Speicherplatzmangel" << endl; exit(1); } } else m_matrix = 0; // m ist ein leeres CMatrix-Objekt

return *this; }

126

3.2 berladen von Operatoren

127

//......................................................... Ausgabeoperator << ostream &operator<<(ostream& os, const CMatrix& m) { for (unsigned i=1; i<=m.getZeilen(); i++) { for (unsigned j=1; j<=m.getSpalten(); j++) os << setiosflags(ios::fixed) << setprecision(2) << setw(6) << m(i,j) << " os << endl; } return os; } ";

3.2.8 Array mit negativen und nicht ganzzahligen Indizes


Programm 3.22 zeigt eine mgliche Realisierung der Klasse CArray.
Programm 3.22 negativindex.cpp: Klasse CArray mit negativen und nicht ganzzahligen Indizes
class CArray { public: CArray(double schrittweite) { m_len = int( 2*PI/schrittweite + 1); m_wert = new double[m_len]; m_schrittweite = schrittweite; } double& operator[] (double index) { if (index < -PI || index > PI) { cerr << "Nur Indizes erlaubt von -PI bis +PI" << endl; exit(1); } int i = int((index + PI) * (1/m_schrittweite) ); return m_wert[i]; } friend ostream &operator<<(ostream &os, const CArray &a) { double max, min; max = min = a.m_wert[0]; for (int i=1; i<a.m_len; i++) { if (a.m_wert[i] > max) max = a.m_wert[i]; if (a.m_wert[i] < min) min = a.m_wert[i]; } for (int i=0; i<a.m_len; i++) os << setw(int((a.m_wert[i]*2/(max-min) + 1)*35)) << " *" << endl; return os; } private: int m_len;

double *m_wert, m_schrittweite; };

127

128

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.2.9 Rechner fr sehr grosse Zahlen


Programm 3.23 rechgross.h: Schnittstelle fr die Klasse CZahl
#include <cstring> #define maxStellen 10000 class CZahl { public: CZahl(const char *wert = "0") { m_stellen = strlen(wert); memset(m_zahl, 0, maxStellen); for (int i=m_stellen-1, j=0; i>=0; i--, j++) m_zahl[i] = wert[j]-0; } ~CZahl() {} const char *getZahl(void) const { return m_zahl; } int getStellen(void) const { return m_stellen; } //.............................................................. Additionen const CZahl const CZahl operator+ operator+ (const CZahl& z) const; // Zahlen bis 10000 Stellen mglich

(const char*& w) const { return *this + CZahl(w); } CZahl& operator+= (const CZahl& z) { return *this = *this + z; } CZahl& operator+= (const char*& w) { return *this += CZahl(w); } operatoroperator(const CZahl& z) const;

//........................................................... Subtraktionen const CZahl const CZahl

(const char*& w) const { return *this - w; } CZahl& operator-= (const CZahl& z) { return *this = *this - z; } CZahl& operator-= (const char*& w) { return *this = *this - w; } //........................................................ Multiplikationen operator* operator* (const CZahl& z) const;

const CZahl const CZahl

(const char*& w) const { return *this * w; } CZahl& operator*= (const CZahl& z) { return *this = *this * z; } CZahl& operator*= (const char*& w) { return *this = *this * w; } //............................................... Prfix/Postfix-Operatoren { return *this += "1"; { return *this += "1"; } } } }

const CZahl& operator++ () const CZahl& operator-- () const CZahl const CZahl

operator++ (int) { CZahl z = *this; operator++(); return z; operator-- (int) { CZahl z = *this; operator--(); return z; (const CZahl& z) const; (const CZahl& z) const;

//.............................................................. Vergleiche bool operator< bool operator>

bool operator== (const CZahl& z) const { return !(*this<z || *this>z); bool operator!= (const CZahl& z) const { return !(*this == z); bool operator<= (const CZahl& z) const { return (*this<z || *this==z); bool operator>= (const CZahl& z) const { return (*this>z || *this==z); bool operator< (const char*& w) const { return *this < CZahl(w); bool operator> (const char*& w) const { return *this > CZahl(w); bool operator== (const char*& w) const { return *this == CZahl(w); bool operator!= (const char*& w) const { return *this != CZahl(w); bool operator<= (const char*& w) const { return *this <= CZahl(w); bool operator>= (const char*& w) const { return *this >= CZahl(w);

} } } } } } } } } }

128

3.2 berladen von Operatoren

129

//............................................... Globale Friend-Operatoren friend const CZahl operator+ (const char*& w, const CZahl& z); friend const CZahl operator- (const char*& w, const CZahl& z); friend const CZahl operator* (const char*& w, const CZahl& z); private: char m_zahl[maxStellen]; int m_stellen;

CZahl& schiebeRechts(int s); };

Programm 3.24 rechgross.cpp: Implementierung der Klasse CZahl


#include "rechgross.h"

//..................................................................... Addition const CZahl CZahl::operator+ (const CZahl& z) const { CZahl tmp; int ueberLauf=0, anzahl;

anzahl = (z.m_stellen < m_stellen) ? m_stellen : z.m_stellen; for (int i=0; i<=anzahl; i++) { int wert = m_zahl[i] + z.m_zahl[i] + ueberLauf; tmp.m_zahl[i] = wert % 10; ueberLauf = wert / 10; } tmp.m_stellen = (tmp.m_zahl[anzahl]) ? anzahl+1 : anzahl; return tmp; }

//.................................................................. Subtraktion const CZahl CZahl::operator- (const CZahl& z) const { CZahl tmp; int ueberLauf=0, anzahl;

anzahl = (z.m_stellen < m_stellen) ? m_stellen : z.m_stellen; for (int i=0; i<=anzahl; i++) { int wert = m_zahl[i] - (z.m_zahl[i] + ueberLauf); tmp.m_zahl[i] = (wert < 0) ? 10+wert : wert; ueberLauf = (wert < 0) ? 1 : 0; } tmp.m_stellen = anzahl; while (!tmp.m_zahl[tmp.m_stellen-1]) tmp.m_stellen--; return tmp; }

129

130

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

//............................................................... Multiplikation const CZahl CZahl::operator* (const CZahl& z) const { CZahl ergeb("0"); for (int i=m_stellen-1; i>=0; i--) { unsigned zaehler = m_zahl[i]; CZahl zwischenZahl("0"); while (zaehler > 0) { zwischenZahl += z; zaehler--; } ergeb.schiebeRechts(1); ergeb += zwischenZahl; } return ergeb; } //................................................................... Vergleiche bool CZahl::operator< (const CZahl& z) const { int anzahl = (z.m_stellen < m_stellen) ? m_stellen : z.m_stellen; for (int i=anzahl-1; i>=0; i--) if (m_zahl[i] - z.m_zahl[i]) return m_zahl[i] < z.m_zahl[i]; return false; } bool CZahl::operator> (const CZahl& z) const { int anzahl = (z.m_stellen < m_stellen) ? m_stellen : z.m_stellen; for (int i=anzahl-1; i>=0; i--) if (m_zahl[i] - z.m_zahl[i]) return m_zahl[i] > z.m_zahl[i]; return false; }

//................................................................ schiebeRechts CZahl& CZahl::schiebeRechts(int s) { int i; for (i=m_stellen-1; i>=0; i--) m_zahl[i+s] = m_zahl[i]; for (i=0; i<s; i++) m_zahl[i] = 0; return *this; }

//.................................................... Globale Friend-Operatoren CZahl const operator+ (const char*& w, const CZahl& z) { return CZahl(w) + z; } CZahl const operator- (const char*& w, const CZahl& z) { return CZahl(w) - z; } CZahl const operator* (const char*& w, const CZahl& z) { return CZahl(w) * z; }

130

3.3 Templates

131

3.3 Templates
3.3.1 Funktionstemplate zum Vertauschen von zwei Objekten
Programm 3.25 swaptempl.cpp: Funktionstemplate zum Vertauschen von zwei Objekten
..... //............................................................... CBruch-Klasse class CBruch { public: CBruch(long z=0, long n=1) : m_zaehler(z), m_nenner(n) { } void print(void) { cout << "(" << m_zaehler << "," << m_nenner << ") "; } CBruch& operator= (const CBruch& b) { m_zaehler = b.m_zaehler; m_nenner = b.m_nenner;

return *this; } private: long m_zaehler, m_nenner; };

//.................................................... Funktionstemplate tausch template <typename T> void tausch (T& a, T& b) { T temp = a; a b } = b; = temp;

//........................................................................ main int main( void ) { .... }

3.3.2 Unterschiedliche Klassen zu einem Klassentemplate


Programm templausgabe.cpp gibt Folgendes aus:

10, a, 15.4, Text 22, b, 42.1, Neuer Text

131

132

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.3.3 Ein Stack-Klassentemplate


Programm 3.26 stacktempl.cpp: Ein Stack-Klassentemplate
.... //..................................................... Stack-Klassentemplate template <typename T, int max = 5> class Stack { public: Stack() : count(0) { } bool push(const T &); bool pop(T &); private: T arr[max];

int count; }; template <typename T, int max> bool Stack<T,max>::push(const T &data) { if (count < max) { arr[count++] = data; return true; } return false; } template <typename T, int max> bool Stack<T,max>::pop(T &data) { if (count > 0) { data = arr[--count]; return true; } return false; } //...................................................................... main int main(void) { .... }

132

3.4 Exceptions (Ausnahmebehandlung)

133

3.4 Exceptions (Ausnahmebehandlung)


3.4.1 Stack-Unwinding
Programm stackunwind.cpp liefert die folgende Ausgabe:

CKlasse-Konstruktor: ....CKlasse Objekt angelegt CKlasse-Destruktor: ....Fehler beim Oeffnen der Datei

....Anweisung nach try-catch-Block

3.4.2 Geschachtelte try-catch-Blcke


Programm exceptschachtel.cpp liefert die folgende Ausgabe:
...Innerer 1. try-Block ...Inneres string-catch: Ein Text Nach innerem try-Block ...Innerer 2. try-Block ...Inneres int-catch: 123 usseres catch: 123 aufgetreten

3.4.3 Eigene terminate()-Funktion


...try-Block ...Programmende wegen Fehler Abgebrochen [bedingt durch abort()-Aufruf ursprngl. terminate()-Funktion,] [da sich myTerminate() nicht mit exit() beendet]

3.4.4 Vordenierte Standard-Exceptions


Programm stdexcept.cpp liefert die folgende Ausgabe:

...Unerlaubter Index: 200 ...Speicherplatzmangel ...Laengen-Fehler und tschuess

3.4.5 Exception-Spezikationen
Programm unexpected.cpp liefert die folgende Ausgabe:

100 3.1415 1. unerwartete Exception abgefangen: 4711 2. unerwartete Exception abgefangen:

133

134

3 Lsungen zu Teil III (Vererbung, Operatoren, Templates, Exceptions)

3.4.6 Overow- und Underow-Exceptions in einem Stack


Programm 3.27 stackexcept.h: Stack-Klasse mit Overow- und Underow-Exceptions
#include <exception> #include <iostream> using namespace std;

//................................................................ Exceptions class StackOverflow {};

class StackUnderflow {};

//..................................................... Stack-Klassentemplate template <typename T> class Stack { public: Stack(int maxEntries) { m_Max = 0; m_Top = -1; try { m_Stack = new T[maxEntries]; m_Max } catch (bad_alloc& b) { // Abfangen vordef. bad_alloc cerr << "...Speicherplatzmangel" << endl; } } virtual ~Stack() { if(m_Stack) delete [] m_Stack; } void push(const T& data) { if (m_Top == m_Max) throw StackOverflow(); m_Stack[++m_Top] = data; } T& pop() { if (m_Top < 0) throw StackUnderflow(); return m_Stack[m_Top--]; } bool IsEmpty() { return (m_Top < 0); } = maxEntries-1; // Stack ist zunchst leer

private: T *m_Stack; int m_Max; int m_Top; };

134

Kapitel 4
Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)
4.1 Standard Template Library (STL)
4.1.1 Sortierte Namensliste
Da die Namen sortiert bentigt werden, bietet sich zur Verwaltung ein Container an, der seine Elemente sortiert hlt, also z. B die STL-Klasse set. Als TemplateParameter bietet sich dabei fr die Namen die STL-Klasse string an.
Programm 4.1 namsetsort.cpp: Sortierte Namensliste
#include <set> #include <string> #include <iostream> #include <algorithm> using namespace std;

void ausgabe(string s) { cout << s << endl; }

int main(void) { set<string> namen; string str;

cout << "Gib nun zeilenweise Namen ein (Abbruch mit EOF): " << endl; while (getline(cin, str) != 0) namen.insert(str);

cout << endl << "Die Namen sortiert sind:" << endl;

for_each(namen.begin(), namen.end(), ausgabe); }

135

136

4 Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)

4.1.2 Das Josephus-Spiel


Programm 4.2 josephus.cpp: Das Josephus-Spiel
#include <list> #include <iomanip> #include <iostream> using namespace std;

int main(void) { unsigned i, persZahl, nr; bool aufListeAnfang = true;

cout << "Wie viele Personen: "; cin >> persZahl;

cout << "Der wievielte soll immer ausgesondert werden: "; cin >> nr;

list<int> kreis; list<int>::iterator it;

for (i=1; i <= persZahl; i++) kreis.push_back(i);

cout << "In folgender Reihenfolge wird ausgesondert:" << endl; while (!kreis.empty()) { if (aufListeAnfang) it = kreis.begin(); for (i=1; i < nr; i++) if (++it == kreis.end()) it = kreis.begin(); cerr << setw(2) << *it << ", "; it = kreis.erase(it); aufListeAnfang = (it == kreis.end()); } cout << endl; }

4.1.3 Kubikzahlen ber Polyas Sieb


Programm 4.3 polya.cpp: Kubikzahlen ber Polyas Sieb
#include <queue> #include <iomanip> #include <iostream> using namespace std;

136

4.1 Standard Template Library (STL)

137

int main(void) { unsigned zahlen, z = 0, summe = 0;

cout << "Wie viele Zahlen: "; cin >> zahlen; queue<unsigned> queue1, queue2;

//.... Aufeinanderfolgende Zahlen in queue1 schieben for (unsigned i=1; i <= zahlen; i++) { queue1.push(i); cout << queue1.back() << ", "; } cout << endl; //.... Aus queue1 ausser jeden dritten die Zahlen in queue2 schieben while (!queue1.empty()) { if ( (z = ++z % 3) != 0) { queue2.push(queue1.front()); cout << queue2.back() << ", "; } queue1.pop(); } cout << endl; //.... Summenfolgen aus queue2 in queue1 schieben while (!queue2.empty()) { summe += queue2.front(); queue1.push(summe); cout << queue1.back() << ", "; queue2.pop(); } cout << endl; //.... Aus queue1 ausser jeden zweiten die Zahlen in queue2 schieben z = 0; while (!queue1.empty()) { if ( (z = ++z % 2) != 0) { queue2.push(queue1.front()); cout << queue2.back() << ", "; } queue1.pop(); } cout << endl; //.... Summenfolgen aus queue2 in queue1 schieben summe = 0; while (!queue2.empty()) { summe += queue2.front(); queue1.push(summe); cout << queue1.back() << ", "; queue2.pop(); } cout << endl; }

137

138

4 Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)

4.1.4 Print-Container
Durch das einheitliche Konzept bei den Containern (Iterator, gleiche Methodennamen fr gleiche Funktionalitt) ist es berhaupt erst mglich, eine (Betonung liegt auf eine) globale Ausgabe-Funktion fr die unterschiedlichen Container zu erstellen. Die Variabilitt ist der unterschiedliche Container und damit verbunden der zugehrige Iterator. Die globale Funktion PrintContainer() muss daher eine Templatefunktion sein, die als Template-Parameter den Containertyp hat. Wie an den Aufrufen in main() zu erkennen ist, wird der Template-Parameter als erster Parameter an die Funktion PrintContainer() bergeben. Der zweite Parameter ist vom Typ char* und legt die Trennzeichen bei der Ausgabe der einzelnen Container-Elemente fest. Wie man an der Ausgabe von main() sieht, ist der Defaultwert ein Leerzeichen.
Programm 4.4 PrintContainer.h: Eine allgemeine Funktion zum Ausgeben des Inhalts unterschiedlicher Container-Typen
#ifndef PRINTCONTAINER_H #define PRINTCONTAINER_H #include <iostream> using namespace std; template<class Container> void PrintContainer(Container& c, // auszugebender Container

const char* sep=" ") { // Trennzeichen zwischen Elementen typename Container::iterator it; for (it=c.begin(); it!=c.end(); it++) cout << *it << sep; cout << endl; } #endif

4.1.5 Umsatzberechnung (Maps in einer Map)


Programm 4.5 stlumsatz.cpp: Umsatzberechnung (Maps in einer Map)
#include <fstream> #include <map> #include <iomanip> #include <iostream> using namespace std; int main(int argc, char *argv[]) { string str; int double monat, jahr; betrag;

if (argc !=2) { cerr << "usage: " << argv[0] << " umsatzDatei" <<endl; exit(1); } ifstream datei(argv[1]); // Input-Stream auf Datei if (!datei) { cerr << "Kann Datei " << argv[1] << "nicht ffnen" << endl; exit(1); }

138

4.1 Standard Template Library (STL)

139

map<int, map<int, double> > jahrUmsatz; while (!datei.eof()) { if (datei >> monat) { datei.ignore(100, /); datei >> jahr; datei.ignore(100, :); datei >> betrag; datei.ignore(100, \n); jahrUmsatz[jahr][monat] += betrag; } } map<int, map<int, double> >::iterator jit; map<int, double>::iterator mit; cout << setiosflags(ios::fixed) << setprecision(2) << setiosflags(ios::right); for (jit = jahrUmsatz.begin(); jit != jahrUmsatz.end(); jit++) { double gesamt = 0; cout << ".................................." << jit->first << endl; for (mit = jahrUmsatz[jit->first].begin(); mit != jahrUmsatz[jit->first].end(); mit++) { cout << setw(12) << mit->first << ": " <<setw(12) << mit->second <<endl; gesamt += mit->second; } cout << setw(14) << "Gesamt: " << setw(12) << gesamt << endl; } }

4.1.6 Geburtstagsverwaltung
Fr einen Geburtstagskalender bietet sich zur Verwaltung eine Map an, denn es besteht eine Assoziation zwischen dem (heutigen) Tag und der Personen, die an diesem Tag Geburtstag haben. Da an einem Tag auch mehrere Personen Geburtstag haben knnen, bietet sich hier die STL-Klasse multimap an. Der Schlssel, nach dem gesucht werden kann, ist dabei ein CDatum-Objekt. Der Wert ist dann der Name der Person (vom Typ string).
Programm 4.6 IEnumGeb.h: Die Schnittstelle IEnumGeb
#ifndef IENUMGEB_H #define IENUMGEB_H #include "Datum.h"

class IEnumGeb { public: IEnumGeb() { }

virtual ~IEnumGeb() { } virtual void Enum(const }; #endif CDatum &day,const char * name)=0;

139

140

4 Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)

Programm 4.7 EnumGebCout.h: Eine Implementierung der Schnittstelle IEnumGeb


#ifndef ENUMGEBCOUT_H #define ENUMGEBCOUT_H #include <iostream> #include <iomanip> #include "IEnumGeb.h" #include "Datum.h" using namespace std; class CEnumGebCout : public IEnumGeb { public: CEnumGebCout() { }

virtual ~CEnumGebCout() { } virtual void Enum(const CDatum &day, const char * name) { if (day.GetTag() > 0) cout << setfill(0) << setw(2) << day.GetTag() << .

<< setw(2) << day.GetMonat() << . << setw(4) << day.GetJahr() << " } }; #endif " << name << endl;

Programm 4.8 Datum.h: Die Klasse CDatum zum Bearbeiten der Geburtsdaten
#ifndef DATUM_H #define DATUM_H #include <time.h> #include <iomanip> #include <iostream> using namespace std;

class CDatum { friend ostream& operator << (ostream& out, CDatum day) { if (day.m_tag > 0) out << setfill(0) << setw(2) << day.m_tag << << setw(2) << day.m_monat << << setw(4) << day.m_jahr; return out; } friend istream& operator >> (istream& in, CDatum& day) { in >> day.m_tag >> day.m_monat >> day.m_jahr; return in; } public: // Definitionen zum Kennzeichnen ungueltiger Attribute fuer Vergleich enum valid_e {val_tag=0x01, val_monat=0x02, val_jahr=0x04};

140

4.1 Standard Template Library (STL)

141

CDatum(int t=0, int m=0, int j=0, unsigned short valid = val_tag | val_monat | val_jahr) { m_tag = t;

m_monat = m; m_jahr = j;

m_valid = valid; } virtual ~CDatum() { } int GetTag() const { return m_tag; }

int GetMonat() const { return m_monat; } int GetJahr() const { return m_jahr; }

void SetHeute(unsigned short valid=val_tag | val_monat | val_jahr) { time_t zeit = time(NULL); tm heute = *localtime(&zeit); m_tag = heute.tm_mday; m_monat = heute.tm_mon+1; m_jahr // Zaehlung beginnt bei 0

= heute.tm_year+1900;

m_valid = valid; } CDatum operator++ (int) { const static int m_tagMax[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; // 1 2 3 4 5 6 7 8 9 10 11 12

CDatum tmp = *this; // alten Wert sichern int tagMax = m_tagMax[m_monat]; if (m_monat == 2 && schaltjahr(m_jahr) ) tagMax++; if (m_tag < tagMax) m_tag++; else if (m_tag == tagMax) { m_tag=1; if (m_monat != 12) m_monat++; else { // Silvester

m_monat=1; m_jahr++; } } return tmp; } bool operator< (const CDatum day) const { return cmp(day) > 0; }

bool operator== (const CDatum day) const { return cmp(day) == 0; } private: int m_tag, m_monat, m_jahr;

unsigned short m_valid; // Welche Attribute fuer Datum-Vergleich gueltig sind void Angleichen(const CDatum& d,const CDatum& neu) { *this = d; if (!(m_valid & val_jahr))

m_jahr

= neu.m_jahr;

if (!(m_valid & val_monat)) m_monat = neu.m_monat; if (!(m_valid & val_tag)) } m_tag = neu.m_tag;

141

142

4 Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)

int cmp(const CDatum &d) const { CDatum day, ThisDay; day.Angleichen(d,*this); ThisDay.Angleichen(*this, day); int erg=day.m_jahr-ThisDay.m_jahr; if (!erg) if ( (erg = day.m_monat-ThisDay.m_monat) == 0) erg=day.m_tag-ThisDay.m_tag; return erg; } bool schaltjahr(int j) { // Schaltjahr: alle 400 Jahre und alle 4 Jahre, aber alle 100 Jahre nicht return j%400==0 || (j%4==0 && j%100!=0); } }; #endif

Programm 4.9 Remind.h: Die Klasse CRemind zur Bearbeitung der Geburtstagsliste
#ifndef REMIND_H #define REMIND_H #include <map> #include <string> #include <cstring> #include <iostream> #include <iomanip> #include <fstream> #include "Datum.h" #include "IEnumGeb.h" using namespace std; class CRemind public: CRemind() { m_cFileName = "gebtag.txt"; // Datenbasis festlegen ifstream fin(m_cFileName); // Geburtstagsliste laden if (!fin) { cerr << "Kann "<< m_cFileName <<" nicht zum Lesen oeffnen" <<endl; exit(1); } m_next = 0; // falls Datei noch leer ist {

fin >> m_next; // Erster Eintrag in Datei: Anzahl der Tage while (!fin.eof()) { // Es folgt die eigentliche Geburtstagsliste CDatum gebtag(0, 0, 0, CDatum::val_tag | CDatum::val_monat); char name[80]; fin >> gebtag; fin.ignore(1000, :); // bis Doppelpkt alle Zeichen extrahieren fin.getline(name, 80); // Jetzt kommt der eigentliche Name

m_liste.insert(paar(gebtag, name)); }

142

4.1 Standard Template Library (STL)

143

} virtual ~CRemind() { ofstream fout(m_cFileName); // Geburtstagsliste speichern if (!fout) { cerr << "Kann "<< m_cFileName << " nicht zum Schreiben oeffnen" << endl; exit(1); } fout << m_next << endl; // Erster Eintrag in Datei: Anzahl der Tage multimap<CDatum, string>::iterator it; // Nun eigentliche Geburtstagsliste for (it=m_liste.begin(); it != m_liste.end(); it++) if (it->second.size() > 0) fout << it->first << " } void NeuEintrag(CDatum tag, string text) { m_liste.insert(paar(tag, text)); } void EnumAll(IEnumGeb *Geb) { multimap<CDatum,string>::iterator it; for (it=m_liste.begin(); it != m_liste.end(); it++) Geb->Enum(it->first, it->second.c_str()); } void EnumNext(IEnumGeb *Geb) { multimap<CDatum,string>::iterator it; CDatum day; day.SetHeute(CDatum::val_monat | CDatum::val_tag); for (int i=0; i < m_next; i++, day++) { it = m_liste.find(day); if (it != m_liste.end()) { Geb->Enum(it->first, it->second.c_str()); while ((++it)->first == day) Geb->Enum(it->first, it->second.c_str()); } } } void SetNext(int next) { m_next = next; } int GetNext() { return m_next; } :" << it->second << endl;

private: const char multimap<CDatum,string> int *m_cFileName; m_liste; m_next;

typedef pair<CDatum,string> paar; }; #endif

Programm 4.10 gebtag.cpp: Die main()-Funktion zur Geburtstagverwaltung


#include "Remind.h" #include "EnumGebCout.h" #include "Datum.h"

143

144

4 Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)

#include <iostream> using namespace std;

int main(void) { CRemind gebkal;

CEnumGebCout En; cout << "Geburtstage in den naechsten "<< gebkal.GetNext() << " Tagen: " << endl; gebkal.EnumNext(&En);

char wahl;

while(true) {

// endlos: Ende bei e << endl << endl << endl << endl << endl << endl;

cerr << endl << "Was wollen Sie tun? " << "e: Ende" << "l: Ganze Liste zeigen" << "x: Anzahl der vorausschauenden Tage setzen" << "n: neuen Eintrag in die Liste vornehmen" cerr << ">> "; cin >> wahl; switch(wahl) {

case e: cerr << "Und tschuess..." << endl; return 0; case l: cout << "Geburtstagsliste:" << endl; gebkal.EnumAll(&En); break; case x: { int tage; cerr << "Wieviele Tage vorausblicken?: "; cin >> tage; gebkal.SetNext(tage); cout << "Geburtstage in den naechsten " << gebkal.GetNext() << " Tagen: " << endl; gebkal.EnumNext(&En); break; } case n: { CDatum gebTag; cerr << "Geburtstag (tag monat jahr)?: "; cin >> gebTag; char name[80]; cerr << "Name: "; cin.ignore(); // Return von vorheriger Ausgabe entfernen cin.getline(name, 80); gebkal.NeuEintrag(gebTag, name); break; } default: cerr << "Ungueltige wahl!" << endl; break; } } } /* int main(void) { //............ Test der Klasse CDatum CDatum day(31,10,1971), day2(26,6,1971); //day.SetHeute();

144

4.1 Standard Template Library (STL)

145

for (int i=0; i < 365; i++, day++) cout << i << ": " << day << endl; cerr << "Geben Sie ein Datum ein (tag monat jahr): "; cin >> day2;

cout << day2 << " ist "; if (!(day2 < day)) cout << "nicht "; cout << "kleiner als " } */ << day << endl;

Anmerkungen: Da die Methode find() der Klasse multimap immer einen Iterator auf das erste vorkommende Objekt zurckliefert, muss selbst dafr gesorgt werden, dass auch alle weiteren Personen ausgegeben werden, die an diesem Tag Geburtstag haben. Es bietet sich daher an, dass die Map bei der Sortierung nur Tag und Monat bercksichtigt und das Jahr auen vor lsst. Der Operator < der Klasse CDatum muss also entsprechend implementiert sein. Da wir die Klasse CDatum mglichst allgemein gltig entwerfen wollten, haben wir dazu ein zustzliches Attribut m_valid eingefhrt, mit dem konguriert werden kann, welche Daten fr den Vergleich gltig sind, d. h. bercksichtigt werden. Ist die Liste nach Tag und Monat sortiert, knnen dann ab der mit find() gefundenen Position alle nachfolgenden Geburtstage mit dem Suchgeburtstag auf Gleichheit berprft werden. Dazu muss auch der Operator == berladen werden. Ebenso haben wir als Trennzeichen zwischen Geburtstag und Text einen Doppelpunkt verwendet, um zu vermeiden, dass die Trennzeichen selbst in den String mit eingelesen werden. Den Namen lesen wir mit cin.getline() ein, damit auch Leerzeichen im String mglich sind. Die Ausgabe der Daten haben wir ber die Schnittstelle IEnumGeb von der Klasse CRemind entkoppelt. Dies erhht die Wiederverwendbarkeit von CRemind, da nun jede Klasse mit CRemind zusammenarbeiten kann, die die Schnittstelle IEnumGeb implementiert. Damit knnte CRemind z. B. auch in GUI-Applikationen gut verwendet werden, oder es wre auch denkbar, die von CRemind gelieferten Daten z. B. ber das Internet zu verschicken, ohne dass die Klasse CRemind gendert werden muss.

145

146

4 Lsungen zu Teil IV (STL, RTTI, Namensrume, Memberzeiger)

4.2

Laufzeit-Typinformationen (RTTI)

4.2.1 Identizieren von Objekttypen


Programm typeid.cpp liefert die folgende Ausgabe:
Kreis, Kreis, Kreis,

Der Grund fr diese Ausgabe liegt an Zeile 17:


17 else if (typeid(g[i]) == typeid(r))

In dieser Zeile werden nmlich nicht die Objekte verglichen, auf die die beiden Zeiger zeigen, sondern die Zeiger selbst, und es gilt nun mal, dass GraphObj* verschieden von Rechteck* ist. Um die erwartete Ausgabe hier zu erhalten, muss man in Zeile 17 die beiden Objekte vergleichen, auf die die beiden Zeiger zeigen:
18 else if (typeid(*g[i]) == typeid(*r))

Dann wrde man folgende (wahrscheinlich erwartete) Ausgabe erhalten:


Kreis, Rechteck, Kreis, Rechteck, Kreis,

146

Kapitel 5
Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)
5.1 Entwurfsprinzipien
5.1.1 Lehrer-Kreide-Tafel
Wie Abbildung 5.1 zeigt, knnten alle drei Klassen abstrahiert werden und damit die Schreibbeziehung allgemein gltig formuliert werden.
schreibt auf

CSchreiber

schreibt mit

CSchreibwerkzeug

CAusgabeMedium

CLehrer

CKreide

CStift

CTafel

CPapier

Abbildung 5.1: Abstraktion der Schreibbeziehung zu Lehrer-Kreide-Tafel

Inwieweit eine solche Abstraktion wie in Abbildung 5.1 Sinn macht, hngt vom jeweiligen Anwendungsfall ab: Wird die dadurch entstehende Flexibilitt im konkreten Anwendungsfall berhaupt bentigt?

147

148

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

5.1.2 Realisierung der NLS-Untersttzung in C++


Programm 5.1 nlssupport.cpp: Realisierung der NLS-Untersttzung in C++
#include <string> #include <map> #include <iostream> using namespace std; //........................Fr folg. beiden Klassen keine nderung erforderlich class CNlsSprache { public: virtual string GetText(int ID) { return ""; } }; class CNlsSupport { public: CNlsSupport(CNlsSprache *spr) { sprache = spr; } virtual string GetText(int ID) { return sprache->GetText(ID); } // Delegation private: CNlsSprache *sprache; }; //....................Hier zwei Klassen (um weitere Sprachklassen erweiterbar) class CNlsDeutsch : public CNlsSprache { public: CNlsDeutsch() { m_texte[1] = "Ja"; m_texte[2] = "Nein"; m_texte[3] = "Wollen Sie das Programm wirklich beenden?"; m_texte[4] = "Wollen Sie die nderungen speichern?"; } virtual ~CNlsDeutsch() {}

virtual string GetText(int ID) { return m_texte[ID]; } private: map<int,string> m_texte; }; class CNlsEnglisch : public CNlsSprache { public: CNlsEnglisch() { m_texte[1] = "Yes"; m_texte[2] = "No"; m_texte[3] = "Do you really want to exit the program?"; m_texte[4] = "Do you want to save the changes?"; } virtual ~CNlsEnglisch() {} virtual string GetText(int ID) { return m_texte[ID]; } private: map<int,string> m_texte; };

enum { DEUTSCH, ENGLISCH };

148

5.1 Entwurfsprinzipien

149

//........................................................................main int main(void) { int CNlsDeutsch i; deutsch;

CNlsEnglisch englisch; CNlsSupport sprachen[] = { &deutsch, &englisch }; for (i=1; i <= 4; i++) cout << sprachen[DEUTSCH].GetText(i) << endl; cout << "-------------------------------------------------" << endl; for (i=1; i <= 4; i++) cout << sprachen[ENGLISCH].GetText(i) << endl; }

Programm 5.1 liefert die folgende Ausgabe:


Ja Nein Wollen Sie das Programm wirklich beenden? Wollen Sie die nderungen speichern? ------------------------------------------------Yes No Do you really want to exit the program? Do you want to save the changes?

5.1.3 Segelboot
Denkt man ber die Beziehung zwischen Antrieb und Fahrzeug nach, so stellt man fest, dass es sich hier um eine Aggregation handelt. Denn jedes Fahrzeug hat einen Antrieb. Die Beziehung zwischen Fahrweg und Fahrzeug ist sogar noch loser: Ein Fahrzeug bewegt sich auf einem Fahrweg. Wir kommen damit zu einem Klassendiagramm, wie es in Abbildung 5.2 gezeigt ist.
fhrt auf Fahrweg Fahrzeug Antrieb

WasserFahrweg

WindAntrieb

MotorAntrieb

Segelboot

Abbildung 5.2: Antrieb und Fahrweg bei Fahrzeugen (Segelboot)

149

150

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

5.1.4 Ente
Wrde man den Klassenentwurf von Zoo beibehalten, so msste die Ente mehrfach abgeleitet werden, denn sie ist sowohl ein Land- als auch ein Wasser- als auch ein Lufttier. Betrachtet man eine Ente bzw. Tiere genauer, so stellt man fest, dass das Schwimmen, Laufen etc. primr nicht vom Lebensraum abhngt, sondern vielmehr von der Fhigkeit des Tieres, verbunden mit dessen anatomischen Voraussetzungen. Natrlich wird es nichts bringen, wenn eine Ente an Land Schwimmbewegungen macht. Daher wird die erlernte Fhigkeit auch etwas mit dem Ort zu tun haben, an dem die Bewegung ausgefhrt wird. Das Klassendiagramm in Abbildung 5.3 skizziert unsere neue Betrachtungsweise.
1

Tier

*
<< uses >>
<< interface>>

Kopf

Rumpf

bewegt sich in

Fhigkeit
Bewegen() <<implements>>

Extremitt Schwimmen
Bewegen()

Laufen
Bewegen()

Fliegen
Bewegen()

Flgel

Bein

Flosse

Ort

Erde

Wasser

Luft

Abbildung 5.3: Schwimmen, Laufen, Fliegen,. . . bei einem Tier (Ente)

150

5.1 Entwurfsprinzipien

151

5.1.5 Anwendung der abstrakten Fabrik auf GUI-Elemente


uses CGUIFabrik {abstract} Client

+ ErzeugeEdit() + ErzeugeKnopf() CEdit {abstract}

uses

CMotifFabrik

CMfcFabrik CMfcEdit CMotifEdit

+ ErzeugeEdit() + ErzeugeKnopf()

+ ErzeugeEdit() + ErzeugeKnopf() erzeugt

CKnopf {abstract}

CMfcKnopf

CMotifKnopf

erzeugt

Abbildung 5.4: Abstrakte Fabrik fr GUI-Elemente

151

152

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

5.2

Objektorientierte Vorgehensweise

5.2.1 Hotelbuchung
Hotelbuchung
Anmelden Minibar auffuellen

Gast
<<include>> <<extend>> <<include>>

Zimmer reservieren

Zimmer reinigen

Portier

Reinigungs personal

Abbildung 5.5: Anwendungsfalldiagramm zu einer Hotelbuchung

5.2.2 Haftpichtschaden
Haftpflichtversicherung
Schaden abwickeln

Versicherter (Sie)

<<include>> <<extend>>
Schaden erfassen

Geschaedigter (Nachbar)
Schaden begutachten

externer Gutachter
Abbildung 5.6: Anwendungsfalldiagramm zum Haftpichtschaden

152

5.2 Objektorientierte Vorgehensweise

153

5.2.3 Essen bestellen, verzehren und bezahlen


Abbildung 5.7 zeigt die Lsung zu dieser Aufgabenstellung.

Gast
H

Ober

Kueche

Essen bestellen Bestellung aufnehmen Essen kochen

Essen servieren

Essen verzehren

Rechnung erstellen

Bezahlen

Abbildung 5.7: Aktivittsdiagramm zu Essen bestellen, verzehren und bezahlen

153

154

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

5.3

Entwurfsmuster (Design Patterns)

5.3.1 Einsatz einer abstrakten Fabrik


Programm 5.2 diffzeich2.cpp: Einsatz einer abstrakten Fabrik
#include <iostream> using namespace std; //............................................................... Grafik-Objekte class CGraphObj { public: CGraphObj() { m_id = m_zaehle++; } virtual void zeichne() protected: int static int }; int CGraphObj::m_zaehle = 0; class CKreis : public CGraphObj { public: void zeichne() { cout << "Kreis }; class CQuadrat : public CGraphObj { public: void zeichne() { cout << "Quadrat }; class CEllipse : public CGraphObj { public: void zeichne() { cout << "Ellipse }; class CRechteck : public CGraphObj { public: void zeichne() { cout << "Rechteck " << m_id << endl; } }; //..................................................................... Fabriken class CFabrik { public: virtual CGraphObj *createRundes() }; class CEinfachFabrik : public CFabrik { public: CGraphObj *createRundes() }; class CVariabelGraphFabrik : public CFabrik { public: CGraphObj *createRundes() { return new CEllipse; } CGraphObj *createEckiges() { return new CRechteck; } }; { return new CKreis; } = 0; " << m_id << endl; } " << m_id << endl; } " << m_id << endl; } m_id; m_zaehle; = 0;

virtual CGraphObj *createEckiges() = 0;

CGraphObj *createEckiges() { return new CQuadrat; }

154

5.3 Entwurfsmuster (Design Patterns)

155

//......................................................................... main int main(void) { char antwort; cout << "Einfach (e) oder Flexibel (f): "; cin >> antwort; CFabrik *fabrik; if (antwort == e) fabrik = new CEinfachFabrik; else fabrik = new CVariabelGraphFabrik; CGraphObj *figur[3]; figur[0] = fabrik->createRundes(); figur[1] = fabrik->createEckiges(); figur[2] = fabrik->createRundes(); for (int i=0; i < 3; i++) figur[i]->zeichne(); }

5.3.2 Unterschiedliche Erbauer als Stack und als Queue


Programm 5.3 erbauer2.cpp: Unterschiedliche Erbauer als Stack und als Queue
#include <stack> #include <queue> #include <iostream> using namespace std;

class Container { public: virtual void gibAus() = 0; }; class Stack : public Container { friend class StackBuilder; public: virtual ~Stack() { } void gibAus() { while (!m_stack.empty()) { cout << m_stack.top() << ", "; m_stack.pop(); } cout << endl; } private: stack<int> m_stack; }; class Queue : public Container { friend class QueueBuilder; public: virtual ~Queue() { }

155

156

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

void gibAus() { while (!m_queue.empty()) { cout << m_queue.front() << ", "; m_queue.pop(); } cout << endl; } private: queue<int> m_queue; }; class Builder { public: virtual void addZahl(int) = 0; virtual Container& getResult() }; class StackBuilder : public Builder { public: virtual ~StackBuilder() { } void addZahl(int z) { lifo.m_stack.push(z); } } = 0;

Container& getResult() { return lifo; private: Stack lifo; }; class QueueBuilder : public Builder { public: virtual ~QueueBuilder() { } void addZahl(int z)

{ fifo.m_queue.push(z); } }

Container& getResult() { return fifo; private: Queue fifo; }; class Director { public: Director(Builder *b) { setBuilder(b); } void setBuilder(Builder *b) { erbauer = b; }

void construct() { for (int i=0; i < 10; i++) erbauer->addZahl(i); } private: Builder *erbauer; }; int main( void ) { StackBuilder s; Director dir(&s); dir.construct(); s.getResult().gibAus(); QueueBuilder q; dir.setBuilder(&q); dir.construct(); q.getResult().gibAus(); }

156

5.3 Entwurfsmuster (Design Patterns)

157

5.3.3 Proxy (Simulation eines File-Puffers)


Programm 5.4 proxy3.cpp: Simulation eines File-Puffers mittels eines Proxy-Musters
#include <cstring> #include <iostream> using namespace std; #define PUFFSIZE 10 //...................................................................... Subject class Subject { public: virtual void schreibe(const char *text) = 0; }; //.................................................................. RealSubject class RealSubject : public Subject { public: void schreibe(const char *text) { cerr << text << endl; } }; //........................................................................ Proxy class Proxy : public Subject { public: Proxy() : m_realSubject(new RealSubject), m_puffLen(0) { } virtual ~Proxy() { m_puffer[m_puffLen] = 0; m_realSubject->schreibe(m_puffer); delete m_realSubject; } void schreibe(const char *text) { for (unsigned i=0; i<strlen(text); i++) { m_puffer[m_puffLen] = text[i]; if (++m_puffLen >= PUFFSIZE) { m_puffer[m_puffLen] = 0; m_realSubject->schreibe(m_puffer); m_puffLen = 0; } } } int getPufferLen() { return m_puffLen; } private: RealSubject *m_realSubject; int char m_puffLen; m_puffer[PUFFSIZE+1];

}; //......................................................................... main int main(void) { //....... }

157

158

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

5.3.4 Adapter fr deutsches Zeitformat in das amerikanische


Programm 5.5 adapter3.cpp: Klassenadapter
//...... class USTime : private GermanTime { public: char *getTime() { static char puffer[30]; char *zgr; int std, min; cout << " German time is " << (zgr = GermanTime::getTime()) << endl;

sscanf(zgr, "%d.%d", &std, &min); if (std >= 12) sprintf( puffer, "%d:%02d PM", std%12, min); else sprintf( puffer, "%d:%02d AM", std, min); return puffer; } }; //......

Programm 5.6 adapter4.cpp: Objektadapter


//...... class USTime { public: char *getTime() { static char puffer[30]; char *zgr; int std, min; cout << " German time is " << (zgr = germanTime.getTime()) << endl;

sscanf(zgr, "%d.%d", &std, &min); if (std >= 12) sprintf( puffer, "%d:%02d PM", std%12, min); else sprintf( puffer, "%d:%02d AM", std, min); return puffer; } private: GermanTime }; //...... germanTime;

158

5.3 Entwurfsmuster (Design Patterns)

159

5.3.5 Vermeiden von Klassen-Explosion mit Dekorierer-Muster


Programm 5.7 dekokombi2.cpp: Vermeiden einer Explosion von Klassen durch Dekorierer-Muster
#include <iostream> using namespace std;

//.................................................................... Component class Component { public: virtual ~Component() { } virtual void out() = 0; };

//................................................................... Dekorierer class Decorator : public Component { public: Decorator(Component *c) : m_comp(c) { } virtual ~Decorator() { delete m_comp; } virtual void out() { m_comp->out(); } private: Component *m_comp; }; class A1 : public Decorator { public: A1(Component *c) : Decorator(c) { } ~A1() { } virtual void out() { Decorator::out(); }; class A2 : public Decorator { public: A2(Component *c) : Decorator(c) { } ~A2() { } virtual void out() { Decorator::out(); }; class A3 : public Decorator { public: A3(Component *c) : Decorator(c) { } ~A3() { } virtual void out() { Decorator::out(); }; cout << "3, "; } cout << "2, "; } cout << "1, "; }

class A4 : public Decorator { public: A4(Component *c) : Decorator(c) { } ~A4() { } virtual void out() { Decorator::out(); }; cout << "4, "; }

159

160

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

class A5 : public Decorator { public: A5(Component *c) : Decorator(c) { } ~A5() { } virtual void out() { Decorator::out(); }; cout << "5, "; }

//............................................................ ConcreteComponent class A : public Component { public: virtual ~A() { } void out() { cout << "A, "; } };

//......................................................................... main int main(void) { Component *a1 Component *a12 = new A1( new A ); = new A2( new A1( new A ) );

Component *a23 = new A3( new A2( new A ) ); Component *a123 = new A3( new A2( new A1( new A ) ) ); //.... Nun beliebige Kompinationen moeglich Component *a1345 = new A5( new A4( new A3( new A1( new A ) ) ) ); Component *a2334 = new A4( new A3( new A3( new A2( new A ) ) ) ); Component *a12345 = new A5( new A4( new A3( new A2( new A1( new A ) ) ) ) ); a1->out(); a12->out(); a23->out(); cout << endl; cout << endl; cout << endl;

a123->out(); cout << endl; a1345->out(); a2334->out(); } cout << endl; cout << endl;

a12345->out(); cout << endl;

Programm 5.7 liefert die folgende Ausgabe:


A, 1, A, 1, 2, A, 2, 3, A, 1, 2, 3, A, 1, 3, 4, 5, A, 2, 3, 3, 4, A, 1, 2, 3, 4, 5,

160

5.3 Entwurfsmuster (Design Patterns)

161

5.3.6 Iterator (Rckwrtiges Traversieren eines Binrbaums)


1. Sowohl die Klasse Node als auch die Klasse BinBaum muss den BackwardIterator als friend deklarieren:
friend class BackwardIterator;

2. Zudem muss im public-Teil der Klasse BinBaum noch Folgendes angegeben werden:
Iterator *CreateBackwardIterator();

3. Programm 5.8 zeigt noch die fehlende Klasse BackwardIterator.


Programm 5.8 iterator3.cpp: Rckwrts-Traversieren eines Binrbaums
//................ //............................................................. BackwardIterator class BackwardIterator : public Iterator { public: BackwardIterator(BinBaum *baum) : m_nextNode(0), m_baum(baum) { } virtual ~BackwardIterator() { } void First() { macheArray(m_baum->m_wurzel); m_nextNode = m_array.size() - 1; } void Next() { m_nextNode--; } bool isDone() const int { return m_nextNode < 0; }

CurrentItem() const { return m_array[m_nextNode]->m_wert; }

private: int BinBaum vector<Node *> m_nextNode; *m_baum; m_array;

void macheArray(Node *knoten) { if (knoten->m_links != 0) macheArray(knoten->m_links); m_array.push_back(knoten); if (knoten->m_rechts != 0) macheArray(knoten->m_rechts); } };

//......................... CreateForwardIterator() und CreateBackwardIterator() Iterator *BinBaum::CreateForwardIterator() { return new ForwardIterator(this); }

Iterator *BinBaum::CreateBackwardIterator() { return new BackwardIterator(this); } //................

161

162

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

5.3.7 Beobachter-Muster fr verschiedene Zahlenkonvertierer


Programm 5.9 beobachter3.cpp: Beobachter-Muster fr unterschiedliche Zahlenkonvertierer
//................ //..................................................................... Observer class Observer { public: Observer(Subject *s) : subject(s) { } virtual void Update() = 0; protected: Subject *subject; }; //...................................................................... Subject class Subject { public: void Attach(Observer *observer) { observers.insert(observer); } void Notify() { set<Observer*>::iterator it; for (it = observers.begin(); it !=observers.end(); it++) (*it)->Update(); } void SetState(unsigned wert) { subjectState = wert; Notify(); } { return subjectState; }

unsigned GetState()

private: unsigned subjectState;

set<Observer*> observers; }; //.................................................................. HexObserver class HexObserver : public Observer { public: HexObserver(Subject *s) : Observer(s) { subject->Attach(this); } void Update() { cout << " Hexa: 0x" << hex << subject->GetState() << endl;} }; //.................................................................. OctObserver class OctObserver : public Observer { public: OctObserver(Subject *s) : Observer(s) { subject->Attach(this); } void Update() { cout << "Oktal: 0" << oct << subject->GetState() << endl; } }; //................................................................. DualObserver class DualObserver : public Observer { public: DualObserver(Subject *s) : Observer(s) { subject->Attach(this); } void Update() { unsigned bit, zahl = subject->GetState(); bool relevant = false;

162

5.3 Entwurfsmuster (Design Patterns)

163

cout << " Dual: "; for (int i=sizeof(unsigned)*8-1; i>=0; i--) { bit = (zahl >> i ) & 1; if (bit == 1) relevant = true; if (relevant) cout << bit; } cout << endl; } }; //................

5.3.8 Zustands-Muster zu vorgegebenen Zustandsautomaten


Programm 5.10 zustand3.cpp: Zustands-Muster zu einem vorgegebenen Zustandsautomaten
#include <string> #include <iostream> using namespace std;

enum ereignis { rot, blau, gelb };

class Kontext;

//...................................................................... Zustand class Zustand { public: virtual void rot( Kontext*) { cout << "... kein Zustandswechsel" << endl; } virtual void blau(Kontext*) { cout << "... kein Zustandswechsel" << endl; } virtual void gelb(Kontext*) { cout << "... kein Zustandswechsel" << endl; } protected: void wechsle(Kontext*, Zustand*); };

//............................................................................ X class X : public Zustand { public: static Zustand *instance() { return (!m_inst) ? m_inst = new X : m_inst; }; virtual void rot( Kontext *); virtual void blau(Kontext *); virtual void gelb(Kontext *); private: static Zustand *m_inst; };

Zustand *X::m_inst = 0;

163

164

5 Lsungen zu Teil V (Entwurfsprinzipien und Entwurfsmuster)

//............................................................................ Y class Y : public Zustand { public: static Zustand *instance() { return (!m_inst) ? m_inst = new Y : m_inst; }; virtual void rot( Kontext *); virtual void blau(Kontext *); virtual void gelb(Kontext *); private: static Zustand *m_inst; };

Zustand *Y::m_inst = 0; //............................................................................ Z class Z : public Zustand { public: static Zustand *instance() { return (!m_inst) ? m_inst = new Z : m_inst; }; virtual void rot( Kontext *); virtual void blau(Kontext *); virtual void gelb(Kontext *); private: static Zustand *m_inst; }; Zustand *Z::m_inst = 0; //.................................... rot(), blau(), gelb() - Implementierungen void X::rot( Kontext *k) { cout << "X, rot ==> X"; wechsle(k, X::instance()); } void X::blau(Kontext *k) { cout << "X, blau ==> Y"; wechsle(k, Y::instance()); } void X::gelb(Kontext *k) { cout << "X, gelb ==> Z"; wechsle(k, Z::instance()); } void Y::rot( Kontext *k) { cout << "Y, rot ==> " ; Zustand::rot(k); } void Y::blau(Kontext *k) { cout << "Y, blau ==> X"; wechsle(k, X::instance()); } void Y::gelb(Kontext *k) { cout << "Y, gelb ==> Z"; wechsle(k, Z::instance()); } void Z::rot( Kontext *k) { cout << "Z, rot ==> " ; Zustand::rot(k); void Z::blau(Kontext *k) { cout << "Z, blau ==> " ; Zustand::blau(k); } }

void Z::gelb(Kontext *k) { cout << "Z, gelb ==> Y"; wechsle(k, Y::instance()); } //...................................................................... Kontext class Kontext { friend class Zustand; public: void rot() { m_state->rot( this); }

void blau() { m_state->blau(this); } void gelb() { m_state->gelb(this); } void setState(Zustand *z) { m_state = z; } private: void wechsle(Zustand *z) Zustand *m_state; }; void Zustand::wechsle(Kontext *k, Zustand *z) { k->wechsle(z); } //......................................................................... main //................ { m_state = z; cout << endl; }

164

5.3 Entwurfsmuster (Design Patterns)

165

5.3.9 Mementos zur gleichzeitigen Verwaltung mehrerer Spieler


Programm 5.11 zeigt die fehlenden Klassen zu dieser Aufgabenstellung.
Programm 5.11 memento3.cpp: Mementos zur gleichzeitigen Verwaltung mehrerer Spieler
//................ //...................................................................... Spieler struct Spieler { Spieler() : min(1), max(20), fertig(false) { } int min, max;

bool fertig; string name; }; //...................................................................... Memento class Memento { friend class Spiel; private: Memento(int wert) : m_wert(wert) { } // privater Konstruktor int m_wert; }; //........................................................................ Spiel class Spiel { public: Memento *anmelden() { return new Memento(rand() % 20 + 1); } int auswerten(Memento *m, int zahl) { return (zahl == m->m_wert) ? 0 : ((zahl > m->m_wert) ? 1 : -1); } }; //................

165

Kapitel 6
Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)
6.1 Zufllige RGB-Farben
Programm 6.1 rgbzufall.cpp: Zufllige RGB-Farben
#include <qapplication.h> #include <qpushbutton.h> #include <qlabel.h>

class CAnzeige : public QLabel { Q_OBJECT public: CAnzeige(QWidget *parent) : QLabel(parent) { neueFarbe(); } public slots: void neueFarbe(void) { QColor farb(rand()&255, rand()&255, rand()&255); setPaletteBackgroundColor( farb ); char text[100]; setFont(QFont("Times", 20)); sprintf(text, " %3d, %3d, %3d", farb.red(), farb.green(), farb.blue()); setText(text); } };

#include "rgbzufall.moc" int main( int argc, char* argv[] ) { QApplication myapp( argc, argv );

QWidget* mywidget = new QWidget(); mywidget->setGeometry( 100, 100, 300, 300 );

167

168

6 Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)

QPushButton *b = new QPushButton( "Neue Farbe", mywidget ); b->setGeometry(0, 0, 300, 40); CAnzeige *farbLabel = new CAnzeige(mywidget); farbLabel->setGeometry( 0, 40, 300, 260); QObject::connect( b, SIGNAL( clicked() ), farbLabel, SLOT( neueFarbe() ) );

myapp.setMainWidget( mywidget ); mywidget->show(); return myapp.exec(); }

6.2

Ziehen von Lottozahlen

Programm 6.2 lottoqt.cpp: Ziehen von Lottozahlen


#include <qapplication.h> #include <qpushbutton.h> #include <qlcdnumber.h>

class CLottoZahl : public QLCDNumber { public: CLottoZahl(int n, QWidget *parent) : QLCDNumber(2, parent), gezogen(false), nr(n) { display(n); setPaletteBackgroundColor(Qt::lightGray); } void setGezogen(bool z) { gezogen = z; bool getGezogen(void) private: bool gezogen; int }; class CLotto : public QWidget { Q_OBJECT public: CLotto(QWidget *p) : QWidget(p), x(0) { srand(time(NULL)); setPaletteBackgroundColor(Qt::white); for (int i=1; i < 50; i++) { lottoZ[i] = new CLottoZahl(i, this); lottoZ[i]->setGeometry(((i-1)%7)*40, (i-1)/7*40, 35, 35 ); } b = new QPushButton( "Ziehen", this ); b->setGeometry(10, 360, 320, 40); QObject::connect( b, SIGNAL( clicked() ), nr; }

{ return gezogen; }

this, SLOT( draw() ) ); }

168

6.2 Ziehen von Lottozahlen

169

public slots: void draw(void) { int z; while (lottoZ[z=rand()%49+1]->getGezogen()) // doppelte Zahl vermeiden ; lottoZ[z]->setGezogen(true); num = new QLCDNumber(2, this); num->setGeometry( (x==6) ? 50+x*40 : 30+x*40, 300, 35, 35); num->display(z); if (x==6) { //........ Zusatzzahl lottoZ[z]->setPaletteBackgroundColor( Qt::magenta ); num->setPaletteBackgroundColor( Qt::yellow ); } else { lottoZ[z]->setPaletteBackgroundColor( Qt::blue ); num->setPaletteBackgroundColor( Qt::green ); } num->show(); if (++x >= 7) b->setDisabled(true); } private: QLCDNumber CLottoZahl *num; *lottoZ[50];

QPushButton *b; int x; }; #include "lottoqt.moc"

int main( int argc, char* argv[] ) { QApplication myapp( argc, argv );

QWidget* mywidget = new QWidget(); mywidget->setGeometry( 200, 200, 380, 450 );

CLotto *ziehObj = new CLotto(mywidget); ziehObj->setGeometry(10, 10, 350, 420);

myapp.setMainWidget( mywidget ); mywidget->show(); return myapp.exec(); }

169

170

6 Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)

6.3

Synchronisierte Anzeige verschiedener Zahlensysteme

Programm 6.3 lcdwandel.cpp: Synchronsisierte Darstellung von Zahlen in verschiedenen Zahlensystemen


#include <qapplication.h> #include <qpushbutton.h> #include <qlabel.h> #include <qlcdnumber.h> #include <qslider.h> #include <qcolor.h> #include <qfont.h> //.......................................................... Klasse LCDZahl class LCDZahl : public QWidget { Q_OBJECT public: LCDZahl( const QString& int QLCDNumber::Mode QColor titel, ziffernZahl, zsystem, hintergrundFarbe,

QLCDNumber::SegmentStyle stil, QWidget* QLabel* parent ) : QWidget( parent ) {

text = new QLabel( this );

text->setGeometry( 0, 0, 150, 20 ); text->setText( titel ); text->setAlignment( QLabel::AlignCenter ); QLCDNumber *lcd = new QLCDNumber( ziffernZahl, this ); lcd->setGeometry( 0, 20, 150, 150 ); lcd->setMode( zsystem ); lcd->setPaletteBackgroundColor( hintergrundFarbe ); lcd->setSegmentStyle( stil ); lcd->display( 100 ); slider = new QSlider( 0, 65535, 1, 100, Horizontal, this ); slider->setGeometry( 0, 180, 150, 20 ); slider->setValue( 100 ); QObject::connect( slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );

QObject::connect( slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)) ); } int value() const { return slider->value(); } public slots: void setValue( int value ) { slider->setValue( value ); } signals: void valueChanged( int ); private: QSlider *slider; }; #include "lcdwandel.moc"

170

6.3 Synchronisierte Anzeige verschiedener Zahlensysteme

171

//.................................................................... main int main( int argc, char **argv ) { QApplication myapp( argc, argv );

QWidget* mywidget = new QWidget(); mywidget->setGeometry( 10, 10, 600, 300 ); mywidget->setMinimumSize( 200, 300 );

QColor blaugruen( 0, 255, 255 ); LCDZahl* z[4]; z[0] = new LCDZahl( "Dual", 16, QLCDNumber::BIN, blaugruen, QLCDNumber::Flat, mywidget ); z[0]->setGeometry(0, 10, 150, 200); z[1] = new LCDZahl( "Oktal", 6, QLCDNumber::OCT, QLCDNumber::darkBlue, QLCDNumber::Outline, mywidget ); z[1]->setGeometry(150, 10, 150, 200); z[2] = new LCDZahl( "Dezimal", 5, QLCDNumber::DEC, QLCDNumber::darkGray, QLCDNumber::Filled, mywidget ); z[2]->setGeometry(300, 10, 150, 200); z[3] = new LCDZahl( "Hexadezimal", 4, QLCDNumber::HEX, QLCDNumber::darkYellow, QLCDNumber::Filled, mywidget ); z[3]->setGeometry(450, 10, 150, 200);

for (int i=0; i<4; i++) for (int j=0; j < 4; j++) if (i != j) QObject::connect( z[i], SIGNAL(valueChanged(int)), z[j], SLOT(setValue(int)) ); QPushButton* quit = new QPushButton( "Quit", mywidget ); quit->setGeometry( 0, 240, 600, 40 ); quit->setFont( QFont( "Times", 18, QFont::Bold ) ); quit->setPaletteBackgroundColor( Qt::yellow ); QObject::connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) ); myapp.setMainWidget( mywidget ); mywidget->show(); return myapp.exec(); }

171

172

6 Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)

6.4

Rasterung in einer Zeichenche

Programm 6.4 raster.cpp: Rasterung in einer Zeichenche


#include <qapplication.h> #include <qwidget.h> #include <qpainter.h> #include <qpixmap.h> #include <qmenubar.h> #include <qpopupmenu.h> #include <qscrollview.h>

QColor farbArray[] = { Qt::black, Qt::lightGray, Qt::cyan, Qt::darkGreen, }; Qt::white, Qt::red, Qt::magenta, Qt::darkGray, Qt::gray, Qt::green, Qt::yellow, Qt::blue, Qt::darkRed,

Qt::darkBlue, Qt::darkCyan, Qt::darkMagenta, Qt::darkYellow

//------------------------------------------ class MalFlaeche -------------class MalFlaeche : public QWidget { public: MalFlaeche() { farbe = gray; abstand = 10; setBackgroundMode( NoBackground ); // beim Neumalen nicht loeschen setPaletteBackgroundColor( white ); } ~MalFlaeche() { }

void

setAbstand(int n) {

if (abstand+n > 0) { abstand += n; draw(); } } void setFarbe(QColor f) { farbe = f; draw(); }

private: QColor int farbe; abstand;

virtual void paintEvent( QPaintEvent* )

{ draw(); }

virtual void resizeEvent( QResizeEvent* ) { draw(); }

172

6.4 Rasterung in einer Zeichenche

173

void draw(void) { QPainter wMaler; wMaler.begin( this ); wMaler.eraseRect(0, 0, maximumWidth(), maximumHeight()); wMaler.setPen( farbe ); for (int i=0; i < maximumWidth(); i += abstand) wMaler.drawLine(i, 0, i, maximumHeight()); for (int j=0; j < maximumHeight(); j += abstand) wMaler.drawLine(0, j, maximumWidth(), j); wMaler.end(); } };

//------------------------------------------ class MalFenster -------------class MalFenster : public QWidget { Q_OBJECT public: MalFenster() { malflaeche = new MalFlaeche(); malflaeche->setGeometry( 0, 0, 1000, 1000 ); // notwendig, da MalFenster Slots enthaelt

dateimenu = new QPopupMenu; //... erzeugt ein Datei-Menue dateimenu->insertItem( "&Beenden", qApp, SLOT( quit() ) );

QPixmap pixmap(40, 15); farbemenu = new QPopupMenu; //... erzeugt ein Farbe-Menue for (unsigned i=0; i < sizeof(farbArray) / sizeof(*farbArray); i++) { pixmap.fill(farbArray[i]); farbemenu->insertItem(pixmap, i); } QObject::connect( farbemenu, this, SIGNAL( activated( int ) ), SLOT( farbeMenuSlot( int ) ) );

menubalken = new QMenuBar( this ); //... erzeugt einen Menue-Balken menubalken->insertItem( "&Datei", dateimenu ); menubalken->insertItem( "&Farbe", farbemenu );

scrollview = new QScrollView( this ); scrollview->setGeometry( 0, menubalken->height(), width(), height() - menubalken->height() ); scrollview->addChild( malflaeche ); } ~MalFenster() { }

protected: virtual void resizeEvent( QResizeEvent* ) { scrollview->setGeometry( 0, menubalken->height(), width(), height() - menubalken->height() ); }

173

174

6 Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)

virtual void keyPressEvent( QKeyEvent* event ) { if (event->key() == Key_Less) malflaeche->setAbstand(-1); else if (event->key() == Key_Greater) malflaeche->setAbstand(1); }

private slots: void farbeMenuSlot(int farbeId) { malflaeche->setFarbe(farbArray[farbeId]); }

private: QMenuBar QPopupMenu QPopupMenu QScrollView MalFlaeche }; *menubalken; *dateimenu; *farbemenu; *scrollview; *malflaeche;

#include "raster.moc"

//------------------------------------------------------ main -------------int main( int argc, char* argv[] ) { QApplication myapp( argc, argv );

MalFenster *mywidget = new MalFenster(); mywidget->setGeometry( 50, 50, 400, 400 );

myapp.setMainWidget( mywidget ); mywidget->show(); return myapp.exec(); }

174

6.5 Grasches Mastermind-Spiel gegen den Computer

175

6.5 Grasches Mastermind-Spiel gegen den Computer


Programm 6.5 mastermind.cpp: Grasches Mastermind-Spiel gegen den Computer
#include <qapplication.h> #include <qlabel.h> #include <qpushbutton.h> #include <qmessagebox.h> #include <qgrid.h> #include <qhbox.h> #include <qvbox.h> #include <math.h> #include <stdio.h>

#define POS_ZAHL

QColor farben[] = { Qt::white,

Qt::blue,

Qt::red,

Qt::cyan,

Qt::yellow, Qt::green, Qt::black, Qt::magenta };

const int farbZahl = sizeof( farben ) / sizeof( farben[0] );

//....................................................... Klasse MeinButton class MeinButton : public QPushButton { Q_OBJECT public: MeinButton( QWidget *parent=0, const char *name=0 ) : QPushButton( parent, name ) { farbe = Qt::lightGray; setPaletteBackgroundColor( farbe ); QObject::connect( this, SIGNAL( clicked() ), this, SLOT( changeColor() ) ); } QColor getColor() { return farbe; }

private slots: void changeColor( void ) { if ( farbe == Qt::lightGray ) farbe = Qt::white;

else if ( farbe == Qt::white ) farbe = Qt::black; else if ( farbe == Qt::black ) farbe = Qt::lightGray; setPaletteBackgroundColor( farbe ); }

private: QColor farbe; };

175

176

6 Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)

//....................................................... Klasse Mastermind class Mastermind : public QVBox { Q_OBJECT public: Mastermind( QWidget *parent=0, const char *name=0 ) : QVBox( parent, name ) { int i, j, p;

nothing = QColor( 1, 1, 1 );

QPushButton *b = new QPushButton( "Ok", this ); QObject::connect( b, SIGNAL( clicked() ), this, SLOT( streichen() ) );

//........................... Horizontales Layout (QHBox) for ( i=0; i<15; i++) { QHBox *reihe = new QHBox( this ); reihe->setSpacing(10);

QHBox *stones = new QHBox( reihe ); stones->setSpacing(2); stones->setMargin(2); for (j=0; j<POS_ZAHL; j++) { stein[i*10+j] = new QLabel( " stein[i*10+j]->show(); } ", stones );

stein[i*10+j]->setFixedSize( 20, 20 );

QHBox *sticks = new QHBox( reihe ); sticks->setSpacing(2); sticks->setMargin(2); for (j=0; j<POS_ZAHL; j++) { stoepsel[i*10+j] = new MeinButton( sticks ); stoepsel[i*10+j]->setFixedSize( 20, 20); } }

//... Erstellen aller moeglichen Kombinationen runde = 0; kombiZahl = nochMoeglich = (int)pow(farbZahl, POS_ZAHL);

kombination = new QColor* [kombiZahl]; for ( i=0; i<kombiZahl; i++) { int z = i; kombination[i] = new QColor [POS_ZAHL]; for ( p=0; p<POS_ZAHL; z /= (int)farbZahl, p++) kombination[i][p] = farben[z % farbZahl]; } naechsteKombination(); }

176

6.5 Grasches Mastermind-Spiel gegen den Computer

177

private slots: //............. Streichen aller nicht in Frage kommender Kombinationen void streichen( ) { i, p, p2, pr, fr;

int

positionRichtig = farbeRichtig = 0; for ( p=0; p<POS_ZAHL; p++) { QColor farbe = stoepsel[runde*10+p]->getColor(); if ( farbe == Qt::white ) farbeRichtig++; else if ( farbe == Qt::black ) positionRichtig++; }

for (i=0; i<kombiZahl; i++) { pr = fr = 0; //.. Pruefen nicht noetig, wenn Kombination schon gestrichen if (kombination[i] == NULL) continue; //.. Sichern, um Originale nicht zu zerstoeren for (p=0; p<POS_ZAHL; p++) { aktTemp[p] = aktuelleKombi[p];

kombiTemp[p] = kombination[i][p]; } //.. Positionen ueberpruefen for (p=0; p<POS_ZAHL; p++) if (kombiTemp[p] == aktTemp[p]) { pr++; aktTemp[p] = kombiTemp[p] = nothing; // bereits ausgewertet } //.. Farben ueberpruefen for (p=0; p<POS_ZAHL; p++) for (p2=0; p2<POS_ZAHL; p2++) if (kombiTemp[p] != nothing && kombiTemp[p] == aktTemp[p2]) { fr++; aktTemp[p2] = kombiTemp[p] = nothing; // bereits ausgewertet break; } //.. Wenn nicht in Frage kommend, diese Kombi. streichen if (pr != positionRichtig || fr != farbeRichtig) { delete kombination[i]; kombination[i] = NULL; nochMoeglich--; } } rest = nochMoeglich; runde++; naechsteKombination(); if ( positionRichtig == 5 || rest <= 1 ) auswert(); }

177

178

6 Lsungen zu Teil VI (Portable GUI-Programmierung mit Qt)

void

auswert( void ) {

if ( positionRichtig == 5 || rest == 1 ) QMessageBox::information( 0, "Fertig", "Ok, ich habe die Kombination gefunden", QMessageBox::Ok ); else QMessageBox::information( 0, "Fehler", "Irgendwo hast Du mir falsche Infos gegeben", QMessageBox::Ok ); qApp->quit(); }

private: //......................... Holen der naechsten moeglichen Kombination void naechsteKombination( void ) { for (int i=0; i<kombiZahl; i++) if (kombination[i] != NULL) { for (int p=0; p<POS_ZAHL; p++) aktuelleKombi[p] = kombination[i][p]; break; } for (int p=0; p<POS_ZAHL; p++) { QColor farbe( aktuelleKombi[p] ); stein[runde*10+p]->setPaletteBackgroundColor( farbe ); } }

//.................................................. private-Variablen QLabel MeinButton QColor *stein[150]; *stoepsel[150]; **kombination, aktuelleKombi[POS_ZAHL], aktTemp[POS_ZAHL], kombiTemp[POS_ZAHL]; int int QColor }; runde, kombiZahl, nochMoeglich, rest; positionRichtig, farbeRichtig; nothing;

#include "mastermind.moc"

//.................................................................... main int main( int argc, char *argv[] ) { QApplication a( argc, argv );

Mastermind* mastermind = new Mastermind(); mastermind->show(); a.setMainWidget( mastermind );

return a.exec(); }

178

Kapitel 7
Lsungen zum Anhang A
7.1 Exceptions in Konstruktoren
Programm exceptkonstr.cpp liefert die folgende Ausgabe:
EinsZwei Eins Eins Eins new Zwei int abgefangen: 2

Die Schwche von Programm exceptkonstr.cpp ist, dass im berladenen newOperator bei zgr2 = new Zwei; eine Exception auftritt, so dass der Konstruktor zu EinsZwei obj(3); nicht erfolgreich ausgefhrt wird, was dazu fhrt, dass Destruktor ~EinsZwei nicht ausgefhrt wird, woraus Speicherleichen resultieren.

179

180

7 Lsungen zum Anhang A

7.2

Exceptions versus setjmp()/longjmp()

Die Schwche von Programm setlongjmp.cpp ist, dass es nicht mehr den Destruktor zur Klasse K aufruft, was an folgender Ausgabe erkennbar wird:
Los gehts --K

fkt() Fertig, oder?

Programm 7.1 nosetlongjmp.cpp: Beseitigen der Schwche von Programm setlongjmp.cpp


#include <iostream> using namespace std; class K { public: K() { cout << "--K" << endl; }

~K() { cout << "--- ~K" << endl; } }; void fkt(void) { K obj; cout << "fkt()" << endl; throw 1; } int main(void) { cout << "Los gehts" << endl; try { fkt(); } catch (...) { cout << "Fertig, oder?" << endl; } }

Programm 7.1 behebt diese Schwche, was auch folgende Ausgabe zeigt:
Los gehts --K

fkt() --- ~K Fertig, oder?

180

Kapitel 8
Lsungen zum Anhang B
8.1 Schnittstellen
8.1.1 Schnittstelle ISprechweise fr unterschiedliche Dialekte
Vorteil: Das Schlsselwort virtual wre nicht vergessen worden, da der Compiler einen Fehler gemeldet htte, da er nirgends eine Implementierung zur Methode sprechen() gefunden htte. Nachteil: Jeder Mensch msste nun sprechen() explizit denieren. Eine Bereitstellung einer allgemeinen hochdeutschen Sprechweise (mittels Klassenvererbung) wie in Programm virtfrank.cpp spart hier redundanten Code.

8.1.2 Eine allgemein verwendbare verkettete Liste


Wichtig ist, dass die Listen-Klasse keine Objekte einer bestimmten Klasse enthlt, sondern allgemein Objekte, die eine festgelegte Schnittstelle implementieren. Hier wurde fr die Schnittstellenklasse der Namen CObj (fr beliebiges Objekt) gewhlt. Auch wichtig ist, dass ein CObj-Zeiger verwendet wird, damit Polymorphismus stattnden kann; dies ist schon allein wegen der Konstruktion und Destruktoren notwendig. Abbildung 8.1 zeigt die Zusammenhnge im Klassendiagramm. Wie man in Abbildung 8.1 erkennt, wird bei CListe::insert() der Parametertyp CObj& statt CObj* verwendet. Der Grund hierfr ist, dass dann ein Aufruf mit einem temporr auf dem Stack erzeugten Objekt mglich ist, wie z. B. poly.insert(CPunkt(3,4)). Bei CObj* msste statt dessen poly.insert(new CPunkt(3,4)) angegeben werden. Fr den Polymorphismus hat dies keine Auswirkung. Die Schnittstellen-Methode CObj::clone() ist fr das tatschliche Anlegen der Objekte in der Liste notwendig. Sie ist vergleichbar mit CGraphObj::create(), wobei hier jedoch beim Anlegen gleich die Daten des neuen Objektes verfllt werden, und zwar mit den Werten des aktuellen Objektes. Das Objekt, das die Botschaft erhlt, klont sich also somit selbst.

181

182

8 Lsungen zum Anhang B

Neues Objekt anlegen, das identische Daten zum eigenen besitzt.

CListe insert(obj: CObj&): bool getHead(): CListElem* 1

CListElem getNext(): CListElem* getObj(): CObj*

<<interface>> CObj clone: CObj* show()

Nachfolger

<<realize>> 1 CPolygon einfuegen(pkt: CPunkt): bool ausgeben() CPunkt m_x: int m_y: int

Abbildung 8.1: Klassendiagramm zu Polygon mit allgemeiner verketteter Liste

Programm 8.1 zeigt die C++-Realisierung der beiden Klassen CListElem und CListe aus Abbildung 8.1.
Programm 8.1 liste.h: Die Klassen CListElem und CListe
#ifndef LISTE_H #define LISTE_H // Realisierung einer verketteten Liste mit Hilfe von Rekursion. //============================================================================== #include <iostream> #include "obj.h" // ....Aufgabe von class CListElem: speichert ein Objekt und kennt Nachfolger. // bertragene Aufgaben werden, wenn es Sinn macht, auch an Nachfolger // weitergemeldet. (Prinzip: Stille Post) class CListElem { public: CListElem(CListElem *next, CObj& pObj) : m_pObj(pObj.clone()), m_next(next) {} ~CListElem() { // Nachfolger wird automatisch mit ListenElement selbst gelscht if (m_next) { // Wenn Nachfolger existiert delete m_next; if (m_pObj) delete m_pObj; } } CObj *getObj() { return m_pObj; } CListElem *getNext() { return m_next; } // Nachfolger lschen

182

8.1 Schnittstellen

183

void printall() { if (m_next) // Nchsten Element Druckauftrag mitteilen (Rekursiv) m_next->printall(); m_pObj->show(); // Das jeweilige Element (Rekursion!) zeichnet sich nun std::cout << std::endl; } private: CObj *m_pObj; CListElem *m_next; }; // ....Aufgabe von class CListe: Reprsentiert eine verkettete Liste nach aussen // Intern wird nur ein Zeiger auf das erste Element gespeichert. // Alle Funktionsaufrufe werden an erstes Element in der Liste delegiert. class CListe { public: CListe() : m_Head(0) { } ~CListe() { if (m_Head) delete m_Head; // Liste lschen -> Lschauftrag an 1. Knoten } bool insert(CObj& pObj) { // Neuen Knoten am Beginn der Liste einfgen CListElem *pNew = new CListElem(m_Head, pObj); if (pNew) m_Head=pNew; return (pNew!=0); } // Da es sich um eine kopfgesteuerte Liste handelt, ist das // erste Element am Ende der Liste und das letzte am Anfang CListElem* getHead() { return m_Head; } private: CListElem *m_Head; // Kopf der Liste }; #endif // Liste als leer kennzeichnen

Programm 8.2 zeigt die C++-Realisierung der Schnittstellen-Klasse CObj aus Abbildung 8.1.
Programm 8.2 obj.h: Die Schnittstellen-Klasse CObj
#ifndef OBJ_H #define OBJ_H

class CObj public: CObj() {}

virtual ~CObj() {} virtual void show()=0; virtual CObj* clone()=0; }; #endif

183

184

8 Lsungen zum Anhang B

Programm 8.3 zeigt die C++-Realisierung der Klasse CPunkt aus Abbildung 8.1 fr die Punkte des Polygons.
Programm 8.3 pkt.h: Die Klasse CPunkt
#ifndef PUNKT_H #define PUNKT_H #include <iostream> #include "obj.h" class CPunkt public: CPunkt(int x=0, int y=0) : m_x(x), m_y(y) { } virtual ~CPunkt() { } CObj* clone() { return new CPunkt(m_x,m_y); void show() private: int m_x, m_y; }; #endif } : public CObj {

{ std::cout << "P(" << m_x << ", " << m_y << ")"; }

Um Strings mit der allgemein verwendbaren verketteten Liste verwalten zu knnen, msste Abbildung 8.1 so gendert werden, wie es Abbildung 8.2 zeigt.
Dieser Teil kann vollstndig ohne jegliche nderungen bernommen werden.
CListe insert(obj: CObj&): bool getHead(): CListElem* 1
Nachfolger
Neues Objekt anlegen, das identische Daten zum eigenen besitzt.

CListElem getNext(): CListElem* getObj(): CObj*

<<interface>> CObj clone: CObj* show()

<<realize>> 1 CStrListe einfuegen( str: CStr ): bool ausgeben() CStr m_str: char*

Abbildung 8.2: Klassendiagramm zur Stringverwaltung mit allgemeiner verketteter Liste

Statt der Klasse CPunkt (aus Programm 8.3 (pkt.h) wird nun die Klasse CStr (aus Programm 8.4 (str.h) bentigt.
Programm 8.4 str.h: Die Klasse CStr
#ifndef CSTR_H #define CSTR_H

184

8.2 Assoziative Arrays durch berladen des Indexoperators

185

#include <iostream> #include <cstring> #include "obj.h" class CStr : public CObj { public: CStr(char* str) { if ( (m_str = new char[strlen(str)+1]) ) strcpy(m_str, str); } virtual ~CStr() { if (m_str) // Speicherplatz fuer m_str wurde besorgt delete[] m_str; } CObj* clone() { return new CStr(m_str); } void show() private: char *m_str; }; #endif { std::cout << m_str; }

8.2 Assoziative Arrays durch berladen des Indexoperators


8.2.1 Binrbaum mittels eines assoziativen Arrays
Programm 8.5 wortstat.cpp: Klasse CArray mit ssoziativen Zugriff auf Binrbaum
class CArray { public: CArray() : m_first(0) {} CArray(char *w) : m_wort(new char[strlen(w)+1]), m_zaehl(0), m_links(0), m_rechts(0) { strcpy(m_wort, w); } int& operator[] (char *wort) { return sucheWort(m_first, wort); } friend ostream &operator<<(ostream &os, const CArray *a) { if (a != 0) { return os << a->m_links << setiosflags(ios::left) << setw(20) << a->m_wort << resetiosflags(ios::left) << setw(5) << a->m_zaehl << endl << a->m_rechts; } else return os << ""; }

185

186

8 Lsungen zum Anhang B

const CArray *getFirst(void) const { return m_first; } private: char int *m_wort; m_zaehl; // zeigt auf das Wort // zaehlt die Vorkommen von wort

CArray *m_first; CArray *m_links;

// linker Nachfolger-Knoten im Binaerbaum

CArray *m_rechts; // rechter Nachfolger-Knoten im Binaerbaum int& sucheWort(CArray *&zgr, char *w) { int vergl; if (zgr == 0) { zgr = new CArray(w); return zgr->m_zaehl; // neues Wort // Wort kommt erstemal vor

} else if ( (vergl=strcmp(w, zgr->m_wort)) == 0) return zgr->m_zaehl; else return sucheWort((vergl < 0) ? zgr->m_links : zgr->m_rechts, w); } }; // Wort kommt wiederholt vor

8.3

Smart- und Referenz-Pointer

8.3.1 Lager-Verwaltung mit Referenz-Pointern


Programm 8.6 zeigt eine mgliche Lsung zu dieser Aufgabenstellung.
Programm 8.6 regallager.cpp: Verwaltung von Lager- und Regalbestnden mit Referenzpointern
......... //----------------------------------------------------------------- Klasse CData class CData { public: CData(const char *name=0) : m_count(0), m_artikel(new char[strlen(name)+1]) { strcpy(m_artikel, name); } ~CData() { delete [] m_artikel; } void addRef(void) { m_count++; } void releaseRef(void) { if (--m_count == 0) { cout << "---------" << m_artikel << " nachbestellen---" << endl; delete this; } } int getCount(void) { return m_count; }

char *getName(void) { return m_artikel; } private: int m_count;

char *m_artikel; };

186

8.3 Smart- und Referenz-Pointer

187

//--------------------------------------------------------------- Klasse CRefPtr class CRefPtr { public: CRefPtr(CData *ptr=0) : m_ptr(ptr) { addPtr(); } // Defaultkonstr. CRefPtr(const CRefPtr &ptr) : m_ptr(ptr.m_ptr) { addPtr(); } // Kopierkonstr. ~CRefPtr() { releasePtr(); } //... Destruktor -> gibt Pointer frei const CRefPtr& operator =(const CRefPtr &ptr) { //... Zuweisungsoperator releasePtr(); // akt. Zeiger kann momentan auf anderes Objekt zeigen. // Diese Referenz muss erst erniedrigt werden, bevor // die Referenz des neuen Objektes erhht wird. m_ptr = ptr.m_ptr; addPtr(); return ptr; } CData* operator-> () { return m_ptr; CData& operator* () CData *getPtr(void) private: CData *m_ptr; void addPtr(void) { if (m_ptr) m_ptr->addRef(); } void releasePtr(void) { if (m_ptr) m_ptr->releaseRef(); m_ptr = 0; } }; //---------------------------------------------------------------- Klasse CPlatz class CPlatz { public: CPlatz(const char *name, int size) : m_size(size), m_anzahl(0) { m_slots = new CRefPtr[size]; strcpy(m_name, name); } bool voll(void) { return m_anzahl >= m_size; } bool leer(void) { return m_anzahl == 0; bool get(CRefPtr &slot, const char *name) { if (leer()) return false; for (int i=0; i<m_size; i++) if (m_slots[i].getPtr() != 0 && !strcmp(name, m_slots[i]->getName())) { slot = m_slots[i]; m_slots[i] = CRefPtr(); // Lschen des Pointers in der Tabelle m_anzahl--; return true; } return false; } } //...... Pfeiloperator

{ return *m_ptr; } //...... Sternoperator { return m_ptr; }

187

188

8 Lsungen zum Anhang B

} bool put(const CRefPtr &slot) { if (voll()) return false; m_slots[m_anzahl++] = slot; return true; } void print(const char *artikel) { int i; cout << setw(10) << m_name << ": for (i=0; i<m_size; i++) if (m_slots[i].getPtr() != 0) cout << setw(6) << m_slots[i]->getName() << "(" << m_slots[i]->getCount() << ") "; if (artikel != 0) cout << "---> " << artikel; cout << endl; } ";

private: CRefPtr *m_slots; int m_size, m_anzahl; char }; //------------------------------------------------------------------------- main int main(int argc, char* argv[]) { .......... } m_name[20];

188

Literaturverzeichnis
Die Programiersprache C
C Programming Language; Brian W. Kernighan, Dennis M. Ritchie; Prentice Hall Kernighan und Ritchie sind die Ernder von C und beschreiben in diesem Buch den ersten C-Standard. http://gcc.gnu.org/ Diese Webseite ist die Homepage fr GNU C Compiler gcc und g++. C-Programmierung unter Linux, Unix und Windows; Helmut Herold; millin Verlag Dieses Buch begngt sich nicht mit der Vorstellung der einzelnen C-Sprachelemente, sondern vermittelt auch Einblicke in wichtige Grundlagen der Informatik. Darber hinaus werden zu den einzelnen C-Konstruktionen effektive Programmiertechniken aus der Praxis und typische Anwendungsgebiete im Detail vorgestellt. Zu den wichtigsten Themen nden sich zudem vielfltige Tipps sowie Hinweise zur Vermeidung von Fallgruben, die in C leider nicht allzu selten sind. Mit der im Rahmen dieses Buches entwickelten Grakbibliothek LCGI (Linux C Graphics Interface) ist eine einfache Grakprogrammierung unter Linux mglich. Am Ende jedes Kapitels nden sich bungen, die dem Leser eine Selbstkontrolle der jeweils erworbenen Kenntnisse ermglichen. C-Kompaktreferenz; Helmut Herold; Addison-Wesley Verlag Dieses Buch ist eine Kurzfassung zur Programmiersprache C, die fr das schnelle Nachschlagen von C-Funktionen, C-Konstrukten und allgemeinen Algorithmen konzipiert wurde. Es beschreibt kurz und prgnant die einzelnen C-Konstrukte und standardisierten Headerdateien. Zudem stellt es wesentliche Programmiertechniken, wichtige Algorithmen und ntzliche Programme vor, die beim tagtglichen Programmieren hilfreich sein knnen.

Die Programmiersprache C++


The C++-Programming Language; Bjarne Stroustrup; Addison-Wesley Verlag In diesem Buch prsentiert der Autor Bjarne Stroustrup, Urheber von C++, die vollstndige Spezikation fr die Programmiersprache C++ und die Standardbibliothek.

189

190

8 Lsungen zum Anhang B

Die Standardbibliothek von C++; Helmut Herold; Addison-Wesley Verlag Die C++-Standardbibliothek ist eine Klassenbibliothek, deren Schwerpunkt auf allgemein gltigen Datenstrukturen und Algorithmen liegt. In diesem von der Homepage von Addison-Wesley herunterladbaren eBook Die C++Standardbibliothek wird die C++-Standardbibliothek mit ihrem Kernstck, der Standard Template Library (STL), ausfhrlich anhand vieler Beispielprogramme aus Anwendersicht vorgestellt, um dem Leser ein schnelles Verstndnis und Nachschlagen der einzelnen Komponenten und Konstrukte der C++-Standardbibliothek zu ermglichen. Effektiv C++ programmieren; Scott Meyers; Addison-Wesley Verlag In diesem Buch werden 50 Richtlinien vorgestellt, die man einhalten sollte, um klaren, korrekten und efzienten C++-Code zu erzeugen. Mehr Effektiv C++ programmieren; Scott Meyers; Addison-Wesley Verlag Dieses Buch stellt 35 weitere Regeln zu C++ vor, die sehr hilfreich sind, um Programm und Design zu verbessern.

Unied Modeling Language


http://www.uml.org/ Dies ist die Homepage zu UML.

Entwurfsmuster (Design Patterns)


Design Patterns; E. Gamma, R. Helm, R. Johnson, J. Vlissides; Addison-Wesley Verlag Dies ist das Standardwerk zu Design Patterns, das man scherzhaft auch The GoF-Book nennt, wobei GoF fr Gang of Four (Viererbande) steht. Im GoFBuch werden 23 Lsungen zu bestimmten Problemklassen prsentiert.

Die C++-Bibliothek Qt zur portablen GUI-Programmierung


http://www.trolltech.com/ Dies ist die Homepage der Firma Trolltech, die Qt entwickelt hat. Das Qt-Buch Portable GUI-Programmierung unter Linux, Unix, Windows; Helmut Herold; millin Verlag Dieses Buch stellt die Qt-Klassenbibliothek vor, mit der sich leicht GrakOberchen entwerfen lassen, die plattformunabhngig sind und ohne jeglichen Portierungsaufwand unter den heute weit verbreiteten Betriebssystemen Linux/Unix und Windows verwendet werden knnen.

Allgemein zu Linux/Unix
Linux-Unix Grundlagen; Helmut Herold; Addison-Wesley Verlag Dieses Buch ist eine Einfhrung in das Betriebssystem Unix und geht insbesondere auf das immer beliebtere und frei verfgbare System Linux ein.

190

8.3 Smart- und Referenz-Pointer

191

Es macht den Leser anhand von leicht nachvollziehbaren Beispielen mit den grundlegenden Linux/Unix-Kommandos und -Konzepten vertraut. Linux-Unix-Grundlagenreferenz; Helmut Herold; Addison-Wesley Verlag In diesem Buch werden die wichtigsten Linux/Unix-Kommandos nach Anwendungsfllen gegliedert und anhand kleiner Beispiele wird deren typischer Einsatz beschrieben. Eine alphabetische Kommandobersicht hilft dem Leser, nach einzelnen Kommandos zu suchen und sich so schnell zurecht zu nden. Linux-Unix Shells; Helmut Herold; Addison-Wesley Verlag Dieses Buch behandelt die fnf heute am weitest verbreiteten Unix-Shells: Bourne-Shell, Korn-Shell, C-Shell, bash und tcsh. Es beschreibt die einzelnen Shells und ihre Konstrukte ausfhrlich und leicht nachvollziehbar anhand von ber 200 Shell-Programmbeispielen, die online ber den Verlag zu beziehen sind. Die meisten Unix-Systeme bieten standardgem mehrere Shells, manche Systeme wie Linux bieten standardgem sogar alle fnf Shells an. make das Werkzeug zur automatischen Generierung von Programmen; Helmut Herold; Addison-Wesley Verlag Ein Softwarepaket besteht aus mehreren Modulen, die durch ein Netz von Abhngigkeiten miteinander verbunden sind. Nimmt man an einer Stelle dieses Pakets nderungen vor, werden mit Hilfe von make alle von nderungen betroffenen Module automatisch kompiliert. Anwender, Softwareentwickler und Administratoren erfahren hier alles ber die zu Grunde liegende Technik, die Funktionsweise und die Anwendungsgebiete von make. Darber hinaus stellt dieses Buch auch fortgeschrittene Themen wie Techniken fr das Projektmanagement oder die automatische Makele-Generierung vor. Linux-Unix Kurzreferenz; Helmut Herold; Addison-Wesley Verlag Dieses Buch ist eine Kurzreferenz zu den Bchern Linux-Unix Grundlagen, Linux-Unix Shells, make, awk & sed, lex & yacc und Linux-Unix Systemprogrammierung. Es enthlt neben der Beschreibung wichtiger Linux/Unix-Kommandos und -Tools auch eine Kurzfassung zu allen typischen Aufrufformen der zur Systemprogrammierung bentigten Funktionen. Die Systemaufrufe werden dabei ebenso wie alle C-Funktionen nicht nur kurz vorgestellt, sondern oft wird noch ein kleiner Codeausschnitt angegeben, der zeigt, wie diese Funktionen zu verwenden sind.

Systemprogrammierung unter Linux/Unix


Advanced Programming in the Unix Environment; W. R. Stevens; Addison-Wesley Verlag Dies ist das Standardwerk zum Programmieren unter Unix. Es beschreibt die gesamte Breite der Systemaufrufe von BSD4.3 ber SVR4 bis zum POSIXStandard.

191

192

8 Lsungen zum Anhang B

Linux-Unix Systemprogrammierung; Helmut Herold; Addison-Wesley Verlag Das Buch wendet sich an alle, die mehr ber die Interna von Linux/Unix wissen mchten. Es behandelt die Systemprogrammierung unter Linux/Unix und gibt auch Einblicke in die Datenstrukturen und Algorithmen, um dem interessierten Leser die Realisierung von Systemaufrufen und Betriebssystemkonzepten an einem konkreten System zu verdeutlichen. Aufgrund der ber 200 Beispiel- und bungsprogramme eignet sich dieses Buch sowohl zum Selbststudium als auch zum Nachschlagen. Neu hinzugekommen sind in der dritten Auage ein Kapitel zur Netzwerkprogrammierung mit Sockets und ein Kapitel zur Threadprogrammierung.

192

Index
Lsung abc.cpp, 95 abcd.cpp, 95 Abstrakte Fabrik, 151 adapter3.cpp, 158 adapter4.cpp, 158 Aktivittsdiagramm, 153 Anwendungsfalldiagramm, 152 array.cpp, 19 arrayop.h, 117 arrayrotate.cpp, 20 arraystruct.cpp, 27 autofahr.cpp, 94 beobachter3.cpp, 162 bigadd.cpp, 37 bitmap.cpp, 50 blumen.cpp, 94 bruch.cpp, 54 bruch.h, 54 bruchadd.cpp, 90 brucharray.cpp, 90 bruchio.cpp, 56 bruchio.h, 55 bruchmain.cpp, 56 bruchop.cpp, 117 bruchop.h, 115 calculator.cpp, 107 calculator.h, 105 castueb.cpp, 121 copyzuwkonstr.cpp, 49 dackel.cpp, 95 Datum.h, 140 dekokombi2.cpp, 159 dezahexa.cpp, 94 dhondt.cpp, 65 dialekt.cpp, 102 Die C-Datentypen, 33 diffzeich2.cpp, 154 doublearray.cpp, 44 dualbyte.cpp, 24 dualrech.cpp, 120 ehe.cpp, 113 Ente, 150 Entwurfsmuster, 151 EnumGebCout.h, 140 erbauer2.cpp, 155 exceptkonstr.cpp, 179 exceptschachtel.cpp, 133 fahrzeuge.h, 98 fahrzeuge2.h, 100 fahrzeuge3.h, 104 friendviel.cpp, 91 gebtag.cpp, 143 geldstap.cpp, 67 graphline.h, 96 Haftpichtschaden, 152 hai.cpp, 114 Hotelbuchung, 152 IEnumGeb.h, 139 intarray.cpp, 59 intervall.cpp, 28 iterator3.cpp, 161 josephus.cpp, 136 kaestner.cpp, 67 klasse.cpp, 48 komma.cpp, 29 konstrarray.cpp, 27 konstrdestr.cpp, 25 konstrdestr2.cpp, 26 konstrukt.cpp, 23 konstrukt2.cpp, 24 kontocopy.cpp, 43 konvert.cpp, 18 kreis.cpp, 22 kreisconst.cpp, 49 kunde.cpp, 52 lcdwandel.cpp, 170 Lehrer-Kreide-Tafel, 147 life.cpp, 30 liste.h, 182 lkw.cpp, 102 lottoqt.cpp, 168 mastermind.cpp, 175 matrixfunk.cpp, 125 matrixfunk.h, 124 memento3.cpp, 165 mengenarray.cpp, 123 mengenarray.h, 121 monopclass1.cpp, 71 monopclass2.cpp, 72 moo.cpp, 77 mwst.cpp, 35 namsetsort.cpp, 135 negativindex.cpp, 127 nlssupport.cpp, 148 nosetlongjmp.cpp, 180 obj.h, 183 objgroesse.cpp, 34 paraadd.cpp, 57 paraadd.h, 57 parkhaus.cpp, 53 parkplatz.cpp, 104 peano.cpp, 30 person2.cpp, 62 person2.h, 62 personcopy.cpp, 42 pkt.h, 184 polya.cpp, 136 polygon.cpp, 70 primsieb.cpp, 16 PrintContainer.h, 138 proxy3.cpp, 157 punkt.h, 68 queue.cpp, 39 raster.cpp, 172 raubtiere.cpp, 103 rechgross.cpp, 129 rechgross.h, 128 regallager.cpp, 186 Remind.h, 142 rgbzufall.cpp, 167 schuh.cpp, 14 scopestruct.cpp, 26 Segelboot, 149 setlongjmp.cpp, 180 signum.cpp, 11 sortfile.cpp, 17 stackexcept.h, 134 stacktempl.cpp, 132 stackunwind.cpp, 133 statcount.cpp, 53 stdexcept.cpp, 133 stlumsatz.cpp, 138 str.h, 184 swap.cpp, 12 swaptempl.cpp, 131 taschrech.cpp, 86 taschrech.h, 84 taschrech2.cpp, 89 templausgabe.cpp, 131 terminate.cpp, 133 terminfinder.cpp, 60 terminfinder.h, 60 terminfindermain.cpp, 60 Tiere mit/ohne Beine, 32 typeid.cpp, 146

193

194
uhrzeit.cpp, 115 unexpected.cpp, 133 Unterschiedliche Menschen, 33 vectoradd.cpp, 13 vieleck.cpp, 68 virtbytes.cpp, 102 virtdestr3.cpp, 103 virtfrank.cpp, 102 virtrech.cpp, 103 wortstat.cpp, 185 zeichzaehl.cpp, 15 zeitrechner.cpp, 36 zoo.cpp, 112 zoo.h, 110 zustand3.cpp, 163 Programm adapter3.cpp, 158 adapter4.cpp, 158 array.cpp, 19 arrayop.h, 117 arrayrotate.cpp, 20 arraystruct.cpp, 27 beobachter3.cpp, 162 bigadd.cpp, 37 bitmap.cpp, 50 bruch.cpp, 55 bruch.h, 54 bruchadd.cpp, 90 brucharray.cpp, 90 bruchio.cpp, 56 bruchio.h, 55 bruchmain.cpp, 56 bruchop.cpp, 117 bruchop.h, 115 calculator.cpp, 107 calculator.h, 105 Datum.h, 140 dekokombi2.cpp, 159 dhondt.cpp, 65 diffzeich2.cpp, 154 doublearray.cpp, 44 dualrech.cpp, 120 ehe.cpp, 113 EnumGebCout.h, 140 erbauer2.cpp, 155 fahrzeuge.cpp, 98 fahrzeuge2.h, 100 fahrzeuge3.h, 104 gebtag.cpp, 143 geldstap.cpp, 67 graphline.cpp, 96 hai.cpp, 114 IEnumGeb.h, 139 intarray.cpp, 59 intervall.cpp, 28 iterator3.cpp, 161 josephus.cpp, 136 kaestner.cpp, 67 klasse.cpp, 48 komma.cpp, 29 kontocopy.cpp, 43 konvert.cpp, 18 kreis.cpp, 22 kreisconst.cpp, 49 kunde.cpp, 52 lcdwandel.cpp, 170 life.cpp, 30 liste.h, 182 lkw.cpp, 102 lottoqt.cpp, 168 mastermind.cpp, 175 matrixfunk.cpp, 125 matrixfunk.h, 124 memento3.cpp, 165 mengenarray.cpp, 123 mengenarray.h, 121 monopclass1.cpp, 72 monopclass2.cpp, 73 moo.cpp, 77 mwst.cpp, 35 namsetsort.cpp, 135 negativindex.cpp, 127 nlssupport.cpp, 148 nosetlongjmp.cpp, 180 obj.h, 183 paraadd.cpp, 58 paraadd.h, 57

Index
parkhaus.cpp, 53 parkplatz.cpp, 104 peano.cpp, 30 person2.cpp, 62 person2.h, 62 personcopy.cpp, 42 pkt.h, 184 polya.cpp, 136 polygon.cpp, 70 primsieb.cpp, 16 PrintContainer.h, 138 proxy3.cpp, 157 punkt.h, 68 queue.cpp, 40 raster.cpp, 172 rechgross.cpp, 129 rechgross.h, 128 regallager.cpp, 186 Remind.h, 142 rgbzufall.cpp, 167 schuh.cpp, 14 sortfile.cpp, 17 stackexcept.h, 134 stacktempl.cpp, 132 statcount2.cpp, 53 stlumsatz.cpp, 138 str.h, 184 swap.cpp, 12 swaptempl.cpp, 131 taschrech.cpp, 86 taschrech.h, 84 taschrech2.cpp, 89 terminfinder.cpp, 61 terminfinder.h, 60 terminfindermain.cpp, 60 uhrzeit.cpp, 115 vectoradd.cpp, 13 vieleck.cpp, 69 wortstat.cpp, 185 zeichzaehl.cpp, 15 zeitrechner.cpp, 36 zoo.cpp, 112 zoo.h, 110 zustand3.cpp, 163

194