pdf-Datei - Extras Springer

Transcription

pdf-Datei - Extras Springer
Aufgaben und Lösungen
zu
Richard Kaiser
Aufgaben und Lösungen
zu
C++ mit dem
Borland C++Builder
Einführung in den ANSI/ISO-Standard und die objektorientierte WindowsProgrammierung
Version vom 11.11.2001
Prof. Richard Kaiser
Schwärzlocher Straße 53
72070 Tübingen
[email protected]
www.rkaiser.de
Dieses Werk ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere die der Übersetzung, des Nachdrucks, des Vortrags,
der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder der Vervielfältigung auf anderen Wegen und der
Speicherung in Datenverarbeitungsanlagen, bleiben, auch bei nur auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Werkes
oder von Teilen dieses Werkes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtsgesetzes der
Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig.
Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes.
Der Springer-Verlag ist nicht Urheber der Daten und Programme. Weder der Springer-Verlag noch der Autor übernehmen Haftung für die CDROM und das Buch, einschließlich ihrer Qualität, Handels- oder Anwendungseignung. In keinem Fall übernehmen der Springer-Verlag oder der
Autor Haftung für direkte, indirekte, zufällige oder Folgeschäden, die sich aus der Nutzung der CD-ROM oder des Buches ergeben.
Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu
der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann
benutzt werden dürften.
Bibliografische Angaben zu „C++ mit dem Borland C++Builder“
ISSN 1439-5428
ISBN 3-540-62994-7 Springer-Verlag Berlin Heidelberg New York
Die Deutsche Bibliothek – CIP-Einheitsaufnahme
Kaiser, Richard: C++ mit dem Borland C++ Builder : Einführung in den ANSI/ISO-Standard und die objektorientierte WindowsProgrammierung / Richard Kaiser. –
Berlin; Heidelberg; New York; Barcelona; Hongkong; London; Mailand; Paris;
Tokio: Springer, 2002
(Xpert.press)
ISBN 3-540-62994-7
Springer-Verlag Berlin Heidelberg New York
ein Unternehmen der BertelsmannSpringer Science+Business Media GmbH
http://www.springer.de
© Springer-Verlag Berlin Heidelberg 2002
Printed in Germany
Umschlaggestaltung: KünkelLopka, Heidelberg
Satz: Belichtungsfähige Daten vom Autor
Gedruckt auf säurefreiem Papier – SPIN: 10629246
33/3142 GF
543210
Vorwort
Dieser Text enthält alle Aufgaben und Lösungen zu meinem Buch „C++ mit dem Borland C++Builer“. Die
Lösungen sind auch als vollständige Projekte auf der CD im Buch.
Damit der Bezug zwischen dem Buchtext und den Aufgaben leichter hergestellt werden kann, habe ich alle
Kapitelüberschriften aus dem Buch in diese Aufgabensammlung übernommen.
Anregungen, Korrekturhinweise und Verbesserungsvorschläge sind willkommen. Bitte senden Sie diese an die
Adresse [email protected].
Falls Sie Interesse an einer gedruckten Version dieser Aufgaben- und Lösungssammlung haben, bitte eine kurze
EMail. Der Verlag wird sie dann eventuell auch als Buch veröffentlichen.
Tübingen, im November 2001
Richard Kaiser
Geleitwort zu „C++ mit dem Borland C++Builder“
Das Programmieren unter C++ gilt als die Königsklasse der objektorientierten Applikations-Entwicklung: Anwender
nutzen C++, um universell einsetzbare, modulare Programme zu erstellen. Wer diese Sprache beherrscht, profitiert von
einem beispiellosen Funktionsumfang und von der Option, plattformunabhängig zu arbeiten. Das war anfangs nur
hochgradig versierten Profis vorbehalten. Sie allein waren in der Lage, der Komplexität des C++-Quellcodes Herr zu
werden.
Längst aber stehen die Vorzüge von C++ auch all jenen zur Verfügung, die nur gelegentlich oder schlicht und
ergreifend aus Freude am Tüfteln Applikationen erstellen. Einen wesentlichen Beitrag zur „Demokratisierung“ der
objektorientierten Programmierung leisten integrierte RAD-Systeme (Rapid Application Development) wie der
C++Builder von Borland.
Ganz gleich ob Profi oder Einsteiger: Die C++-Version der erfolgreichen Object Pascal-Lösung Borland Delphi bietet
Programmierern eine visuelle Entwicklungsumgebung, mit der sie einfach und rasch objektorientierte Windows-Applikationen schreiben können. Der C++Builder verfügt über eine umfangreiche Palette an fertigen Komponenten und
erleichtert seit der Version 5 auch die Entwicklung von Web-Applikationen. Wer grafische Benutzeroberflächen bauen
will, stellt diese einfach mit wenigen Handgriffen per Maus zusammen. Das ist die Basis für ein schnelles, effizientes
und komfortables Arbeiten. Kurzum: Mit dem C++Builder wird die Applikations-Entwicklung von der langwierigen
Fleißaufgabe zur zielorientierten Kopfarbeit.
Das vorliegende Buch ist eine systematische Einführung in die Arbeit mit C++ und dem Borland C++Builder.
Ausführlich und praxisnah schildert Richard Kaiser die Konzepte und Elemente der Programmiersprache und der
Entwicklungsumgebung. Mit zahlreichen Beispielen und Übungsaufgaben erschließt er auch Lesern ohne
Vorkenntnisse die Logik objektorientierten Programmierens.
Borland wünscht allen Nutzern dieses hervorragenden Lehrbuchs und Nachschlagewerks viel Spaß und Erfolg bei der
Arbeit mit dem C++Builder.
Jason Vokes
European Product Line Manager - RAD Products and InterBase
Vorwort zu „C++ mit dem Borland C++Builder“
Dieses Buch entstand ursprünglich aus dem Wunsch, in meinen Vorlesungen über C++ nicht nur Textfensterprogramme, sondern Programme für eine grafische Benutzeroberfläche zu entwickeln. Mit dem C++Builder von
Borland stand 1997 erstmals ein Entwicklungssystem zur Verfügung, das so einfach zu bedienen war, dass man es auch
in Anfängervorlesungen einsetzen kann, ohne dabei Gefahr zu laufen, dass die Studenten nur noch mit dem
Entwicklungssystem kämpfen und gar nicht mehr zum Programmieren kommen.
Angesichts der damals anstehenden Verabschiedung des ANSI/ISO-Standards von C++ lag es nahe, in diesem
einführenden Lehrbuch auch gleich den gesamten Sprachumfang des Standards umfassend darzustellen. Mir war
allerdings nicht klar, auf welche Arbeit ich mich damit eingelassen hatte. Ich hatte weder vor, vier Jahre an diesem
Buch zu schreiben, noch einen Wälzer mit 1100 Seiten zu produzieren.
Als ich dann die Möglichkeit bekam, Kurse für erfahrene Praktiker aus der Industrie zu halten, wurde ich mit einer
Fülle von Anregungen aus ihrer täglichen Arbeit konfrontiert. Diese gaben dem Buch enorme Impulse.
Die Programmiersprache C++ wurde als Obermenge der Programmiersprache C entworfen. Dieser Entscheidung
verdankt C++ sicher seine weite Verbreitung. Sie hat aber auch dazu geführt, dass oft weiterhin wie in C programmiert
wird und lediglich ein C++-Compiler anstelle eines C-Compilers verwendet wird. Dabei werden viele Vorteile von C++
verschenkt. Um nur einige zu nennen:
– In C++ werden die fehleranfälligen Zeiger viel seltener als in C benötigt.
– Die Stringklassen lassen sich wesentlich einfacher und risikoloser als die nullterminierten Strings von C verwenden.
– Die Containerklassen der C++-Standardbibliothek haben viele Vorteile gegenüber Arrays, selbstdefinierten
verketteten Listen oder Bäumen.
– Exception-Handling bietet eine einfache Möglichkeit, auf Fehler zu reagieren.
– Objektorientierte Programmierung ermöglicht übersichtlichere Programme.
– Templates sind die Basis für eine außerordentlich vielseitige Standardbibliothek.
Ich habe versucht, bei allen Konzepten nicht nur die Sprachelemente und ihre Syntax zu beschreiben, sondern auch
Kriterien dafür anzugeben, wann und wie man sie sinnvoll einsetzen kann. Deshalb wurde z.B. mit der
objektorientierten Programmierung eine Einführung in die objektorientierte Analyse und das objektorientierte Design
verbunden. Ohne die Beachtung von Design-Regeln schreibt man leicht Klassen, die der Compiler zwar übersetzen
kann, die aber kaum hilfreich sind.
Man hört immer wieder die Meinung, dass C++ viel zu schwierig ist, um es als einführende Programmiersprache
einzusetzen. Dieses Buch soll ein in mehreren Jahren erprobtes Gegenargument zu dieser Meinung sein. Damit will ich
aber die Komplexität von C++ überhaupt nicht abstreiten.
Zahlreiche Übungsaufgaben geben dem Leser die Möglichkeit, die Inhalte praktisch anzuwenden und so zu vertiefen.
Da man Programmieren nur lernt, indem man es tut, möchte ich ausdrücklich dazu ermuntern, zumindest einen Teil
der Aufgaben zu lösen und sich dann selbst neue Aufgaben zu stellen. Der Schwierigkeitsgrad der Aufgaben reicht von
einfachen Wiederholungen des Textes bis zu kleinen Projektchen, die ein gewisses Maß an selbständiger Arbeit
erfordern. Die Lösungen der meisten Aufgaben findet man auf der beiliegenden CD und auf meiner Internetseite
http://www.rkaiser.de.
Anregungen, Korrekturhinweise und Verbesserungsvorschläge sind willkommen. Bitte senden Sie diese an die Adresse
[email protected].
Bei allen meinen Schulungskunden und ganz besonders bei Herrn Welsner und der Alcatel University der Alcatel SEL
AG Stuttgart bedanke ich mich für die Möglichkeit, dieses Manuskript in zahlreichen Kursen mit erfahrenen Praktikern
weiterzuentwickeln. Ohne die vielen Anregungen aus diesen Kursen hätte es weder diesen Praxisbezug noch diesen
Umfang erreicht. Peter Schwalm hat große Teile des Manuskripts gelesen und es in vielen Diskussionen über diffizile
Fragen mitgestaltet. Mein Sohn Alexander hat als perfekter Systembetreuer dafür gesorgt, dass die Rechner immer
liefen und optimal installiert waren.
Die Unterstützung von Dr. Hans Wössner und seinem Team vom Springer-Verlag hätte nicht besser sein können. Seine
Hilfsbereitschaft und seine überragende fachliche Kompetenz waren immer wieder beeindruckend. „Meiner“ Lektorin
Ruth Abraham verdankt dieses Buch eine in sich geschlossene Form, die ich allein nicht geschafft hätte. Die technische
Herstellung war bei Gabi Fischer in erfahrenen guten Händen. Herrn Engesser danke ich für die gute Zusammenarbeit
beim Abschluss des Projekts.
Tübingen, im Oktober 2001
Richard Kaiser
Inhalt
1
Die Entwicklungsumgebung ...................................................................................................1
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1.10
2
Visuelle Programmierung: Ein erstes kleines Programm....................................................................... 1
Erste Schritte in C++............................................................................................................................ 1
Der Quelltexteditor............................................................................................................................... 1
Projekte, Projektdateien und Projektoptionen ........................................................................................ 1
Einige Tipps zur Arbeit mit Projekten .................................................................................................. 1
Die Online-Hilfe................................................................................................................................... 1
Das lokale Menü................................................................................................................................... 1
Die Symbolleiste................................................................................................................................... 1
Programmierhilfen ............................................................................................................................... 1
Packages und eigenständig ausführbare Programme ............................................................................. 1
Die Komponentenpalette ........................................................................................................2
2.1 Die Online-Hilfe zu den Komponenten ................................................................................................. 2
2.2 Namen.................................................................................................................................................. 2
2.3 Labels und Datentypen ......................................................................................................................... 3
2.4 Funktionen, Methoden und die Komponente TEdit ............................................................................... 3
2.5 Memos, ListBoxen, ComboBoxen und die Klasse TStrings ................................................................... 4
2.6 Buttons und Ereignisse ......................................................................................................................... 5
2.7 CheckBoxen, RadioButtons und einfache if-Abfragen........................................................................... 5
2.8 Die Container GroupBox, Panel und RadioGroup................................................................................. 6
2.9 ScrollBar .............................................................................................................................................. 6
2.10 Hauptmenüs und Popup-Menüs ............................................................................................................ 7
2.11 Die Komponenten der Seite Dialoge ..................................................................................................... 7
2.12 Der Aufruf von eigenen Formularen und modale Fenster ⊕.................................................................. 8
2.13 Die Komponenten der Seite „Zusätzlich“ ⊕.......................................................................................... 8
2.14 Einige Komponenten der Seiten Win32 und Win 3.1............................................................................ 9
2.14.1 Mehrseitige Dialoge ⊕ ............................................................................................................... 9
2.14.2 ImageList und ListView ⊕ ......................................................................................................... 9
2.14.3 Komponenten zur Anzeige hierarchischer Datenstrukturen ⊕ .................................................... 9
2.14.4 Komponenten zur Größenanpassung anderer Komponenten ⊕................................................... 9
2.14.5 Formatierte Texte....................................................................................................................... 9
2.14.6 Statusanzeigen ⊕ ....................................................................................................................... 9
2.14.7 Weitere Komponenten ⊕............................................................................................................ 9
2.15 Einige Komponenten der Seite System ⊕............................................................................................ 10
2.16 ActiveX-Komponenten ⊕ ................................................................................................................... 10
2.17 Vordefinierte DialogBoxen ⊕ ............................................................................................................. 10
3
Elementare Datentypen und Anweisungen...........................................................................11
3.1 Windows-Programme und Units......................................................................................................... 11
3.2 Syntaxregeln ...................................................................................................................................... 11
3.3 Variablen und Bezeichner .................................................................................................................. 11
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
xii
Inhalt
3.4 Ganzzahldatentypen ........................................................................................................................... 11
3.4.1
Die interne Darstellung von Ganzzahlwerten ........................................................................... 11
3.4.2
Der Datentyp von Ganzzahlliteralen......................................................................................... 11
3.4.3
Zuweisungen und Standardkonversionen bei Ganzzahlausdrücken ........................................... 12
3.4.4
Operatoren und die „üblichen arithmetischen Konversionen“................................................... 12
3.4.5
Der Datentyp bool.................................................................................................................... 12
3.4.6
Die char-Datentypen und der ASCII- und ANSI-Zeichensatz ................................................... 12
3.4.7
Der Datentyp __int64............................................................................................................... 13
3.5 Kontrollstrukturen und Funktionen..................................................................................................... 13
3.5.1
Die if- und die Verbundanweisung ........................................................................................... 13
3.5.2
Wiederholungsanweisungen ..................................................................................................... 13
3.5.3
Funktionen und der Datentyp void ........................................................................................... 13
3.6 Der integrierte Debugger .................................................................................................................... 14
3.7 Gleitkommadatentypen....................................................................................................................... 15
3.7.1
Die interne Darstellung von Gleitkommawerten ....................................................................... 15
3.7.2
Der Datentyp von Gleitkommaliteralen .................................................................................... 15
3.7.3
Implizite Konversionen ............................................................................................................ 15
3.7.4
Mathematische Funktionen ...................................................................................................... 15
3.8 Syntaxregeln für einfache Deklarationen ............................................................................................ 18
3.9 Zeiger, Strings und dynamisch erzeugte Variablen ............................................................................. 18
3.9.1
Die Definition von Zeigern....................................................................................................... 18
3.9.2
Der Adressoperator, Zuweisungen und generische Zeiger......................................................... 18
3.9.3
Dynamisch erzeugte Variablen: new und delete........................................................................ 18
3.9.4
Zeiger als Parameter und die Zeigertypen der Windows-API .................................................... 18
3.9.5
Zeigerarithmetik ...................................................................................................................... 18
3.9.6
Nullterminierte Strings und Zeiger auf char............................................................................. 18
3.9.7
Zeiger auf Zeiger auf Zeiger auf ... ........................................................................................... 18
3.9.8
Referenztypen und Referenzparameter ..................................................................................... 18
3.10 Konstanten ......................................................................................................................................... 19
3.11 Deklarationen mit typedef und typeid-Ausdrücke ............................................................................... 19
3.12 Aufzählungstypen............................................................................................................................... 19
3.13 Kommentare und interne Programmdokumentation............................................................................ 20
3.14 Präprozessoranweisungen ................................................................................................................... 20
3.14.1 Die include-Anweisung............................................................................................................ 20
3.14.2 Makros ⊕................................................................................................................................. 20
3.14.3 Bedingte Kompilation ⊕ .......................................................................................................... 20
3.14.4 Pragmas ⊕ ............................................................................................................................... 20
4
Strukturierte Datentypen und vordefinierte Klassen...........................................................21
4.1 Die Stringklassen AnsiString und string ............................................................................................. 21
4.1.1
Die Verwendung von Elementfunktionen vordefinierter Klassen .............................................. 21
4.1.2
Gemeinsamkeiten und Unterschiede der Stringklassen ............................................................. 21
4.1.3
Funktionen für die Klasse AnsiString ....................................................................................... 21
4.1.4
Einige Elementfunktionen der Klasse string............................................................................. 21
4.2 Arrays und Container ......................................................................................................................... 23
4.2.1
Eindimensionale Arrays ........................................................................................................... 23
4.2.2
Arrays als Container ................................................................................................................ 24
4.2.3
Mehrdimensionale Arrays ........................................................................................................ 26
4.2.4
Arrays, Zeiger und Zeigerarithmetik ........................................................................................ 27
4.2.5
Arrays als Funktionsparameter................................................................................................. 28
4.2.6
Dynamisch erzeugte Arrays...................................................................................................... 28
4.2.7
Array-Eigenschaften der VCL.................................................................................................. 29
4.3 Sequenzielle Container der Standardbibliothek................................................................................... 29
4.3.1
Die Container-Klasse vector..................................................................................................... 29
4.3.2
Algorithmen der Standardbibliothek ........................................................................................ 29
4.3.3
Die Container-Klassen list und deque ...................................................................................... 29
4.3.4
Die Container-Adapter stack, queue und priority_queue .......................................................... 29
4.3.5
Container mit Zeigern .............................................................................................................. 29
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
Inhalt
xiii
4.3.6
Die Container-Klasse bitset ⊕.................................................................................................. 29
4.4 Strukturen und Klassen ...................................................................................................................... 29
4.4.1
Mit struct definierte Klassen .................................................................................................... 29
4.4.2
Verkettete Listen ...................................................................................................................... 31
4.4.3
„Memory leaks“ und die Überwachung des Heap ⊕.................................................................. 32
4.4.4
Datenstrukturen mit generischen Zeigern ⊕ ............................................................................. 32
4.4.5
Mit union definierte Klassen ⊕ ................................................................................................ 32
4.4.6
Die Datentypen TVarRec und Variant ⊕ .................................................................................. 32
4.4.7
Bitfelder ⊕............................................................................................................................... 32
4.5 Einige Klassen der VCL ..................................................................................................................... 32
4.5.1
Ein wenig Grafik: TCanvas, TImage und TPrinter ⊕ ............................................................... 32
4.5.2
Die Steuerung von MS-Office: Word-Dokumente erzeugen ⊕ .................................................. 35
4.5.3
Internet-Komponenten ⊕ ......................................................................................................... 35
4.5.4
Visuell gestaltete Ausdrucke mit QuickReport ⊕...................................................................... 35
4.5.5
Kaufmännische Rechnungen: Die Klassen Currency und BCD ⊕............................................. 35
4.5.6
Klassen und Funktionen zu Uhrzeit und Kalenderdatum ⊕ ...................................................... 35
4.5.7
Die Klasse Set ⊕ ...................................................................................................................... 36
4.6 Dateien............................................................................................................................................... 36
4.6.1
Stream-Variablen, ihre Verbindung mit Dateien und ihr Zustand............................................. 37
4.6.2
Fehler und der Zustand von Stream-Variablen ......................................................................... 37
4.6.3
Lesen und Schreiben von Binärdaten mit read und write.......................................................... 37
4.6.4
Lesen und Schreiben von Daten mit den Operatoren << und >>............................................... 38
4.6.5
Manipulatoren und Funktionen zur Formatierung von Texten .................................................. 40
4.6.6
Textbildschirm-Anwendungen ................................................................................................. 40
4.6.7
Stringstreams ........................................................................................................................... 40
4.6.8
Dateibearbeitung im Direktzugriff ⊕........................................................................................ 41
4.6.9
Sortieren, Mischen und Gruppenverarbeitung ⊕ ...................................................................... 41
4.6.10 C-Funktionen zur Dateibearbeitung ⊕...................................................................................... 42
4.7 Assoziative Container......................................................................................................................... 42
4.7.1
Die Container set und multiset ................................................................................................. 42
4.7.2
Die Container map und multimap............................................................................................. 42
4.7.3
Iteratoren der assoziativen Container ....................................................................................... 42
4.8 Die numerischen Klassen der Standardbibliothek ............................................................................... 44
4.8.1
Komplexe Zahlen ⊕................................................................................................................. 44
4.8.2
Valarrays und Slices ⊕............................................................................................................. 44
5
Anweisungen und Ausdrücke ...............................................................................................46
5.1 Die Ausdrucksanweisung.................................................................................................................... 46
5.2 Ausdrücke .......................................................................................................................................... 46
5.2.1
Primäre Ausdrücke ⊕............................................................................................................... 46
5.2.2
Postfix-Ausdrücke ⊕ ................................................................................................................ 46
5.2.3
Unäre Ausdrücke ⊕.................................................................................................................. 46
5.2.4
Typkonversionen in Typecast-Schreibweise ⊕.......................................................................... 46
5.2.5
Zeiger auf Klassenelemente ⊕.................................................................................................. 46
5.2.6
Multiplikative Operatoren ⊕ .................................................................................................... 46
5.2.7
Additive Operatoren ⊕............................................................................................................. 46
5.2.8
Shift-Operatoren ⊕ .................................................................................................................. 46
5.2.9
Vergleichsoperatoren ⊕ ........................................................................................................... 46
5.2.10 Gleichheitsoperatoren ⊕........................................................................................................... 46
5.2.11 Bitweise Operatoren ⊕ ............................................................................................................. 46
5.2.12 Logische Operatoren ⊕ ............................................................................................................ 46
5.2.13 Der Bedingungsoperator ⊕....................................................................................................... 46
5.2.14 Konstante Ausdrücke ⊕ ........................................................................................................... 46
5.2.15 Zuweisungsoperatoren.............................................................................................................. 46
5.2.16 Der Komma-Operator ⊕ .......................................................................................................... 46
5.2.17 L-Werte und R-Werte ⊕........................................................................................................... 46
5.2.18 Die Priorität und Assoziativität der Operatoren ⊕ .................................................................... 46
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
xiv
Inhalt
5.2.19 Alternative Zeichenfolgen ⊕ .................................................................................................... 46
5.2.20 Explizite Typkonversionen ⊕................................................................................................... 48
5.3 Ein wenig Programmierlogik: Symbolische Ausführung..................................................................... 48
5.4 Die Deklarationsanweisung ................................................................................................................ 49
5.5 Die Verbundanweisung und die Blockstruktur von C++ ..................................................................... 49
5.6 Lebensdauer und Speicherklassenspezifizierer .................................................................................... 50
5.7 Auswahlanweisungen ......................................................................................................................... 50
5.7.1
Die if-Anweisung ..................................................................................................................... 50
5.7.2
Die switch-Anweisung.............................................................................................................. 52
5.7.3
Ein wenig Programmierlogik für Auswahlanweisungen ........................................................... 52
5.8 Wiederholungsanweisungen ............................................................................................................... 53
5.8.1
Die while-Anweisung ............................................................................................................... 53
5.8.2
Die do-Anweisung ................................................................................................................... 53
5.8.3
Die for-Anweisung................................................................................................................... 53
5.8.4
Endlosschleifen, Abbruchbedingungen und Windows............................................................... 54
5.8.5
Ein wenig Programmierlogik für Schleifen............................................................................... 57
5.9 Die Sprunganweisungen goto, break und continue ............................................................................. 59
5.10 Exception-Handling............................................................................................................................ 59
5.10.1 Die try-Anweisung................................................................................................................... 59
5.10.2 Exception-Handler und Exceptions der Standardbibliothek ...................................................... 59
5.10.3 Vordefinierte Exceptions der VCL ........................................................................................... 59
5.10.4 Der Programmablauf bei Exceptions ........................................................................................ 59
5.10.5 Das vordefinierte Exception-Handling der VCL ....................................................................... 59
5.10.6 throw-Ausdrücke und selbst definierte Exceptions.................................................................... 59
5.10.7 Fehler, Exceptions und die Korrektheit von Programmen......................................................... 59
5.10.8 Die Freigabe von Ressourcen bei Exceptions ............................................................................ 59
5.10.9 Exceptions in <math.h> und die Funktion _matherr ⊕............................................................. 61
5.10.10 Die Klasse auto_ptr ⊕ ............................................................................................................ 61
5.10.11 Exception-Spezifikationen ...................................................................................................... 61
5.10.12 Die Funktion terminate ⊕....................................................................................................... 61
5.10.13 Das Win32-Exception-Handling mit try-__except ⊕............................................................... 61
6
Funktionen............................................................................................................................62
6.1 Die Definition und der Aufruf von Funktionen ................................................................................... 62
6.2 Die Verwaltung von Funktionsaufrufen über den Stack ...................................................................... 62
6.3 Funktionen mit Parametern ................................................................................................................ 62
6.3.1
Werteparameter........................................................................................................................ 62
6.3.2
„Call by reference“ mit Referenzparametern............................................................................. 62
6.3.3
Zeiger als Parameter ................................................................................................................ 62
6.3.4
Konstante Parameter ................................................................................................................ 62
6.3.5
Seiteneffekte und die Reihenfolge von Auswertungen............................................................... 62
6.3.6
Default-Argumente .................................................................................................................. 63
6.3.7
Der Datentyp einer Funktion .................................................................................................... 63
6.3.8
Zeiger auf Funktionen .............................................................................................................. 63
6.3.9
Unspezifizierte Anzahl von Argumenten ⊕.............................................................................. 67
6.3.10 Die Funktionen main bzw. WinMain und ihre Parameter.......................................................... 67
6.3.11 Der Aufruf von Funktionen aus Delphi im C++Builder ⊕ ........................................................ 67
6.3.12 Traditionelle K&R-Funktionsdefinitionen ⊕ ............................................................................ 67
6.3.13 Aufrufkonventionen ⊕ ............................................................................................................. 67
6.4 Schrittweise Verfeinerung als Entwurfstechnik................................................................................... 67
6.5 Etwas Programmierlogik und -stil für Funktionen .............................................................................. 67
6.6 Rekursion ........................................................................................................................................... 67
6.6.1
Quicksort ................................................................................................................................. 67
6.6.2
Ein rekursiv absteigender Parser .............................................................................................. 67
6.6.3
Rekursiv definierte Kurven ⊕................................................................................................... 67
6.6.4
Indirekte Rekursion ⊕.............................................................................................................. 67
6.6.5
Rekursive Datenstrukturen und binäre Suchbäume................................................................... 70
6.6.6
Rekursive Datenstrukturen in der Standardbibliothek von C++ ................................................ 70
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
Inhalt
xv
6.7 Inline-Funktionen............................................................................................................................... 71
6.8 Überladene Funktionen....................................................................................................................... 71
6.9 Überladene Operatoren mit globalen Operatorfunktionen ................................................................... 72
6.9.1
Globale Operatorfunktionen ..................................................................................................... 72
6.9.2
Die Inkrement- und Dekrementoperatoren ............................................................................... 72
6.9.3
Referenzen als Funktionswerte ................................................................................................. 72
6.9.4
Die Ein- und Ausgabe von selbst definierten Datentypen.......................................................... 72
7
Modulare Programmierung und Namensbereiche ...............................................................74
7.1 Separate Kompilation und statische Bibliotheken ............................................................................... 74
7.1.1
Projekte im C++Builder ⊕ ....................................................................................................... 74
7.1.2
Bindung ⊕ ............................................................................................................................... 74
7.1.3
Deklarationen und Definitionen ⊕ ........................................................................................... 74
7.1.4
Die „One Definition Rule“ ⊕ ................................................................................................... 74
7.1.5
Header-Dateien und Bibliotheken ⊕......................................................................................... 74
7.1.6
Der Aufruf von in C geschriebenen Funktionen ⊕.................................................................... 74
7.2 Dynamic Link Libraries (DLLs) ......................................................................................................... 76
7.2.1
DLLs erzeugen ⊕..................................................................................................................... 76
7.2.2
Implizit geladene DLLs ⊕........................................................................................................ 76
7.2.3
Explizit geladene DLLs ⊕........................................................................................................ 76
7.2.4
Hilfsprogramme zur Identifizierung von Funktionen in DLLs ⊕ .............................................. 76
7.2.5
DLL-Funktionen mit visuell gestalteten Komponenten ⊕ ......................................................... 76
7.2.6
Projektgruppen ⊕..................................................................................................................... 76
7.2.7
Batch-Dateien ⊕ ...................................................................................................................... 76
7.3 Namensbereiche ................................................................................................................................. 77
7.3.1
Die Definition von benannten Namensbereichen ...................................................................... 77
7.3.2
Die Verwendung von Namen aus Namensbereichen ................................................................. 77
7.3.3
Aliasnamen für Namensbereiche .............................................................................................. 77
7.3.4
Unbenannte Namensbereiche ................................................................................................... 77
7.3.5
Module und das Geheimnisprinzip........................................................................................... 77
8
Objektorientierte Programmierung .....................................................................................79
8.1 Klassen............................................................................................................................................... 79
8.1.1
Datenelemente und Elementfunktionen .................................................................................... 79
8.1.2
Der Gültigkeitsbereich von Klassenelementen .......................................................................... 79
8.1.3
Objekte und die Zugriffsrechte private und public .................................................................... 79
8.1.4
Der Aufruf von Elementfunktionen und der this-Zeiger............................................................ 79
8.1.5
Konstruktoren und Destruktoren .............................................................................................. 79
8.1.6
OO Analyse und Design: Der Entwurf von Klassen.................................................................. 84
8.1.7
Ein wenig Programmierlogik: Klasseninvarianten und Korrektheit .......................................... 84
8.1.8
UML-Diagramme für Klassen und Objekte .............................................................................. 84
8.2 Klassen als Datentypen....................................................................................................................... 84
8.2.1
Der Standardkonstruktor .......................................................................................................... 84
8.2.2
Objekte als Klassenelemente und Elementinitialisierer ............................................................. 84
8.2.3
friend-Funktionen und -Klassen ............................................................................................... 85
8.2.4
Überladene Operatoren als Elementfunktionen......................................................................... 85
8.2.5
Der Copy-Konstruktor.............................................................................................................. 87
8.2.6
Der Zuweisungsoperator = für Klassen..................................................................................... 87
8.2.7
Benutzerdefinierte Konversionen ............................................................................................. 90
8.2.8
Explizite Konstruktoren ⊕ ....................................................................................................... 90
8.2.9
Statische Klassenelemente........................................................................................................ 90
8.2.10 Konstante Klassenelemente und Objekte .................................................................................. 90
8.2.11 Weitere Deklarationsmöglichkeiten in Klassen ⊕..................................................................... 90
8.2.12 Klassen und Header-Dateien .................................................................................................... 90
8.2.13 Überladene Operatoren für new und delete ⊕ ........................................................................... 90
8.3 Vererbung .......................................................................................................................................... 91
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
xvi
Inhalt
8.3.1
Die Elemente von abgeleiteten Klassen .................................................................................... 91
8.3.2
Zugriffsrechte auf die Elemente von Basisklassen..................................................................... 91
8.3.3
Die Bedeutung von Elementnamen in einer Klassenhierarchie ................................................. 91
8.3.4
using-Deklarationen in abgeleiteten Klassen ⊕ ........................................................................ 91
8.3.5
Konstruktoren, Destruktoren und implizit erzeugte Funktionen................................................ 91
8.3.6
Vererbung bei Formularen im C++Builder ............................................................................... 93
8.3.7
OO Design: public Vererbung und „ist ein“-Beziehungen ........................................................ 93
8.3.8
OO Design: Komposition und „hat ein“-Beziehungen .............................................................. 93
8.3.9
Konversionen zwischen public abgeleiteten Klassen................................................................. 93
8.3.10 protected und private abgeleitete Klassen ⊕............................................................................. 93
8.3.11 Mehrfachvererbung und virtuelle Basisklassen ......................................................................... 93
8.4 Virtuelle Funktionen, späte Bindung und Polymorphie ....................................................................... 94
8.4.1
Der statische und der dynamische Datentyp.............................................................................. 94
8.4.2
Virtuelle Funktionen ................................................................................................................ 94
8.4.3
Die interne Realisierung von virtuellen Funktionen: vptr und vtbl............................................ 94
8.4.4
OO-Design: Der Einsatzbereich von virtuellen Funktionen ...................................................... 96
8.4.5
Komposition und private Mehrfachvererbung ⊕ ...................................................................... 96
8.4.6
Virtuelle Konstruktoren und Destruktoren................................................................................ 96
8.4.7
Virtuelle Funktionen in Konstruktoren und Destruktoren ⊕ ..................................................... 96
8.4.8
Virtuelle Funktionen und Erweiterbarkeit ................................................................................ 96
8.4.9
Rein virtuelle Funktionen und abstrakte Klassen ...................................................................... 96
8.4.10 OO-Design: Virtuelle Funktionen und abstrakte Basisklassen .................................................. 96
8.4.11 Protokollklassen und Programmgerüste.................................................................................... 96
8.4.12 Muster (Patterns) ..................................................................................................................... 97
8.4.13 UML-Diagramme für Vererbung und Komposition .................................................................. 97
8.4.14 Zeiger auf Klassenelemente ⊕.................................................................................................. 97
8.5 Laufzeit-Typinformationen ................................................................................................................. 98
8.5.1
Typinformationen mit dem Operator typeid ⊕.......................................................................... 98
8.5.2
Typkonversionen mit dynamic_cast ⊕...................................................................................... 98
8.5.3
Anwendungen von Laufzeit-Typinformationen ⊕..................................................................... 98
8.5.4
static_cast mit Klassen ⊕......................................................................................................... 98
8.5.5
Laufzeit-Typinformationen für die Klassen der VCL ⊕ ............................................................ 98
9
Die Bibliothek der visuellen Komponenten (VCL).............................................................100
9.1 Besonderheiten der VCL .................................................................................................................. 100
9.2 Visuelle Programmierung und Properties (Eigenschaften) ................................................................ 100
9.2.1
Lesen und Schreiben von Eigenschaften................................................................................. 100
9.2.2
Array-Properties..................................................................................................................... 100
9.2.3
Indexangaben......................................................................................................................... 100
9.2.4
Speicherangaben .................................................................................................................... 100
9.2.5
Überschriebene Eigenschaften................................................................................................ 100
9.3 Die Klassenhierarchie der VCL ........................................................................................................ 100
9.4 Selbst definierte Komponenten und ihre Ereignisse .......................................................................... 100
9.5 MDI-Programme .............................................................................................................................. 101
9.6 Klassenreferenztypen und virtuelle Konstruktoren ............................................................................ 101
9.7 Botschaften (Messages) .................................................................................................................... 102
9.7.1
Die Message Queue und die Window-Prozedur ...................................................................... 102
9.7.2
Botschaften für eine Anwendung............................................................................................ 102
9.7.3
Die Behandlung von Botschaften in der VCL......................................................................... 102
9.7.4
Selbst definierte Message-Handler für Windows-Steuerelemente ............................................ 102
9.7.5
Botschaften versenden............................................................................................................ 102
9.8 Die Erweiterung der Komponentenpalette ........................................................................................ 103
10
Templates und die STL ......................................................................................................104
10.1 Generische Funktionen: Funktions-Templates .................................................................................. 104
10.1.1 Die Deklaration von Funktions-Templates mit Typ-Parametern ............................................. 104
10.1.2 Spezialisierungen von Funktions-Templates........................................................................... 104
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
Inhalt
xvii
10.1.3 Funktions-Templates mit Nicht-Typ-Parametern ⊕................................................................ 104
10.1.4 Explizit instanziierte Funktions-Templates ⊕......................................................................... 104
10.1.5 Explizit spezialisierte und überladene Templates.................................................................... 104
10.1.6 Rekursive Funktions-Templates ............................................................................................. 104
10.2 Generische Klassen: Klassen-Templates ........................................................................................... 105
10.2.1 Die Deklaration von Klassen-Templates mit Typ-Parametern................................................. 105
10.2.2 Spezialisierungen von Klassen-Templates .............................................................................. 105
10.2.3 Templates mit Nicht-Typ-Parametern ⊕................................................................................. 105
10.2.4 Explizit instanziierte Klassen-Templates ⊕............................................................................ 105
10.2.5 Partielle und vollständige Spezialisierungen ⊕....................................................................... 105
10.2.6 Elemente und friend-Funktionen von Klassen-Templates ⊕ ................................................... 105
10.2.7 Ableitungen von Templates ⊕................................................................................................ 105
10.2.8 Exportierte Templates ............................................................................................................ 105
10.2.9 UML-Diagramme für parametrisierte Klassen ⊕.................................................................... 105
10.3 Funktionsobjekte in der STL............................................................................................................. 107
10.3.1 Der Aufrufoperator () ............................................................................................................. 107
10.3.2 Prädikate und arithmetische Funktionsobjekte........................................................................ 107
10.3.3 Binder, Funktionsadapter und Negatoren ............................................................................... 107
10.4 Iteratoren und die STL-Algorithmen ................................................................................................ 108
10.4.1 Die verschiedenen Arten von Iteratoren.................................................................................. 108
10.4.2 Umkehriteratoren ................................................................................................................... 108
10.4.3 Einfügefunktionen und Einfügeiteratoren............................................................................... 108
10.4.4 Stream-Iteratoren ................................................................................................................... 108
10.4.5 Container-Konstruktoren mit Iteratoren ................................................................................. 108
10.4.6 STL-Algorithmen für alle Elemente eines Containers............................................................. 108
10.5 Die Algorithmen der STL................................................................................................................. 109
10.5.1 Lineares Suchen..................................................................................................................... 109
10.5.2 Zählen.................................................................................................................................... 109
10.5.3 Der Vergleich von Bereichen ................................................................................................. 109
10.5.4 Suche nach Teilfolgen............................................................................................................ 109
10.5.5 Minimum und Maximum ....................................................................................................... 109
10.5.6 Elemente vertauschen............................................................................................................. 111
10.5.7 Kopieren von Bereichen ......................................................................................................... 111
10.5.8 Elemente transformieren und ersetzen.................................................................................... 111
10.5.9 Elementen in einem Bereich Werte zuweisen ......................................................................... 111
10.5.10 Elemente entfernen............................................................................................................... 111
10.5.11 Die Reihenfolge von Elementen vertauschen......................................................................... 111
10.5.12 Permutationen ...................................................................................................................... 111
10.5.13 Partitionen............................................................................................................................ 111
10.5.14 Bereiche sortieren ................................................................................................................. 111
10.5.15 Binäres Suchen in sortierten Bereichen................................................................................. 111
10.5.16 Mischen von sortierten Bereichen ......................................................................................... 111
10.5.17 Mengenoperationen auf sortierten Bereichen ........................................................................ 111
10.5.18 Heap-Operationen................................................................................................................. 111
10.5.19 Verallgemeinerte numerische Operationen ........................................................................... 111
11
Verschiedenes .....................................................................................................................113
11.1 3D-Grafik mit OpenGL .................................................................................................................... 113
11.1.1 Initialisierungen..................................................................................................................... 113
11.1.2 Grafische Grundelemente: Primitive ...................................................................................... 113
11.1.3 Modelltransformationen ......................................................................................................... 113
11.1.4 Vordefinierte Körper .............................................................................................................. 113
11.1.5 Lokale Transformationen ....................................................................................................... 113
11.1.6 Beleuchtungseffekte ............................................................................................................... 113
11.1.7 Texturen ................................................................................................................................ 113
11.2 Win32-Funktionen zur Dateibearbeitung .......................................................................................... 114
11.2.1 Elementare Funktionen .......................................................................................................... 114
11.2.2 Der gleichzeitige Zugriff von mehreren Anwendern auf eine Datei ........................................ 114
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
xviii
Inhalt
11.2.3 Record-Locking...................................................................................................................... 114
11.2.4 VCL-Funktionen zur Dateibearbeitung und die Klasse TFileStream ....................................... 114
11.3 Datenübertragung über die serielle Schnittstelle ............................................................................... 114
11.3.1 Grundbegriffe......................................................................................................................... 114
11.3.2 Standards für die serielle Schnittstelle: RS-232C bzw. V.24 ................................................... 114
11.3.3 Win32-Funktionen zur seriellen Kommunikation................................................................... 114
11.4 Datenbank-Komponenten der VCL................................................................................................... 115
11.4.1 Tabellen und die Komponente TTable .................................................................................... 115
11.4.2 Die Anzeige von Tabellen mit der Komponente DBGrid ........................................................ 115
11.4.3 Indizierte Tabellen ................................................................................................................. 115
11.4.4 Datenbanken mit mehreren Tabellen...................................................................................... 115
11.4.5 SQL-Abfragen........................................................................................................................ 115
11.4.6 Transaktionen und Cached Updates ....................................................................................... 115
11.4.7 Die BDE am Beispiel von ODBC und MS Access Datenbanken ............................................. 115
11.4.8 Visuell gestaltete Datenbank-Ausdrucke mit QuickReport...................................................... 115
12
Lösungen ............................................................................................................................117
12.1 Lösungen Kapitel 2 .......................................................................................................................... 117
12.1.1 Aufgabe 2.2............................................................................................................................ 117
12.1.2 Aufgabe 2.3............................................................................................................................ 117
12.1.3 Aufgabe 2.4............................................................................................................................ 118
12.1.4 Aufgabe 2.5............................................................................................................................ 119
12.1.5 Aufgabe 2.6............................................................................................................................ 120
12.1.6 Aufgabe 2.7............................................................................................................................ 121
12.1.7 Aufgabe 2.9............................................................................................................................ 121
12.1.8 Aufgabe 2.11.......................................................................................................................... 122
12.1.9 Aufgabe 2.12.......................................................................................................................... 123
12.1.10 Aufgabe 2.13 ........................................................................................................................ 125
12.1.11 Aufgabe 2.14 ........................................................................................................................ 126
12.1.12 Aufgabe 2.15 ........................................................................................................................ 131
12.2 Lösungen Kapitel 3 .......................................................................................................................... 132
12.2.1 Aufgabe 3.3............................................................................................................................ 132
12.2.2 Aufgaben 3.4.......................................................................................................................... 132
12.2.3 Aufgaben 3.5.......................................................................................................................... 134
12.2.4 Aufgabe 3.6............................................................................................................................ 138
12.2.5 Aufgaben 3.7.......................................................................................................................... 139
12.2.6 Aufgaben 3.9.......................................................................................................................... 143
12.2.7 Aufgabe 3.11.......................................................................................................................... 146
12.2.8 Aufgabe 3.12.......................................................................................................................... 146
12.2.9 Aufgabe 3.14.......................................................................................................................... 148
12.3 Lösungen Kapitel 4 .......................................................................................................................... 148
12.3.1 Aufgaben 4.1.......................................................................................................................... 148
12.3.2 Aufgaben 4.2.1....................................................................................................................... 154
12.3.3 Aufgaben 4.2.2....................................................................................................................... 156
12.3.4 Aufgaben 4.2.3....................................................................................................................... 160
12.3.5 Aufgaben 4.2.4....................................................................................................................... 163
12.3.6 Aufgaben 4.2.5....................................................................................................................... 163
12.3.7 Aufgaben 4.2.6....................................................................................................................... 165
12.3.8 Aufgaben 4.3.......................................................................................................................... 167
12.3.9 Aufgabe 4.4.1 a)..................................................................................................................... 171
12.3.10 Aufgaben 4.4.1 ..................................................................................................................... 174
12.3.11 Aufgabe 4.4.2 ....................................................................................................................... 177
12.3.12 Aufgabe 4.4.7 ....................................................................................................................... 179
12.3.13 Aufgabe 4.5.1 ....................................................................................................................... 180
12.3.14 Aufgabe 4.5.3 ....................................................................................................................... 186
12.3.15 Aufgaben 4.5.6 ..................................................................................................................... 188
12.3.16 Aufgabe 4.5.7 ....................................................................................................................... 189
12.3.17 Aufgaben 4.6.3 ..................................................................................................................... 190
12.3.18 Aufgaben 4.6.3, 4.6.4.2, 4.6.8 und 4.6.9 ............................................................................... 193
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
Inhalt
xix
12.3.19 Aufgabe 4.6.4.1 .................................................................................................................... 204
12.3.20 Aufgabe 4.6.6 ....................................................................................................................... 206
12.3.21 Aufgabe 4.6.7 ....................................................................................................................... 208
12.3.22 Aufgabe 4.7 .......................................................................................................................... 209
12.3.23 Aufgabe 4.8 .......................................................................................................................... 215
12.4 Lösungen Kapitel 5 .......................................................................................................................... 218
12.4.1 Aufgaben 5.2.19..................................................................................................................... 218
12.4.2 Aufgaben 5.3.4....................................................................................................................... 223
12.4.3 Aufgabe 5.5............................................................................................................................ 224
12.4.4 Aufgabe 5.6............................................................................................................................ 225
12.4.5 Aufgabe 5.7............................................................................................................................ 226
12.4.6 Aufgaben 5.7.3....................................................................................................................... 231
12.4.7 Aufgabe 5.8............................................................................................................................ 232
12.4.8 Aufgaben 5.8.4....................................................................................................................... 236
12.4.9 Aufgabe 5.8.4.3...................................................................................................................... 237
12.4.10
Aufgaben 5.8.5................................................................................................................... 241
12.4.11
Aufgaben 5.10.8................................................................................................................. 244
12.5 Lösungen Kapitel 6 .......................................................................................................................... 253
12.5.1 Aufgabe 6.3.5 und 6.3.7 ......................................................................................................... 253
12.5.2 Aufgabe 6.3.8.6...................................................................................................................... 260
12.5.3 Aufgabe 6.5............................................................................................................................ 268
12.5.4 Aufgaben 6.6.4 und 6.6.6 ....................................................................................................... 269
12.5.5 Aufgabe 6.6.4.3...................................................................................................................... 275
12.5.6 Aufgabe 6.6.4.4...................................................................................................................... 279
12.5.7 Aufgabe 6.6.4.5...................................................................................................................... 280
12.5.8 Aufgabe 6.6.6.3...................................................................................................................... 282
12.5.9 Aufgabe 6.6.6.4...................................................................................................................... 284
12.5.10
Aufgabe 6.8........................................................................................................................ 286
12.5.11
Aufgaben 6.9...................................................................................................................... 289
12.6 Lösungen Kapitel 7 .......................................................................................................................... 292
12.6.1 Aufgaben 7.1.......................................................................................................................... 292
12.6.2 Aufgabe 7.1.2......................................................................................................................... 294
12.6.3 Aufgabe 7.1.3......................................................................................................................... 296
12.6.4 Aufgabe 7.2............................................................................................................................ 299
12.6.5 Aufgabe 7.2............................................................................................................................ 300
12.6.6 Aufgabe 7.3............................................................................................................................ 300
12.7 Lösungen Kapitel 8 .......................................................................................................................... 302
12.7.1 Aufgabe 8.1.5 und 8.1.7 ......................................................................................................... 303
12.7.2 Aufgabe 8.2.2......................................................................................................................... 317
12.7.3 Aufgabe 8.2.4......................................................................................................................... 319
12.7.4 Aufgabe 8.2.6......................................................................................................................... 323
12.7.5 Aufgabe 8.2.13....................................................................................................................... 332
12.7.6 Aufgabe 8.2.13....................................................................................................................... 332
12.7.7 Aufgabe 8.2.13....................................................................................................................... 333
12.7.8 Aufgaben 8.3.......................................................................................................................... 335
12.7.9 Aufgaben 8.4.......................................................................................................................... 348
12.7.10
Aufgabe 8.4.3.3.................................................................................................................. 362
12.7.11
Aufgabe 8.5........................................................................................................................ 363
12.8 Lösungen Kapitel 9 .......................................................................................................................... 364
12.8.1 Aufgabe 9.2............................................................................................................................ 364
12.8.2 Aufgabe 9.3............................................................................................................................ 365
12.8.3 Aufgabe 9.4............................................................................................................................ 367
12.8.4 Aufgabe 9.5............................................................................................................................ 369
12.8.5 Aufgabe 9.5............................................................................................................................ 369
12.8.6 Aufgabe 9.5............................................................................................................................ 370
12.8.7 Aufgabe 9.6............................................................................................................................ 370
12.8.8 Aufgabe 9.7............................................................................................................................ 371
12.9 Lösungen Kapitel 10......................................................................................................................... 373
12.9.1 Aufgabe 10.1.......................................................................................................................... 373
12.9.2 Aufgabe 10.2.......................................................................................................................... 377
⊕ weniger wichtig - siehe den Hinweis auf Seite xx
xx
Inhalt
12.9.3 Aufgabe 10.3.......................................................................................................................... 381
12.9.4 Aufgabe 10.4.......................................................................................................................... 384
12.9.5 Aufgabe 10.5.......................................................................................................................... 387
12.9.6 Aufgabe 10.5.......................................................................................................................... 397
12.10Lösungen Kapitel 11......................................................................................................................... 397
12.10.1
Aufgabe 11.1...................................................................................................................... 397
12.10.2
Aufgabe 11.1...................................................................................................................... 398
12.10.3
Aufgabe 11.1...................................................................................................................... 400
12.10.4
Aufgabe 11.1...................................................................................................................... 412
12.10.5
Aufgabe 11.1...................................................................................................................... 413
12.10.6
Aufgabe 11.1...................................................................................................................... 415
12.10.7
Aufgabe 11.2...................................................................................................................... 417
12.10.8
Aufgabe 11.3...................................................................................................................... 420
12.10.9
Aufgabe 11.4...................................................................................................................... 424
12.10.10 Aufgabe 11.4...................................................................................................................... 430
⊕ Angesichts des Umfangs dieses Buches habe ich einige Abschnitte mit dem Zeichen ⊕ in der Überschrift als
„weniger wichtig“ gekennzeichnet. Damit wollte ich dem Anfänger nur eine kleine Orientierung durch die Fülle des
Stoffes geben. Eine solche Kennzeichnung soll aber überhaupt nicht bedeuten, dass dieser Teil unwichtig ist. Es
kann sogar gut sein, dass gerade Sie diese Inhalte laufend benötigen.
1 Die Entwicklungsumgebung
1.1 Visuelle Programmierung: Ein erstes kleines Programm
1.2 Erste Schritte in C++
1.3 Der Quelltexteditor
1.4 Projekte, Projektdateien und Projektoptionen
1.5 Einige Tipps zur Arbeit mit Projekten
1.6 Die Online-Hilfe
1.7 Das lokale Menü
1.8 Die Symbolleiste
1.9 Programmierhilfen
1.10 Packages und eigenständig ausführbare Programme
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
2 Die Komponentenpalette
2.1 Die Online-Hilfe zu den Komponenten
2.2 Namen
Aufgaben 2.2
1. Schreiben Sie ein Programm, das ein Fenster mit folgenden Elementen anzeigt:
Verwenden Sie dazu die Komponenten Label, Edit und Button von der Seite Standard der Komponentenpalette.
2. Ersetzen Sie alle vom C++Builder vergebenen Namen durch aussagekräftige Namen. Da in diesem Beispiel sowohl
ein Label als auch ein Edit-Fenster für den Vornamen, Nachnamen usw. verwendet wird, kann es sinnvoll sein, den
Typ der Komponente im Namen zu berücksichtigen, z.B. LVorname und LNachname für die Label und EVorname
und ENachname für die Edit-Fenster.
3. Als Reaktion auf ein Anklicken des Buttons Eingabe löschen soll jedes Eingabefeld mit der Methode Clear gelöscht
werden. Für den Button Daten speichern soll keine weitere Reaktion vorgesehen werden. Beim Anklicken des
Buttons Programm beenden soll das Formular durch den Aufruf der Methode Close geschlossen werden.
Die entsprechenden Funktionen finden Sie am einfachsten durch einen Doppelklick auf den jeweiligen Button im
Formular.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
2.3 Labels und Datentypen
3
2.3 Labels und Datentypen
Aufgabe 2.3
Schreiben Sie ein Programm, das nach dem Start folgendes Fenster anzeigt:
Damit die Größe des Labels nicht dem Text angepasst wird, soll Autosize im Objektinspektor auf false gesetzt werden.
Damit das ganze Label sichtbar ist, soll seine Farbe z.B. auf Gelb gesetzt werden.
Durch Anklicken der Buttons
– für ausrichten soll der Text im Label mit Hilfe der Eigenschaft TAlignment (siehe Online-Hilfe) links bzw. rechts
ausgerichtet werden,
– für sichtbar/unsichtbar soll das Label sichtbar bzw. unsichtbar gemacht werden,
– für links/rechts soll das Label so verschoben werden, dass sein linker bzw. rechter Rand auf dem linken bzw. rechten
Rand des Formulars liegt. Informieren Sie sich dazu in der Online-Hilfe über die Eigenschaften Width und
ClientWidth eines Formulars.
2.4 Funktionen, Methoden und die Komponente TEdit
Aufgaben 2.4
1. Schreiben Sie einen einfachen Taschenrechner, mit dem man zwei Ganzzahlen addieren kann. Nach dem Anklicken
der Taste Löschen sollen sämtliche Eingabefelder gelöscht werden.
Offensichtlich produziert dieser Taschenrechner für ganze Zahlen falsche Ergebnisse, wenn die Summe außerhalb
des Bereichs –2147483648 .. 2147483647 (–231..231 – 1) liegt:
Die Ursache für diese Fehler werden wir später kennen lernen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4
2 Die Komponentenpalette
2. Ergänzen Sie den Taschenrechner um einen Button, mit dem auch Gleitkommazahlen addiert werden können.
Verwenden Sie dazu die Funktionen
extern Extended __fastcall StrToFloat (const System::AnsiString S);
extern System::AnsiString __fastcall FloatToStr (Extended Value);
// weitere Informationen dazu in der Online-Hilfe
Dabei ist der Datentyp Extended einer der Datentypen, die der C++Builder für Gleitkommazahlen zur Verfügung
stellt. Damit können Dezimalbrüche (Zahlen mit Nachkommastellen wie z.B. 3,1415) dargestellt werden. Gleitkommadatentypen haben einen wesentlich größeren Darstellungsbereich als der Ganzzahldatentyp int. Deshalb
treten bei dieser Variante des Taschenrechners nicht so schnell Bereichsüberschreitungen auf.
Speichern Sie das Projekt ab, z.B. die Unit unter dem Namen tr1 und die Projektdatei unter dem Namen tr1p. Die
Lösung der nächsten Aufgabe kann dann unter tr2 und tr2p gespeichert werden.
3. Ändern Sie den Taschenrechner so ab, dass die Grundrechenarten +, –, * und / über verschiedene Buttons aktiviert
werden können. Die jeweils gewählte Rechenart soll in einem Label zwischen den ersten beiden Eingabefeldern angezeigt werden:
2.5 Memos, ListBoxen, ComboBoxen und die Klasse TStrings
Aufgabe 2.5
Schreiben Sie ein Programm, dessen Formular ein Memo, eine ListBox, eine ComboBox, ein Edit-Fenster, zwei Buttons
und zwei Labels enthält:
a) Beim Anklicken von Button1 soll der aktuelle Text im Edit-Fenster in jede der drei TStrings-Listen eingetragen
werden.
b) Wenn ein Eintrag in der ListBox angeklickt wird, soll er auf Label1 angezeigt werden.
c) Beim Anklicken von Button2 soll der in der ComboBox ausgewählte Text auf dem Label2 angezeigt werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
2.6 Buttons und Ereignisse
5
2.6 Buttons und Ereignisse
Aufgabe 2.6
Schreiben Sie ein Programm, das in jeweils einem Edit-Fenster durch einen Text anzeigt, ob eines der Ereignisse (außer
den Drag-Ereignissen) für einen Button (mit der Aufschrift Test) eingetreten ist:
In den Feldern für die Key-Ereignisse soll die jeweils gedrückte Taste angezeigt werden. Beachten Sie dabei, dass der
Parameter Key bei der Funktion
void __fastcall TForm1::Button1KeyPress(TObject *Sender, char &Key)
den Datentyp char hat (der der Eigenschaft Text des Edit-Fensters direkt zugewiesen werden kann), während Key bei
den Funktionen KeyDown und KeyUp
void __fastcall TForm1::Button1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
den Datentyp WORD hat. Dieser Datentyp kann mit IntToStr in einen String umgewandelt und dann der Eigenschaft
Text des Edit-Fensters zugewiesen werden.
Im Edit-Fenster zu MouseMove sollen die Mauskoordinaten angezeigt werden, die im Ereignis OnMouseMove unter
den Werten X und Y zur Verfügung stehen.
Die Aufschrift der Buttons soll beim Ereignis FormCreate gesetzt werden. Mit dem Button Clear sollen alle Anzeigen
gelöscht werden können.
2.7 CheckBoxen, RadioButtons und einfache if-Abfragen
Aufgabe 2.7
Schreiben Sie ein Programm, das ein Fenster mit drei CheckBoxen, zwei RadioButtons, einem Label und zwei weiteren
Buttons anzeigt:
Beim Anklicken des Buttons Test sollen in Abhängigkeit von den Markierungen der CheckBoxen und der RadioButtons
folgende Aktionen stattfinden:
a) Die Markierung der CheckBox enable/disable soll entscheiden, ob die Eigenschaft Enabled von Button1 auf true
oder false gesetzt wird.
b) Die Markierung der CheckBox Aufschrift soll entscheiden, welchen von zwei beliebigen Texten Button1 als
Aufschrift erhält.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6
2 Die Komponentenpalette
c) Die Markierung der CheckBox show/hide soll entscheiden, ob Button1 angezeigt wird oder nicht.
d) Falls der RadioButton extra Info markiert ist, soll in Label1 ein zusätzlicher Text (beliebig) angezeigt werden.
2.8 Die Container GroupBox, Panel und RadioGroup
Aufgabe 2.8
Schreiben Sie ein Programm, das etwa das folgende Fenster anzeigt:
Dabei sollen die Buttons OK, Hilfe und Abbruch zu einer Gruppe zusammengefasst werden, die optisch nicht erkennbar
ist. Bei jedem Anklicken eines Buttons oder einer CheckBox soll ein Text in die ComboBox eingetragen werden (z.B.
„CheckBox 1 angeklickt“).
2.9 ScrollBar
Aufgabe 2.9
In der frühen Steinzeit der Rechenmaschinen (bis ca. 1970) gab es nicht nur Digitalrechner (wie heute nahezu
ausschließlich), sondern auch Analogrechner. Die Bezeichnung analog kommt daher, dass mathematische Zusammenhänge durch physikalische Geräte dargestellt wurden, bei denen aus Eingabewerten in Analogie zu den mathematischen
Zusammenhängen Ausgabewerte erzeugt werden. Beispiele sind der Rechenschieber oder spezielle elektrische Geräte,
bei denen man die Operanden an Drehreglern eingeben und das Ergebnis an Zeigerinstrumenten ablesen konnte.
Analogrechner wurden oft für spezielle Aufgaben entwickelt, z.B. um mit den Kirchhoffschen Regeln Gleichungssysteme zu lösen. Sie arbeiteten oft wesentlich schneller als die damaligen Digitalrechner.
Schreiben Sie ein Programm zur Lösung des symmetrischen linearen Gleichungssystems
ax + by = 1
bx + dy = 1
bei dem man wie bei einem Analogrechner die Koeffizienten a, b und d an Schiebereglern einstellen kann:
Diese Koeffizienten sowie die Ergebnisse sollen digital in einem Edit-Feld dargestellt werden, wenn einer der
Schieberegler bewegt wird (Ereignis: OnChange). Die Lösung des obigen Gleichungssystems ist gegeben durch
y = (b – a)/(b*b – a*d)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
2.10 Hauptmenüs und Popup-Menüs
7
x = (b – d)/(b*b – a*d)
Stellen Sie mit einer if-Anweisung sicher, dass keine Division durch 0 stattfindet. In diesem Fall braucht kein neuer
Wert für x bzw. y angezeigt werden, und im Feld für die Lösung soll „Division durch 0“ stehen.
2.10 Hauptmenüs und Popup-Menüs
Aufgabe 2.10
Bei dieser Aufgabe soll nur der Umgang mit dem Menüdesigner und der Entwurf von Menüs geübt werden, ohne dass
über die Menüs Funktionen aufgerufen werden können. Im nächsten Abschnitt werden dann einige Standarddialoge
vorgestellt, die in den Menüs dieses Programms aufgerufen werden können.
Entwerfen Sie ein Programm Menu1, in dessen Menüleiste die Optionen Datei, Bearbeiten und Drucken angeboten
werden. Diese sollen die folgenden Unterpunkte enthalten:
– Unter der Option Datei: Datei öffnen und Datei speichern
– Unter der Option Bearbeiten: Suchen, Suchen und Ersetzen und nach einem Trennstrich Alles markieren, Kopieren
und Ausschneiden.
– Unter der Option Drucken: Drucken und Drucker einrichten.
Durch Drücken der rechten Maustaste auf einem freien Bereich des Fensters soll ein Popup-Menü aufgerufen werden
können, das die Optionen Farben und Schriftart anbietet.
2.11 Die Komponenten der Seite Dialoge
Aufgabe 2.11
Ergänzen Sie das Programm Menu1 aus der Aufgabe des letzten Abschnitts um ein Memo-Fenster, das den gesamten
Client-Bereich ausfüllt (Eigenschaft Align= alClient). Als Reaktion auf die Auswahl der angebotenen Menüoption
sollen die folgenden Aktionen stattfinden:
– Datei|Datei öffnen: Falls ein Dateiname ausgewählt wird, soll diese Datei in das Memo-Fenster eingelesen werden.
Dazu kann die Methode LoadFromFile von Memo1->Lines verwendet werden. In diesem OpenDialog sollen nur die
Dateien mit der Endung „.txt“ angezeigt werden.
– Datei|Datei speichern: Falls ein Dateiname ausgewählt wird, soll der Text aus dem Memo mit der Methode
SaveToFile von Memo1->Lines unter diesem Namen gespeichert werden. Auch hier kann man einen Filter setzen.
Diese Option soll außerdem mit dem ShortCut „Strg+S“ verfügbar sein. Dazu muss man nur diese Eigenschaft im
Objektinspektor der entsprechenden Menüoption setzen.
–
–
–
–
–
–
Bearbeiten|Suchen: Ein FindDialog ohne jede weitere Aktion.
Bearbeiten|Suchen und Ersetzen: Ein ReplaceDialog ohne jede weitere Aktion.
Nach einem Trennstrich in den Menüoptionen:
Bearbeiten|Alles Markieren: Ein Aufruf von Memo1->SelectAll.
Bearbeiten|Ausschneiden: Ein Aufruf von Memo1->CutToClipboard.
Bearbeiten|Kopieren: Ein Aufruf von Memo1->CopyToClipboard.
– Drucker|Drucken. Falls hier Drucken ausgewählt wird, soll das Formular durch den Aufruf seiner Methode Print
gedruckt werden. Mit den bisher vorgestellten Sprachelementen ist es noch nicht möglich, den Inhalt des Memos
auszudrucken.
– Drucker|Drucker einrichten: Ein PrinterSetupDialog ohne weitere Aktion.
– Popup-Menü|Farben: Die ausgewählte Farbe (Eigenschaft Color des ColorDialogs) soll der Eigenschaft Brush>Color des Memos zugewiesen werden.
– Popup-Menü|Schriftart: Die ausgewählte Schriftart (Eigenschaft Font des FontDialogs) soll der Eigenschaft Font
des Memos zugewiesen werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8
2 Die Komponentenpalette
2.12 Der Aufruf von eigenen Formularen und modale Fenster ⊕
Aufgabe 2.12
Schreiben Sie ein Programm, das ein Fenster mit einem Button anzeigt. Beim Anklicken dieses Buttons soll ein zweites
Formular modal angezeigt werden, das drei Buttons mit der Aufschrift „Dialog mit Hilfe“, „Passwort-Dialog“ und
„Standard-Dialog“ enthält. Beim Anklicken dieser Buttons sollen die entsprechenden Dialoge aus Datei|neu|Dialoge
modal angezeigt werden. Diese Dialoge sollen als Kopie übernommen werden.
2.13 Die Komponenten der Seite „Zusätzlich“ ⊕
Aufgabe 2.13
Schreiben Sie ein kleines Zeichenprogramm, das im Wesentlichen aus einem Formular mit einem Image besteht. Die
Menüleiste soll die folgenden Optionen anbieten:
– Datei|Öffnen: lädt ein Bild in das Image
– Farbe|Pen: setzt im Canvas von Image die Farbe des Zeichenstiftes
– Farbe|Brush: setzt im Canvas von Image die Füllfarbe von Flächen.
Über drei SpeedButtons (die im Folgenden als Linie, Quadrat und Kreis bezeichnet werden) auf einem Panel soll die
Art der gezeichneten Elemente festgelegt werden. Diese Buttons können mit fertigen Bitmaps, z.B. aus dem Verzeichnis „Borland Shared\Images“, verziert werden oder mit eigenen, die man mit dem Bildeditor (unter Tools in der
Menüleiste des C++Builders) oder einem anderen Zeichenprogramm entworfen hat. Eine Textaufschrift reicht aber
auch aus.
– Falls der SpeedButton Linie gedrückt ist, wird eine Linie von der Position gezeichnet, an der die linke Maustaste das
letzte Mal gedrückt wurde, bis zu der Position, an der sie wieder losgelassen wird.
– Falls die SpeedButtons Quadrat oder Kreis gedrückt sind, soll ein Quadrat oder Kreis mit dem Durchmesser von 10
Pixeln ab der aktuellen Mausposition gezeichnet werden.
Ein SpeedButton soll so lange gedrückt dargestellt werden, bis ein anderer SpeedButton gedrückt wird.
Dieses Programm ist natürlich sehr primitiv. Es vermittelt aber einen Eindruck davon, wie Zeichenprogramme
aufgebaut sind.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
2.14 Einige Komponenten der Seiten Win32 und Win 3.1
9
2.14 Einige Komponenten der Seiten Win32 und Win 3.1
2.14.1 Mehrseitige Dialoge ⊕
2.14.2 ImageList und ListView ⊕
2.14.3 Komponenten zur Anzeige hierarchischer Datenstrukturen ⊕
2.14.4 Komponenten zur Größenanpassung anderer Komponenten ⊕
2.14.5 Formatierte Texte
2.14.6 Statusanzeigen ⊕
2.14.7 Weitere Komponenten ⊕
Aufgaben 2.14
1. Schreiben Sie ein Programm mit einem dreiseitigen Dialogfenster.
a) Das erste Dialogfenster soll einen Button, ein Label und eine Statusleiste enthalten. Je nachdem, ob sich der
Mauszeiger über dem Button oder über dem Label befindet, soll in der Statusleiste ein entsprechender Text
angezeigt werden. Sie können dazu das Ereignis OnMouseMove verwenden.
b) Das zweite Dialogfenster soll die Komponenten TrackBar und ProgressBar enthalten. Beim Verschieben des
Reglers von TrackBar soll der Positionswert durch ProgressBar angezeigt werden.
c) Das dritte Dialogfenster soll ein ListView enthalten, dem beim Anklicken eines Buttons mit der Aufschrift Add der
Text eines Edit-Fensters als neuer Eintrag hinzugefügt wird. Außerdem sollen die Einträge von zwei weiteren EditFenstern als neuer Untereintrag hinzugefügt werden (siehe die nächste Abbildung). Die Ansicht des ListViews soll
über 4 RadioButtons zwischen vsIcon, vsList, vsSmallIcon und vsReport umgeschaltet werden können:
2. Unter Datei|neu|Projekte findet man die Projektvorlage „Win95/98-Logo-Anwendung“. Sie enthält ein RichEdit als
Editor und Menüoptionen zum Öffnen und Speichern von Dateien. Ergänzen Sie eine solche Anwendung um einen
FontDialog, mit dem die Schriftart gewählt werden kann.
Damit in diesem FontDialog die Eigenschaften des aktuellen Fonts angezeigt werden, kann man die Eigenschaft
SelAttributes mit der Methode Assign als Ganzes dem FontDialog zuweisen. Ebenso kann man mit Assign den Font
des FontDialogs dem RichEdit zuweisen.
FontDialog1->Font->Assign(RichEdit1->SelAttributes);
if (FontDialog1->Execute())
RichEdit1->SelAttributes->Assign(FontDialog1->Font);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
10
2 Die Komponentenpalette
2.15 Einige Komponenten der Seite System ⊕
Aufgaben 2.15
1. Schreiben Sie ein Programm mit den folgenden beiden Funktionen:
a) Die Hintergrundfarbe eines Edit-Fensters soll gelb blinken, wenn es den Fokus hat. Sie können dazu die Ereignisse
OnEnter und OnExit verwenden.
b) Prüfen Sie Genauigkeit von Timer-Ereignissen, indem Sie jede Sekunde einen Timer-Tick auslösen und dabei den
Wert
FloatToStr(double(Now())*24*60*60)
in einem Memo-Fenster ausgeben. Hier ist Now() die aktuelle Systemzeit als Gleitkommazahl. Die Stellen vor dem
Komma geben die Anzahl der Tage seit dem 30.12.1899 und die Stellen nach dem Komma die aktuelle Uhrzeit an.
Vergleichen Sie die angezeigten Werte mit den erwarteten Werten.
2. Schreiben Sie ein Programm, mit dem man WAV-, MIDI-, AVI- und ähnliche Dateien abspielen kann, die mit
einem OpenDialog geöffnet wurden:
2.16 ActiveX-Komponenten ⊕
2.17 Vordefinierte DialogBoxen ⊕
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
3 Elementare Datentypen und Anweisungen
3.1 Windows-Programme und Units
3.2 Syntaxregeln
3.3 Variablen und Bezeichner
Aufgabe 3.3
Begründen Sie für jede der folgenden Definitionen, ob sie zulässig ist oder nicht:
int Preis_in_$, x kleiner y, Zinssatz_in_%,
x/y, this, Einwohner_von_Tübingen;
3.4 Ganzzahldatentypen
3.4.1 Die interne Darstellung von Ganzzahlwerten
3.4.2 Der Datentyp von Ganzzahlliteralen
Aufgaben 3.4.2
1. Stellen Sie mit 8 Bits
a) die Zahl 37 im Binärsystem dar
b) die Zahl -37 im Zweierkomplement dar
c) die Zahlen 37 und -37 im Hexadezimalsystem dar.
2. Welchen Wert stellt der Hexadezimalwert „ab“
a) im Zweierkomplement dezimal dar
b) im Binärsystem dezimal dar.
3. Welche Werte werden durch die folgenden Anweisungen ausgegeben:
Memo1->Lines->Add(IntToStr(030)); // Vorwahl Berlin
Memo1->Lines->Add(IntToStr(017+15));
Memo1->Lines->Add(IntToStr(0x12+10));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12
3 Elementare Datentypen und Anweisungen
3.4.3 Zuweisungen und Standardkonversionen bei Ganzzahlausdrücken
3.4.4 Operatoren und die „üblichen arithmetischen Konversionen“
Aufgaben 3.4.4
1. Welchen Wert haben die Variablen
unsigned char b = 255;
unsigned char c = 0;
short i = 20000;
nach den Zuweisungen
b = b
c = c
short
short
short
short
+
–
j
k
l
m
1;
1;
= i/8;
= i/–8;
= i%8;
= i%–8;
2. Im C++Builder werden die Koordinaten einer visuellen Komponente immer absolut in Pixeln angegeben. Das hat
zur Folge, dass eine Komponente (z.B. ein Button), die beim Entwurf des Formulars in der Mitte platziert ist, nach
einer Vergrößerung des Fensters zur Laufzeit nicht mehr zentriert ist.
Bei jeder Änderung der Größe eines Fensters tritt das Ereignis OnResize ein. In der zugehörigen
Ereignisbehandlungsroutine OnResize kann die Position einer Komponente an die neue Größe des Formulars
angepasst werden:
void __fastcall TForm1::FormResize(TObject *Sender)
{
Button1->Left = (Form1->Width – Button1->Width)/2;
}
Überarbeiten Sie die Funktion FormResize so, dass zwei Komponenten Button1 und Button2 nach einer Änderung
der Größe des Formulars horizontal symmetrisch auf das Formular verteilt werden. Dabei kann der Fall unberücksichtigt bleiben, dass das Fenster für alle drei Komponenten zu klein wird.
3. Beschreiben Sie für beliebige positive Werte von i und j (beide Ganzzahldatentypen) das Ergebnis von
(i/j)*(j/i)
3.4.5 Der Datentyp bool
3.4.6 Die char-Datentypen und der ASCII- und ANSI-Zeichensatz
Aufgaben 3.4.6
1. Für die Variable c (Datentyp char) soll eine boolesche Variable
a) Grossbuchstabe genau dann den Wert true erhalten, wenn c ein Großbuchstabe ist.
b) Buchstabe genau dann den Wert true erhalten, wenn c ein Buchstabe ist.
c) alphanumerisch genau dann den Wert true erhalten, wenn c ein Buchstabe oder eine Ziffer ist.
2. Schaltjahre
Die Umlaufzeit der Erde um die Sonne bezeichnet man als ein Jahr. Ein Tag ist der Zeitraum, in dem sich die Erde
einmal um ihre Achse dreht (Abstand zwischen zwei Mittagen).
Misst man ein Jahr als den Zeitraum zwischen den so genannten Frühlingspunkten, an denen ein Tag und eine
Nacht genau gleich lang sind, war am 1.1.1900 ein Jahr 365 Tage, 48 Minuten und 46,0 Sekunden oder 365,24220
Tage lang. Dass ein Jahr in einem Jahrtausend 5,6 Sekunden kürzer wird, soll im Folgenden nicht berücksichtigt
werden.
Der von Julius Cäsar 46 v. Chr. festgelegte Julianische Kalender ging von durchschnittlich 365,25 Tagen pro Jahr
aus. Dieser Fehler ist bis in das 16. Jahrhundert auf ca. 10 Tage angewachsen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
3.5 Kontrollstrukturen und Funktionen
13
Papst Gregor XIII. hat im Jahr 1582 den auch heute noch gültigen Gregorianischen Kalender eingeführt, nach
dem ein Jahr durchschnittlich 365,2425 Tage lang ist. Dabei wurden die Nachkommastellen folgendermaßen durch
Schaltjahre berücksichtigt: Jede durch 4 teilbare Jahreszahl ist ein Schaltjahr, außer den durch 100 teilbaren, wenn
diese nicht durch 400 teilbar sind. Bei diesem Verfahren summiert sich der Fehler in 3300 Jahren auf einen Tag auf.
Weisen Sie einer booleschen Variablen Schaltjahr den Wert eines booleschen Ausdrucks zu, so dass Schaltjahr den
Wert true erhält, wenn die Variable Jahr (Datentyp int) nach dem Gregorianischen Kalender ein Schaltjahr ist, und
andernfalls den Wert false.
3. Die Ganzzahlvariablen t1, m1 und j1 sowie t2, m2 und j2 sollen zwei Kalenderdaten bezeichnen (z.B. t1 = 17, m1 =
12, j1 = 1995). Eine boolesche Variable vorher soll den Wert true erhalten, wenn das Datum (t1, m1, j1) zeitlich vor
dem Datum (t2, m2, j2) liegt, und andernfalls den Wert false.
4. Ein Formular soll ein MainMenu mit verschiedenen Unterpunkten (MenuItems) und eine GroupBox mit einigen
Komponenten enthalten. Durch das Anklicken eines Buttons sollen die Eigenschaften
a) Enabled (für einen Unterpunkt des Menüs)
b) Visible (der GroupBox)
von true auf false und von false auf true umgeschaltet werden.
3.4.7 Der Datentyp __int64
3.5 Kontrollstrukturen und Funktionen
3.5.1 Die if- und die Verbundanweisung
3.5.2 Wiederholungsanweisungen
3.5.3 Funktionen und der Datentyp void
Aufgaben 3.5
1. Welche Werte werden durch die for-Schleife ausgegeben
int n=StrToInt(Edit1->Text);
for (unsigned int u=0; u<=n-1; u++)
Memo1->Lines->Add(IntToStr(u));
a) mit n=0
b) mit n=1
c) Welche Ausgabe erhält man mit n=0, wenn man in der for-Schleife die Bedingung u<=n-1 durch u<n ersetzt?
2. Schreiben Sie
a) eine Funktion Quersumme, die für einen als Parameter übergebenen Ganzzahlwert die Quersumme als
Funktionswert zurückgibt (z.B. Quersumme(123)=6, Quersumme(100)=1)
b) eine Funktion ZeigeQuersumme, die für alle Werte, die zwischen zwei als Parametern übergebenen Grenzen
liegen, die Quersumme in einem Memo-Fenster ausgibt.
3. Die Fibonacci-Zahlen sind durch f0 = 0, f1 = 1, fi+1 = fi + fi–1 für i = 1, 2, 3, ... definiert, d.h. jede Zahl, außer den
ersten beiden, ist die Summe der beiden vorangehenden.
Diese Zahlenfolge geht auf eine Additionsübung des italienischen Mathematikers Fibonacci im Jahr 1202 zurück.
Dabei bezeichnet fn die Anzahl der Hasenpaare nach n Monaten, wobei jedes Hasenpaar am Anfang eines jeden Monats ein neues Paar Junge bekommt, das nach einem Monat ebenfalls ein Paar Junge bekommt. In diesem einfachen
Modell sind alle Hasen unsterblich.
a) Schreiben Sie eine Funktion, die den n-ten Wert der Fibonacci-Folge berechnet. Dabei soll n als Parameter
übergeben werden.
b) Geben Sie die ersten 50 Werte der Fibonacci-Folge in einem Memo-Fenster aus.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
14
3 Elementare Datentypen und Anweisungen
4. Eine positive ganze Zahl n ist eine Primzahl, wenn sie genau zwei verschiedene Teiler hat (nämlich 1 und n) und
keine weiteren. Beispielsweise sind 2, 3 und 5 Primzahlen, aber 1, 4 und 6 sind keine.
a) Schreiben Sie eine boolesche Funktion prim, die für einen als Parameter übergebenen Ganzzahlwert n den Wert
true zurückgibt, wenn n eine Primzahl ist, und andernfalls den Wert false. Prüfen Sie dazu in einer Schleife alle
möglichen Teiler von n.
b) Schreiben Sie eine Funktion zeige_Primzahlen, die für einen als Parameter übergebenen Wert n alle Primzahlen
ausgibt, die <= n sind.
c) Nach einer Vermutung des Mathematiker Goldbach kann jede gerade ganze Zahl >= 6 als Summe von 2
Primzahlen dargestellt werden. Obwohl diese Vermutung schon vor über 200 Jahren aufgestellt und für
zahlreiche Werte überprüft wurde, konnte sie bis jetzt noch nicht bewiesen werden. Schreiben Sie eine Funktion
Goldbach, die für einen als Parameter übergebenen Wert n alle Kombinationen von Primzahlen mit der Summe
n in einem Memo-Fenster ausgibt. Außerdem soll die Anzahl dieser Kombinationen angezeigt werden.
5. Drei Zahlen a, b und c, für die a2 + b2 = c2 gilt (z.B. 3, 4 und 5), heißen Pythagoräisches Zahlentripel. Geben Sie
alle solchen Zahlentripel für a und b <= 50 in einem Memo-Fenster aus. Probieren Sie dazu für alle Kombinationen
von a und b und alle möglichen Werte von c aus, ob die Bedingung gilt.
6. Beim so genannten 3n+1-Problem wird ausgehend von einer positiven ganzen Zahl n nach dem folgenden
Verfahren eine Zahlenfolge bestimmt:
Für n=1 wird das Verfahren abgebrochen.
Falls n ungerade ist, ersetzt man n durch (3n+1).
Falls n gerade ist, ersetzt man n durch n/2.
So erhält man z.B. für n=5 die Zahlenfolge 5, 16, 8, 4, 2, 1. Nach einer Vermutung der Mathematiker Ulam und
Collatz konvergiert diese Folge immer. Obwohl diese Vermutung inzwischen für alle Zahlen <7*1011 überprüft
wurde, konnte sie bisher nicht allgemein bewiesen werden.
Schreiben Sie eine Funktion f3nplus1, die für einen als Parameter übergebenen Wert n die Anzahl der Schritte als
Funktionswert zurückgibt, bis das Verfahren abbricht. Zum Testen soll über einen zweiten Parameter gesteuert
werden können, ob alle Werte dieser Zahlenfolge in einem Memo-Fenster ausgegeben werden oder nicht.
7. Schreiben Sie eine Funktion Lotto, die 7 Zufallszahlen im Bereich 1 .. 49 in einem Memo-Fenster ausgibt. Der
Einfachheit halber ist es dabei nicht notwendig, dass alle 7 Zufallszahlen verschieden sind.
3.6 Der integrierte Debugger
Aufgabe 3.6: Die Gauß’sche Osterformel
Auf dem Konzil von Nicäa (325 n. Chr.) wurde festgelegt, dass der Ostersonntag der erste Sonntag nach dem ersten
Vollmond im Frühling ist. Nach Knuth (1973, Bd. 1) war die Berechnung des Ostersonntags die einzige wichtige
Anwendung der Arithmetik im Mittelalter.
Gauß hat die Arbeit der mit dieser Berechnung beschäftigten Mönche durch den folgenden Algorithmus rationalisiert.
Die hier dargestellte Version gilt allerdings nur bis zum Jahr 2299. Ein allgemeineres Verfahren findet man bei Knuth.
M und N seien durch die folgende Tabelle gegeben:
Jahr
1583–1699
1700–1799
1800–1899
1900–2099
2100–2199
2200–2299
M
22
23
23
24
24
25
N
2
3
4
5
6
0
A, B, C seien die Reste der Divisionen der Jahreszahl durch 19, 4 bzw. 7, D der Rest der Division von (19A + M) durch
30 und E der Rest der Division von (2B + 4C + 6D + N) durch 7.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
3.7 Gleitkommadatentypen
15
Dann ist der Ostersonntag gleich dem (22 + D + E)-ten März oder gleich dem (D + E – 9)-ten April, falls die folgenden
Grenzfälle berücksichtigt werden:
1. Ergibt sich der 26. April, so setze man stets den 19. April.
2. Ergibt sich der 25. April und gilt D = 28, E = 6 und A > 10, so fällt der Ostersonntag auf den 18. April.
Der Pfingstsonntag ist dann der siebte Sonntag nach Ostern.
Schreiben Sie ein Programm, das den Oster- und Pfingstsonntag berechnet. Testen Sie das Programm mit den
folgenden Werten:
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
Ostern:
29.
11.
2.
22.
14.
30.
18.
10.
26.
15.
6.
März
April
April
April
April
März
April
April
März
April
April
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
Pfingsten:
17.
30.
21.
10.
2.
18.
6.
29.
14.
3.
25.
Mai
Mai
Mai
Juni
Juni
Mai
Juni
Mai
Mai
Juni
Mai
Falls Ihre Ergebnisse nicht auf Anhieb mit diesen Testdaten übereinstimmen, führen Sie das Programm schrittweise im
Debugger aus und vergleichen Sie die Zwischenergebnisse im Programm mit denen, die sich ergeben müssten.
Eine schön ausgedruckte Liste mit den so berechneten Osterdaten ist auch gut als Ostergeschenk geeignet (jedes Jahr
ein anderes Jahrhundert). Weniger guten Freunden kann man eine Liste mit dem Datum von Weihnachten schenken
(auch jedes Jahr ein anderes Jahrhundert).
3.7 Gleitkommadatentypen
3.7.1 Die interne Darstellung von Gleitkommawerten
3.7.2 Der Datentyp von Gleitkommaliteralen
3.7.3 Implizite Konversionen
3.7.4 Mathematische Funktionen
Aufgaben 3.7
1. Welche der folgenden Anweisungen werden vom Compiler ohne Fehler übersetzt? Welche Werte erhalten die
Variablen d1, d2, i1, ..., i6?
int j=10,k=6;
double x=j,y=3.14,z=10;
double
double
int i1
int i2
int i3
int i4
int i5
int i6
d1 = j/k;
d2 = x/k;
= j/k;
= x/k;
= 3(j+k);
= j/k/k;
= j/(z/y);
= j/(y–j/k);
2. Daniels Notenprogramm
Schreiben Sie ein Programm, das etwa so aussieht:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
16
3 Elementare Datentypen und Anweisungen
Beim Anklicken eines der RadioButtons soll eine entsprechende Datei in ein Memo-Fenster geladen werden (z.B.
„Note.deu“ für die Deutschnoten). Als Reaktion auf „Durchschnitt berechnen“ soll der Mittelwert aller Zeilen im
Memo-Fenster berechnet und im Edit-Fenster angezeigt werden. Der Inhalt des Memos soll beim Anklicken von
„Speichern“ in der entsprechenden Datei gespeichert werden.
Vor dem ersten Start des Programms müssen die Textdateien manuell angelegt werden (z.B. mit dem Editor des
C++Builders).
3. Mit der vordefinierten Funktion rand() erhält man eine Zufallszahl zwischen 0 und RAND_MAX (0x7FFFU). Damit
kann man einen Näherungswert für die Kreiszahl π bestimmen, indem man für eine Folge von zwei aufeinander
folgenden Werten x und y prüft, ob sie im ersten Quadranten eines Kreises liegen (x2 + y2 < 1), und jeden solchen
Treffer mitzählt. Die Multiplikation der Treffer mit 4 und die Division des Ergebnisses durch die Anzahl der Versuche ergibt dann einen Näherungswert für π.
Schreiben Sie eine Funktion RegentropfenPi, die nach dieser „Regentropfenmethode“ einen Näherungswert für π
bestimmt und als Funktionswert zurückgibt. Die Anzahl der Versuche soll als Parameter übergeben werden. Zeigen
Sie die Näherungswerte bei 1000 (10000) Wiederholungen in einem Memo-Fenster an.
4. Das Produkt der ersten n Zahlen
f = 1*2*3* ... *n
wird auch als n! (n Fakultät) bezeichnet. Schreiben Sie eine Funktion Fakultaet, die die Fakultät für einen als
Parameter übergebenen Wert berechnet. Zeigen Sie die Werte für n=1 bis n=30 in einem Memo-Fenster an.
Die Fakultät tritt z.B. beim sogenannten Problem des Handlungsreisenden auf: Wenn ein Handlungsreisender n
Städte besuchen soll, hat er für die erste Stadt n Möglichkeiten, für die zweite n–1, für die dritte n–2 usw. Um jetzt
die kürzeste Route durch diese n Städte zu finden, müssen (zumindest im Prinzip) alle n! Routen miteinander
verglichen werden.
Zeigen Sie die Rechenzeit zur Bestimmung der kürzesten Route für n=15 bis n=30 in einem Memo-Fenster an,
wenn in einer Sekunde 1000 000 Routen verglichen werden können.
5. Reihenfolge der Summation bei Gleitkommadatentypen
a) Berechnen Sie die Summe der Zahlen 1/i2 von i=1 bis n (n=1000000) abwechselnd von unten (for (i=1; i<=n;
i++)...) und von oben (for (i=n; i>=1; i--)...). Dabei sollen alle Variablen für Summen und eventuelle
Zwischenergebnisse den Datentyp float haben. Vergleichen Sie die beiden Summen. Welche ist genauer?
b) Berechnen Sie die Summe aus a) auch für die anderen Gleitkommaformate double und long double. Dabei sollen
alle Summen und eventuellen Zwischenergebnisse den jeweiligen Gleitkommadatentyp haben.
6. Die Fläche zwischen der Funktion y=f(x) und der x-Achse im Bereich von a bis b kann näherungsweise durch die
Summe der Trapezflächen berechnet werden (Numerische Integration mit der Trapezregel):
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
3.7 Gleitkommadatentypen
17
y
a
a+h
a+2h
b
x
Die Fläche des Trapezes
von a bis a+h ist dabei durch h*(f(a) + f(a+h))/2 gegeben,
die von a+h bis a+2h durch h*(f(a+h) + f(a+2*h))/2 usw.
Unterteilt man das Intervall von a bis b in n Teile, ist h = (b–a)/n.
Berechnen Sie so einen Näherungswert für π als Fläche des Einheitskreises, indem Sie die Trapezflächen unter der
Funktion sqrt(1–x*x) von 0 bis 1 aufsummieren. Wählen Sie für n verschiedene Werte, z.B. 100, 1000 und 10000.
7. Das Geburtstagsproblem von Mises
Wenn sich außer Ihnen noch eine weitere Person in einem Raum befindet, ist die Wahrscheinlichkeit q, dass diese
Person an einem anderen Tag als Sie Geburtstag hat
q = 364/365
Bei zwei weiteren Personen im Raum ist die Wahrscheinlichkeit, dass alle drei an einem verschiedenen Tag
Geburtstag haben
q = (364*363)/(365*365)
Bei n Personen ist diese Wahrscheinlichkeit
q = 364*363*...*(365–n+1)/365n–1
Die Wahrscheinlichkeit, dass von n Personen in einem Raum mindestens zwei am selben Tag Geburtstag haben, ist
dann gegeben durch
p = 1–q
a) Schätzen Sie zuerst, ab wie vielen Personen in einem Raum diese Wahrscheinlichkeit > 50% ist.
b) Schreiben Sie eine Funktion, deren Funktionswert diese Wahrscheinlichkeit für n Personen ist. Bestimmen Sie
dann den kleinsten Wert von n, für den ihr Funktionswert größer als 0.5 ist. Am einfachsten geben Sie dazu den
Funktionswert für verschiedene Werte von n aus.
8. Die durch f0 = 0, f1 = 1, fi+1 = fi + fi–1 für i = 1, 2, 3, ... definierten Fibonacci-Zahlen (siehe auch Aufgabe 3.4.7.2)
stehen in einer engen Beziehung zum „Goldenen Schnitt“
g = (1+ sqrt(5))/2
Man kann zeigen, dass für h = 1 – g die folgende Identität gilt:
fn = (gn + hn)/sqrt(5)
Vergleichen Sie die beiden Zahlenfolgen bis n = 40. Stellen Sie dazu g und h in verschiedenen Durchläufen mit den
Datentypen float, double und long double dar.
9. Schreiben Sie eine Funktion round, die ein Gleitkomma-Argument auf den nächsthöheren Ganzzahlwert aufrundet,
falls seine Nachkommastellen >=0.5 sind, und andernfalls abrundet.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
18
3 Elementare Datentypen und Anweisungen
3.8 Syntaxregeln für einfache Deklarationen
3.9 Zeiger, Strings und dynamisch erzeugte Variablen
3.9.1 Die Definition von Zeigern
3.9.2 Der Adressoperator, Zuweisungen und generische Zeiger
3.9.3 Dynamisch erzeugte Variablen: new und delete
3.9.4 Zeiger als Parameter und die Zeigertypen der Windows-API
3.9.5 Zeigerarithmetik
3.9.6 Nullterminierte Strings und Zeiger auf char
3.9.7 Zeiger auf Zeiger auf Zeiger auf ...
3.9.8 Referenztypen und Referenzparameter
Aufgaben 3.9
1. Nach den Definitionen
int i=5;
int *pi, pj;
char *pc, pd;
sollen die folgenden Zuweisungen stattfinden:
a)
c)
e)
g)
i)
pi=i;
*pi=i;
pi=pj;
pi=pc;
*pi=i**pc;
b)
d)
f)
h)
j)
pi=&i;
*pi=&i;
pc=&pd;
pd=*pi;
pi=0;
Geben Sie an, welche dieser Zuweisungen syntaktisch korrekt sind. Geben Sie außerdem für jeden syntaktisch
korrekten Ausdruck den Wert der linken Seite an, wobei die Ergebnisse der zuvor ausgeführten Anweisungen
vorausgesetzt werden sollen. Falls die linke Seite ein Zeiger ist, geben Sie den Wert der dereferenzierten Variablen
an.
2. Was wird durch folgende Anweisungen ausgegeben:
int i=10;
int* p=&i;
*p=17;
Memo1->Lines->Add(IntToStr(i));
Memo1->Lines->Add(IntToStr(*p));
3. In dieser Aufgabe soll lediglich der Umgang mit new, delete und dynamisch erzeugten Variablen geübt werden.
Ansonsten besteht kein Grund, diese Aufgabe so zu lösen.
Formulieren Sie die Lösungen der folgenden Aufgaben mit dynamisch erzeugten Variablen anstatt mit lokalen
Variablen:
a) 3.5.2 (Quersumme)
b) 3.5.2 (Fibonacci-Zahlen)
4. Schreiben Sie eine Funktion, die die Anzahl der Leerzeichen ' ' in einem als Parameter übergebenen
nullterminierten String (char *) als Funktionswert zurückgibt.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
3.10 Konstanten
19
Weitere Informationen zu den Windows-API-Funktionen der folgenden beiden Aufgaben findet man unter
Start|Programme|Borland C++Builder|Hilfe|Hilfedateien MS SDK|Win32 SDK Referenz.
5. Die Windows-API-Funktion
DWORD GetTempPath(
DWORD nBufferLength,
LPTSTR lpBuffer);
// Größe des Puffers (in Zeichen)
// Adresse des Puffers
schreibt die ersten nBufferLength Zeichen des Verzeichnisses für temporäre Dateien in den Speicherbereich, dessen
Adresse als lpBuffer übergeben wird. Dabei wird dieses Verzeichnis folgendermaßen bestimmt:
– als der Pfad, der in der Umgebungsvariablen TMP steht
– falls TMP nicht definiert ist, als der Pfad in TEMP
– falls weder TMP noch TEMP definiert sind, als das aktuelle Verzeichnis.
Der Funktionswert ist 0, falls der Aufruf dieser Funktion fehlschlägt. Andernfalls ist er die Länge des Strings.
Schreiben Sie eine Funktion ShowTempPath, die das Verzeichnis für temporäre Dateien in einem Memo-Fenster
ausgibt. Da ein Pfadname maximal MAX_PATH Zeichen lang sein kann, sollen für den Puffer MAX_PATH
Zeichen reserviert werden.
6. Die Windows-API-Funktion
DWORD GetLogicalDriveStrings(
DWORD nBufferLength, // Größe des Puffers (in Zeichen)
// Adresse des Puffers
LPTSTR lpBuffer);
schreibt eine Liste der gültigen Laufwerke in den Speicherbereich, dessen Adresse in lpBuffer übergeben wird. Die
Größe dieses Speicherbereichs wird in nBufferLength übergeben.
Diese Liste ist eine Folge von nullterminierten Strings, deren Ende durch einen doppelten Nullterminator
gekennzeichnet ist wie z.B.
c:\<null>d:\<null><null>
Hier steht <null> für den Nullterminator. Weitere Informationen findet man in der Datei „win32.hlp“.
Schreiben Sie eine Funktion ShowDriveStrings, die alle gültigen Laufwerke in einem Memo-Fenster anzeigt.
Reservieren Sie dazu Speicherplatz für 25 „Zeiger auf char“, die jeweils auf die Anfänge der Teilstrings „c:\“, „d:\“
usw. zeigen.
3.10 Konstanten
3.11 Deklarationen mit typedef und typeid-Ausdrücke
Aufgabe 3.11
In den Header-Dateien von Windows sind unter anderem die Namen PBOOL, LPLONG, LPDWORD, LPVOID und
LPCVOID für Datentypen definiert. Welche Datentypen verbergen sich hinter diesen Namen?
3.12 Aufzählungstypen
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
20
3 Elementare Datentypen und Anweisungen
Aufgaben 3.12
1. Welche der folgenden Definitionen sind zulässig?
a) enum Monate{Januar=1, Februar, März, April, Mai, Juni,
Juli, August, September, Oktober, November, Dezember};
b) enum Monate {Jan = "Januar", Februar = "Februar"};
c) enum {max=100};
int a[max];
2. Schreiben Sie ein Programm mit 6 RadioButtons, das etwa folgendermaßen aussieht:
a) Beim Anklicken des jeweiligen RadioButtons soll die Eigenschaft Borderstyle des Formulars auf den
entsprechenden Wert gesetzt werden.
b) Durch sukzessives Anklicken des Buttons soll die Eigenschaft BorderStyle nacheinander alle möglichen Werte
annehmen. Nach dem letzten Wert (in der Liste der Aufzählung) soll wieder der erste Wert gesetzt werden.
3.13 Kommentare und interne Programmdokumentation
3.14 Präprozessoranweisungen
3.14.1 Die include-Anweisung
3.14.2 Makros ⊕
3.14.3 Bedingte Kompilation ⊕
3.14.4 Pragmas ⊕
Aufgaben 3.14
In älteren Versionen des C++Builders waren die Konstanten für Farbwerte wie
clBlack, clMaroon, clGreen, clOlive, ...
oder die zulässigen Werte für die Eigenschaft Cursor
crDefault, crArrow, crCross, crIBeam, crSize, ...
in „include\vcl\graphics.hpp“ als Makros definiert. Ein Auszug:
#define clBlack (TColor)(0)
#define clGreen (TColor)(32768)
Was passiert, wenn in einem Programm eine Variable dieses Namens definiert wird, z.B.
int clBlack=1;
In der Version 5 des C++Builders sind diese Werte symbolische Konstanten.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4 Strukturierte Datentypen und vordefinierte Klassen
4.1 Die Stringklassen AnsiString und string
4.1.1 Die Verwendung von Elementfunktionen vordefinierter Klassen
4.1.2 Gemeinsamkeiten und Unterschiede der Stringklassen
4.1.3 Funktionen für die Klasse AnsiString
4.1.4 Einige Elementfunktionen der Klasse string
Aufgaben 4.1
1. Ein String mit einem Kalenderdatum soll aus Zahlen für den Tag, den Monat und das Jahr bestehen, die durch
einen Punkt getrennt sind (z.B. s="1.2.98" oder s="11.12.2001"). Erzeugen Sie aus einem solchen String drei
Strings mit den Zahlen für den Tag, den Monat und das Jahr und geben Sie diese aus.
a) Eine erste Version soll einen String der Klasse AnsiString zerlegen und Funktionen für diese Klasse verwenden.
b) Eine zweite Version soll einen String der Klasse string zerlegen und Funktionen für diese Klasse verwenden,
aber nicht die Funktion erase.
2. Schreiben Sie ein kleines Programm, mit dem man in einer Textdatei nach einer Telefonnummer suchen kann:
Beim Start des Programms (Ereignis OnCreate des Formulars) wird eine Datei (z.B. "c:\telefon.txt") in ein MemoFenster (im Bild nach Edit) geladen.
a) Nach dem Anklicken des Buttons suchen sollen alle Zeilen dieses Memo-Fensters mit der Funktion Pos
daraufhin überprüft werden, ob sie den Teilstring enthalten, der im Edit-Fenster (im Bild rechts von suchen) eingegeben wurde. Falls das zutrifft, wird dieser String in das Memo-Fenster nach Suchergebnisse: eingefügt.
b) Die Suche unter a) soll unabhängig von der Groß- und Kleinschreibung funktionieren.
c) Da man einen Text in einem Memo-Fenster editieren kann, lassen sich hier auch Telefonnummern neu
eintragen, ändern oder löschen. Mit dem Button speichern soll die veränderte Liste gespeichert werden können.
d) Nach der gewünschten Telefonnummer soll nicht nur beim Anklicken des Buttons mit der Aufschrift „suchen“
gesucht werden, sondern auch dann, wenn im Edit-Fenster die Taste Enter bzw. Return gedrückt wird. Dazu
kann man beim Ereignis OnKeyPress des Edit-Fensters die Funktion aufrufen, die als Reaktion auf das
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
22
4 Strukturierte Datentypen und vordefinierte Klassen
Anklicken des Buttons definiert wurde. Wenn die Taste Return gedrückt wurde, hat der Parameter Key in
KeyPressed den Wert 13.
Mit dem Soundex-Verfahren von Aufgabe 6 können auch Namen gefunden werden, deren Schreibweise man nicht
genau kennt.
3. Schreiben Sie zwei Versionen einer Funktion parseString, die alle Teilstrings eines Strings ausgibt, die durch eines
der Zeichen ‘.’, ‘;’, ‘ ‘ und ‘-‘ getrennt sind. Dabei sollen die Trennzeichen nicht ausgegeben werden.
a) Die erste Version der Funktion parseString soll einen string zerlegen.
b) Die zweite Version soll einen String der Klasse AnsiString zerlegen.
c) Testen Sie diese Funktion z.B. mit den folgenden Strings:
void testParseString()
{
parseString("456 ab.xy");//Ausgabe "456","ab","xy"
parseString("123");// nur Teilstring, Ausgabe "123"
parseString("");
// leerer String, keine Ausgabe
parseString(" ");// nur Trennzeichen, keine Ausgabe
}
Weitere Tests sollen die Strings "123" bzw. "456 ab.xy" systematisch mit einem Trennzeichen am Anfang, am
Ende sowie am Anfang und am Ende kombinieren.
4. Geben Sie die Zinsen für ein Kapital von 100 DM bei einem Zinssatz von 5 bis 15% in einem Memo-Fenster aus.
Jede Zeile soll dabei folgendermaßen formatiert sein (Kapital: 8 Stellen, Zinssatz: 4 Stellen, Zins: 7 Stellen, alle mit
2 Nachkommastellen, keine führenden Nullen):
K = 123456,78 p = 12,34% z = 12345,67
Dabei sollen alle Kommas untereinander stehen. Wählen Sie dazu im Memo-Fenster eine Schriftart, die keine
Proportionalschrift ist (z.B. Courier).
5. Schreiben Sie eine Funktion BinaerDarstellung, die aus einem ganzzahligen Wert einen String mit den binären
Ziffern der Zahl erzeugt.
Sie können dazu folgendermaßen vorgehen: „Kleben“ Sie an einen zunächst leeren String die Zeichen „0“ oder „1“,
und zwar je nachdem, ob die Zahl gerade oder ungerade ist. Verschieben Sie dann die Bits der Zahl um eine
Position nach rechts, z.B. durch eine Division durch 2. Wiederholen Sie diese Schritte für alle Bits der Zahl.
6. Schreiben Sie eine Funktion BigIntAdd, die zwei positive ganze Zahlen addiert, deren Ziffern in einem String
dargestellt werden. Die Länge soll lediglich durch die maximale Länge der Strings begrenzt sein. Beispiel:
BigIntAdd("123","1000000") = "1000123"
a) Definieren Sie diese Funktion für AnsiStrings.
b) Definieren Sie diese Funktion für die Strings aus der Standardbibliothek.
c) Testen Sie diese Funktionen, indem Sie den Wert des Strings "2" immer wieder verdoppeln. Dabei müssen sich
dann die folgenden Werte ergeben:
22=4
23=8
24=16
...
250=1125899906842624
2100=1267650600228229401496703205376
7. Der Soundex-Code versucht, ähnlich klingende Wörter durch denselben Code darzustellen. Mit ihm wurden schon
bei der amerikanischen Volkszählung 1880 die Namen der Haushalte kodiert.
Dazu wandelt man das Wort zunächst in Großbuchstaben um und entfernt alle Zeichen, die keine Buchstaben sind.
Umlaute werden durch „AE“, „OE“ und „UE“ ersetzt und doppelte Buchstaben durch einen einzigen (also z.B.
"AA" durch "A").
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.2 Arrays und Container
23
Der Soundex-Code beginnt dann mit dem ersten Zeichen dieses Strings. Die Zeichen ab dem zweiten werden dann
nach der folgenden Tabelle kodiert und an den Soundex-Code angehängt, wenn sie nicht '0' sind und der Code nicht
länger als 4 Zeichen ist:
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
code = "01230120022455012623010202";
'B', 'F', 'P' und 'V' werden also durch '1' kodiert. Ist der entstandene Soundex-Code kürzer als vier Stellen, wird er
mit '0' auf 4 Stellen aufgefüllt.
Beispiele: Euler, Ellery: Code E460
Gauss, Ghosh: Code G200
Hilbert, Heilbronn: Code H416
Wie diese Beispiele zeigen, können auch Strings dieselben Codes ergeben, die nicht sehr ähnlich klingen.
Schreiben Sie eine Funktion SoundexCode, die den Soundex-Code eines Strings als Funktionswert zurückgibt.
4.2 Arrays und Container
4.2.1 Eindimensionale Arrays
Aufgaben 4.2.1
1. Überlegen Sie, ob die folgenden Anweisungen syntaktisch korrekt sind. Falls ja, beschreiben Sie das Ergebnis dieser
Anweisungen.
a) int a[10];
for (int i=1; i<=10;i++) a[i] = 0;
b) char* p[]={"Alle","meine","Entchen"};
for (int i=0; i<=3;i++)
Memo1->Lines->Add(p[i]);
c) char* p="schwimmen in", *q="dem See";
if (p==q) Memo1->Lines->Add("gleich");
else Memo1->Lines->Add("verschieden");
2. Auch auf einfache Fragen gibt es oft vielfältig widersprüchliche Antworten. So hat vor einiger Zeit jemand in einer
Diskussionsgruppe im Internet gefragt, ob die folgenden Anweisungen
char *x;
x = "hello";
von erfahrenen Programmierern als korrekt angesehen würden. Er erhielt darauf über 100 Antworten, aus denen die
folgenden vier ausgewählt wurden.
Diese geben insbesondere auch einen Hinweis auf die Qualität mancher Beiträge in solchen Diskussionsgruppen.
Begründen Sie für jede Antwort, ob sie korrekt ist oder nicht.
a) Nein. Da durch diese Anweisungen kein Speicher reserviert wird, überschreibt die Zuweisung einen anderen
Speicherbereich.
b) Diese Anweisungen haben eine Zugriffsverletzung zur Folge. Die folgende Anweisung ist viel besser:
char* x="hello";
c) Antwort auf b):
Welcher Compiler produziert hier eine Zugriffsverletzung? Die beiden Anweisungen sind völlig gleichwertig.
d) Ich würde die Anweisung
char x[]="hello";
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
24
4 Strukturierte Datentypen und vordefinierte Klassen
vorziehen, da diese sizeof(char*) Bytes Speicherplatz reserviert.
3. Wenn man die ersten 100 Primzahlen bestimmen will, kann man der Reihe nach alle Zahlen daraufhin prüfen, ob
sie keine Teiler haben. Diese Vorgehensweise ist allerdings mit vielen unnötigen Divisionen verbunden.
Diese kann man mit einem Verfahren vermeiden, das nach dem griechischen Mathematiker Eratosthenes als Sieb
des Eratosthenes benannt ist: In einer Liste der Zahlen 1 bis 100 streicht man nacheinander zuerst alle Vielfachen
von 2, dann alle Vielfachen von 3, 5 usw. Die Zahlen, die dabei übrig bleiben, sind dann die Primzahlen.
Realisieren Sie dieses Verfahren mit einem booleschen Array. Alle Werte in diesem Array erhalten zunächst den
Wert true.
4. Bei manchen Problemen findet man eine explizite Lösung nur schwierig oder überhaupt nicht. Dann können
Simulationen hilfreich sein. Sind diese mit Zufallszahlen verbunden, bezeichnet man sie auch als Monte-Carlo-Simulationen (nach dem Spielerparadies).
Schreiben Sie ein Programm zur Simulation des Geburtstagsproblems von Mises (siehe Aufgabe 3.7.7). Sie
können dazu folgendermaßen vorgehen:
Ein Array mit 365 Elementen soll die Tage eines Jahres darstellen. Mit dem Zufallszahlengenerator random(365)
wird dann für alle Personen im Raum ein Geburtstagsdatum ermittelt und im Array für die Tage ein Zähler hochgesetzt. Diese Vorgehensweise wiederholt man dann mehrfach (z.B. 1000-mal). Falls nach einem solchen Versuch
mindestens ein Zähler den Wert 2 oder mehr hat, entspricht das zwei oder mehr Personen, die an einem Tag
Geburtstag haben.
4.2.2 Arrays als Container
Aufgaben 4.2.2
1. Legen Sie ein neues Projekt an. Das dazu gehörende Formular soll zunächst ein Edit- und ein Memo-Fenster
enthalten. Ein Array a mit AnsiStrings soll die Texte aufnehmen, die in dem Edit-Fenster eingegeben werden.
Die ersten 5 Teilaufgaben betreffen unsortierte Arrays:
a) Beim Anklicken eines Buttons mit der Aufschrift „Einfügen“ soll dem Array a der aktuelle Text im Edit-Fenster
hinzugefügt werden.
b) Beim Anklicken eines Buttons „Anzeigen“ sollen alle bisher im Array a abgelegten Strings im Memo-Fenster
ausgegeben werden.
c) Beim Anklicken eines Buttons „Linear suchen“ sollen alle Strings des Arrays a ausgegeben werden, die gleich
dem String im Edit-Fenster sind.
d) Beim Anklicken eines Buttons „Löschen“ soll das erste Element im Array gelöscht werden, dessen Wert der Text
im Edit-Fenster ist.
e) Beim Anklicken eines Buttons „Sortieren“ soll das Array mit dem Auswahlsort sortiert werden.
Die nächsten drei Teilaufgaben betreffen sortierte Arrays. Falls das Array vor der Ausführung einer dieser
Operationen sortiert war, soll es auch nachher noch sortiert sein.
f) Beim Anklicken eines Buttons „Binär suchen“ soll der String im Edit-Fenster im Array gesucht werden. Falls er
gefunden wird, soll er anschließend im Memo-Fenster angezeigt werden.
g) Beim Anklicken eines Buttons „Sortiert Einfügen“ soll der String im Edit-Fenster in das Array eingefügt
werden.
h) Beim Anklicken eines Buttons „Sortiert Löschen“ soll das erste Element im Array gelöscht werden, dessen Wert
der Text im Edit-Fenster ist.
2. Die Datenstruktur Stack
Realisieren Sie einen Stack mit einem Array, dessen Elemente den Datentyp AnsiString haben.
– Beim Anklicken eines Buttons mit der Aufschrift push soll der Text eines Edit-Fensters auf den Stack gelegt
werden (sofern noch Platz frei ist).
– Durch Anklicken eines Buttons pop soll der oberste String vom Stack entfernt (falls dieser ein Element enthält)
werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.2 Arrays und Container
25
– Durch Anklicken eines Buttons top soll der oberste String des Stacks in einem Memo-Fenster angezeigt werden.
Verwenden Sie dazu eine Variable StackPtr, die immer den Index des obersten Elements enthält.
Falls der Stack leer ist, sollen die Buttons pop und top über die Eigenschaft Enabled deaktiviert werden. Falls die
Kapazität des verwendeten Arrays erschöpft ist, dasselbe für den Button push.
3. Tröpfelverfahren zur Berechnung der ersten 5000 Stellen von π
Im Jahr 1995 haben die beiden Mathematiker Rabinowitz und Wagon einen Algorithmus vorgestellt, mit dem man
die Kreiszahl π auf beliebig viele Stellen berechnen kann (Rabinowitz/Wagon 1995, außerdem Stewart 1995).
Im Unterschied zu den meisten anderen Algorithmen zur Berechnung von π wird dabei der Näherungswert nicht als
Gleitkommazahl berechnet. Vielmehr liefert dieses Verfahren eine Ziffer nach der anderen als Ganzzahl und wird
deshalb auch als Tröpfelverfahren bezeichnet (weil die Ziffern wie aus einem Wasserhahn tröpfeln). Es beruht auf
einer Reihendarstellung von π, die als Darstellung zu einer gemischten Basis betrachtet wird. Diese Darstellung
wird dann in das Dezimalsystem umgerechnet.
Um n Stellen von π zu berechnen, definiert man ein Array A mit 10*n/3 int-Elementen und initialisiert alle Werte
auf 2. Eine Ganzzahlvariable U für die Überträge erhält den Wert 0.
a) Die ersten n=31 Stellen von π erhält man, indem die folgenden Schritte n-mal wiederholt:
Vom obersten zum zweiten Index des Arrays A (for .. i--) führt man jeweils die folgenden Berechnungen
durch, bei denen i jeweils den Index des aktuellen Arrayelements bezeichnet:
– Berechne die Summe
S = U*i+10*A[i];
– Berechne den Quotienten q und den Rest r, der sich bei der Division von S durch 2*i–1 ergibt. Ersetze
den bisherigen Wert von A[i] durch r und den bisherigen Übertrag durch q.
Berechne die Summe S aus dem zehnfachen Wert des ersten Elements von A und dem Übertrag. Ersetze das
erste Element von A durch den Rest der Division von S durch 10 und S durch den Quotienten dieser
Division.
In den folgenden Listen der überwachten Ausdrücke sind die Werte für das Übertragsfeld in einem Array U
gespeichert.
Nach dem ersten Durchlauf ergeben sich so die Werte:
Nach dem zweiten Durchlauf:
Nach dem dritten Durchlauf:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
26
4 Strukturierte Datentypen und vordefinierte Klassen
usw. Hier sieht man die ersten drei Stellen von π als den jeweiligen Wert von s nach jeweils einem solchen
Durchlauf.
b) Das unter a) beschriebene Verfahren funktioniert allerdings nur für die ersten 31 Stellen von π, weil der Wert
von S nach dem Durchlauf einer Schleife größer oder gleich 100 werden kann (maximal 109). Dieser Fall tritt
erstmals für die 32. Stelle von π ein und muss durch die folgende Erweiterung des Verfahrens berücksichtigt
werden.
Die nach jeweils einem Durchlauf gefundenen Ziffern S sind nicht immer die Ziffern von π, sondern müssen
zunächst zwischengespeichert werden. In Abhängigkeit vom jeweils aktuellen Wert von S können dann die
bisher zwischengespeicherten Werte entweder als gültige Ziffern freigegeben oder müssen um 1 erhöht werden:
– Falls S weder 9 noch 10 ist, können alle bisher zwischengespeicherten Ziffern als gültige Ziffern freigegeben
werden. Die aktuelle Ziffer S muss als vorläufige Ziffer gespeichert werden.
– Falls S=9 ist, wird S den zwischengespeicherten Ziffern hinzugefügt.
– Falls S=10 ist, werden alle bisher zwischengespeicherten Ziffern um 1 erhöht, wobei 9 zu 0 wird. Alle so
erhöhten Ziffern können als gültige Ziffern freigegeben werden. Als neue Ziffer wird 0 zwischengespeichert.
Nach Rabinowitz und Wagon sind bei diesem Verfahren alle Zwischenergebnisse für die ersten 5000 Stellen von
π kleiner als 600 000 000. Damit können mit 32-bit-Binärzahlen mehr als 5000 Stellen berechnet werden.
Auf einem Pentium 200 dauert die Berechnung von 1000 Stellen ca. 2 Sekunden.
Die ersten 99995 Stellen von π findet man im Internet z.B. unter der Adresse
http://cad.ucla.edu/repository/useful/PI.txt
Damit Sie Ihre Ergebnisse vergleichen können, davon die ersten 101 Stellen:
const AnsiString pi101 = "pi=3"
+"14159265358979323846264338327950288419716939937510"
"58209749445923078164062862089986280348253421170679"
4.2.3 Mehrdimensionale Arrays
Aufgaben 4.2.3
1. Geben Sie Anweisungsfolgen an, mit denen man in einem Array die Position der folgenden Elemente findet. Falls es
mehrere solche Elemente gibt, soll die Position des zuerst gefundenen bestimmt werden:
a) in einem zweidimensionalen Array „int a[m][n]“ das kleinste Element,
b) in einem dreidimensionalen Array „int a[m][n][p]“ das kleinste Element.
Testen Sie diese Anweisungsfolgen.
2. Ein zweidimensionales Array d mit n Zeilen und Spalten soll eine Entfernungstabelle zwischen n Städten
darstellen. Dabei ist der Wert des Elements d[i][j] die Entfernung zwischen den Städten mit den Nummern i und j.
Alle Elemente d[i][i] haben den Wert 0, außerdem gilt d[i][j]==d[j][i]:
d[][3]={{ 0, 10, 20},
{10, 0, 15},
{20, 15, 0}};
Eine Fahrtroute durch m dieser Städte soll durch ein Array mit m+1 Elementen dargestellt werden, wobei das erste
Element die Anzahl der besuchten Städte enthalten soll. Das Array
int r[] = {4, 2, 1, 2, 0};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.2 Arrays und Container
27
stellt also die Route 2 -> 1 -> 2 -> 0 dar.
a) Schreiben Sie eine Anweisungsfolge, die die Länge der durch r dargestellten Route bestimmt.
b) Ein Array s mit m Zeilen soll m solcher Routen enthalten. Schreiben Sie eine Anweisungsfolge, die die kürzeste
dieser Routen bestimmt.
Testen Sie Ihre Anweisungen mit der Entfernungstabelle d.
3. Beim Eliminationsverfahren von Gauß zur Lösung eines linearen Gleichungssystems
// Zeile 0
a00*x0 + a01*x1 + ...+ a0,n–1*xn–1 = b0
// Zeile 1
a10*x0 + a11*x1 + ...+ a1,n–1*xn–1 = b1
...
an–1,0*x0 + an–1,1*x1 + ...+ an–1,n–1*xn–1 = bn–1 // Zeile n–1
wird für j=1, ..., n–1 das
–aj0/a00 -fache der Zeile 0
zur Zeile j addiert. Dadurch werden alle Koeffizienten aj0 unterhalb von a00 zu 0. Diese Vorgehensweise wiederholt
man für die Koeffizienten unterhalb von a11, a22 usw. Dadurch erhält man ein Gleichungssystem der Form
a’00*x0
0*x0
...
0*x0
+ a’01*x1 + ...+ a’0,n–1*xn–1
+ a’11*x1 + ...+ a’1,n–1*xn–1
+ 0*x1
= b’0
= b’1
+ ...+ a’n–1,n–1*xn–1 = b’n–1
// Zeile 0
// Zeile 1
// Zeile n–1
bei dem alle Koeffizienten unterhalb der Diagonale den Wert 0 haben. Dieses Gleichungssystem kann man
rückwärts auflösen:
xn–1 = b’n–1/ a’n–1,n–1
xn–2 = (b’n–2 – a’n–2,n–1 *xn–1)/a’n–2,n–2
...
x0 = (b’0 – a’0,n–1 *xn–1 – ... – a’01 *x1)/a’0,0
a) Schreiben Sie eine Funktion GaussElimination, die ein lineares Gleichungssystem löst. Da im Lauf der
zahlreichen Rechenschritte Rundungsfehler das Ergebnis verfälschen können, soll außerdem eine Probe gemacht
werden. Testen Sie diese Funktion mit n=3 und> den Werten
a[n][n]={{1,2,3},{1,4,6},{2,3,7}}; b[n]={1,2,3}; // x={0,–0.4,0.6}
a[n][n]={{2,3,–5},{4,8,–3},{–6,1,4}}; b[n]={–10,–19,–11}; //x={2,–3,1}
b) Dieses Verfahren funktioniert allerdings nur, wenn keine Division durch Null auftritt. Außerdem sollte zur
Minimierung von Rundungsfehlern das zur Division in –aj,i/ai,i verwendete Diagonalelement möglichst groß sein.
Wenn das Gleichungssystem lösbar ist, erreicht man beide Ziele, indem man unterhalb der Diagonalen die Zeile
mit dem größten Wert in der i-ten Spalte sucht und dann diese Zeile mit der i-ten Zeile vertauscht.
Realisieren Sie diese Vorgehensweise mit zwei Funktionen: In der einen soll nach der Zeile mit dem größten
Spaltenwert gesucht werden und in der anderen sollen zwei Zeilen vertauscht werden.
4.2.4 Arrays, Zeiger und Zeigerarithmetik
Aufgabe 4.2.4
Beschreiben Sie nach den Definitionen
int a[10]={1,3,5,7}, *p=a;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
28
4 Strukturierte Datentypen und vordefinierte Klassen
den Wert der folgenden Ausdrücke durch indizierte Ausdrücke des Arrays a:
*p
*p+1
(*p)+1
*(p+1)
*(p+3**p)
*p**p
4.2.5 Arrays als Funktionsparameter
Aufgaben 4.2.5
1. a) Schreiben Sie eine Funktion AuswSort, die ein als Parameter übergebenes Array von int-Werten mit dem
Auswahlsort aufsteigend sortiert. Die Anzahl der zu sortierenden Elemente soll ebenfalls als Parameter
übergeben werden.
b) Formulieren Sie den Auswahlsort mit der Zeigerarithmetik. Schreiben Sie dazu eine Funktion AuswSortPtr, die
dieselbe Parameterliste wie die Funktion AuswSort hat.
c) Testen Sie diese Funktionen, indem sie nach dem Sortieren prüfen, ob alle Arrayelemente in der richtigen
Reihenfolge aufeinander folgen. Schreiben Sie dazu eine Funktion TestSort. Sie soll dieselbe Parameterliste wie
die Funktion AuswSort haben. Das zu sortierende Array kann mit Zufallszahlen gefüllt werden.
d) Testen Sie die Funktion TestSort an einigen Arrays
– mit einem Arrayelement
– mit zwei Arrayelementen in einer richtigen Anordnung
– mit zwei Arrayelementen in einer falschen Anordnung
– mit drei Arrayelementen in allen dabei möglichen Anordnungen.
2. Die im C++Builder (aber nicht im C++-Standard) vordefinierte Funktion
double poly(double x, int degree, double coeffs[]); // #include <math.h>
berechnet den Funktionswert des Polynoms, dessen Koeffizienten im Array coeffs übergeben werden. Dabei ist x der
Wert, für den der Funktionswert berechnet wird, und degree der Grad des Polynoms. Mit
double p[]={3,–2,1,–1}
wird der Wert von –x3 + x2 – 2*x + 3 berechnet durch
poly(x,3,p)
Schreiben Sie eine Funktion WerteTab, die eine Wertetabelle im Bereich von a bis b in der Schrittweite h in einem
Memo ausgibt. Dieser Funktion sollen a, b, h und das Polynom als Parameter übergeben werden.
4.2.6 Dynamisch erzeugte Arrays
Aufgabe 4.2.6
1. Überarbeiten Sie die Funktion GaussElimination von Aufgabe 4.2.3.3 so, dass sie für ein beliebiges n ein lineares
Gleichungssystem mit n Gleichungen und n Unbekannten löst. Die Koeffizientenmatrix a, n, die rechte Seite b, der
Lösungsvektor x und der Vektor p für die Probe sollen als Parameter übergeben werden. Übernehmen Sie möglichst
viele Anweisungen aus der Lösung von Aufgabe 4.2.3.3. Reservieren Sie den Speicherplatz für das Array a durch
eine Funktion wie Create2DArray und geben Sie ihn durch eine Funktion wie Free2DArray wieder frei.
2. Lesen Sie die Koeffizienten und die rechte Seite des Gleichungssystems aus einem StringGrid (Komponentenpalette
Seite „Zusätzlich“) ein. In die Zellen cells[i][j] eines StringGrid kann man Werte eingegeben, wenn die Option
goEditing unter Options auf true gesetzt wird. Die Größe des StringGrid soll während der Laufzeit des Programms
gesetzt werden können. Informieren Sie sich dazu in der Online-Hilfe über die Eigenschaften RowCount und
ColCount. Beachten Sie, dass ein StringGrid seine Zellen mit [Spalte][Zeile] adressiert und nicht wie C++ mit
[Zeile][Spalte].
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.3 Sequenzielle Container der Standardbibliothek
29
4.2.7 Array-Eigenschaften der VCL
4.3 Sequenzielle Container der Standardbibliothek
4.3.1 Die Container-Klasse vector
4.3.2 Algorithmen der Standardbibliothek
Aufgabe 4.3.2
1. Verwenden Sie für die Aufgaben in a) bis c) geeignete Algorithmen der STL.
a) Sortieren Sie ein Array mit 10 Werten des Datentyps int. Geben Sie alle Werte des Arrays nach dem Sortieren
aus.
b) Kopieren Sie alle Elemente eines Arrays in ein anderes, das genügend groß ist.
c) Prüfen Sie, ob zwei Arrays dieselben Elemente haben.
2. Lösen Sie Aufgabe 4.2.2.1 a) bis h) mit einem Vektor anstelle eines Arrays.
3. Überarbeiten Sie die beiden Versionen der Funktion parseString von Aufgabe 4.1.3 so, dass sie die gefundenen
Teilstrings in einem vector zurückgeben. Damit diese Funktionen später zur Lösung weiterer Aufgaben verwendet
werden können, sollen sie in eine Datei „\CppUtils\StringUt.cpp“ aufgenommen werden, die mit #include in ein
Programm eingebunden werden kann.
4.3.3 Die Container-Klassen list und deque
4.3.4 Die Container-Adapter stack, queue und priority_queue
4.3.5 Container mit Zeigern
4.3.6 Die Container-Klasse bitset ⊕
Aufgaben 4.3.6
Formulieren Sie die Lösung der Aufgabe 4.3.2.2 so, dass Sie die Lösung mit möglichst wenigen Änderungen auf list
und deque übertragen können.
4.4 Strukturen und Klassen
4.4.1 Mit struct definierte Klassen
Aufgaben 4.4.1
1. Ein einfaches Programm zur Datenverwaltung
Schreiben Sie ein Programm, mit dem man Datensätze des in diesem Abschnitt vorgestellten Datentyps CKontobewegung ein- und ausgeben kann. Zur Verwaltung der Datensätze soll als Container ein vector der Standardbibliothek verwendet werden (siehe Abschnitt 4.2.2)
Dieses Programm soll etwa folgendermaßen aussehen:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
30
4 Strukturierte Datentypen und vordefinierte Klassen
Die Namen der Edit-Fenster sollen den Namen der Elemente der Struktur entsprechen, ebenso die Namen der
Labels. Die Tab-Reihenfolge soll von oben nach unten laufen. Damit der Aufwand für diese Aufgabe nicht zu groß
wird, kann auf Plausibilitätsprüfungen der Eingaben (z.B. mit einer Edit-Maske) verzichtet werden.
a) Schreiben Sie die beiden Funktionen:
CKontobewegung FormToKB()
{ // gibt die Werte des Formulars Form1 zurück
CKontobewegung K;
K.KontoNr = StrToInt(Form1->EKontoNr->Text);
// ... usw.
return K;
}
void KBToForm(CKontobewegung K)
{ // überträgt die Daten von K in das Formular Form1
Form1->EKontoNr->Text=IntToStr(K.KontoNr);
// ... usw.
}
Die erste soll die Daten aus den Edit-Feldern des Formulars als Funktionswert zurückgeben. Die zweite soll die
Daten des Parameters K den Edit-Feldern des Formulars zuweisen. Zur Umwandlung des Datumsstrings in
Ganzzahlwerte können Sie sich an Aufgabe 4.1.1 orientieren.
Damit der Datentyp CKontobewegung auch in anderen Programmen verwendet werden kann, soll er in einer
eigenen Datei definiert werden (z.B. mit dem Namen „KBDecl.cpp“ im Verzeichnis „\CppUtils“). Diese Datei
soll dann in das aktuelle Projekt mit einer include-Anweisung eingebunden werden, z.B.
#include "\CppUtils\KBDecl.cpp"
Entsprechend sollen die Funktionen KBToForm und FormToKB in einer Datei mit dem Namen „KBForms.cpp“
enthalten sein.
b) Beim Anklicken des Buttons mit der Aufschrift „Speichern“ sollen die Daten der Eingabemaske unter
Verwendung der Funktion FormToKB in den Container eingefügt werden.
c) Beim Anklicken des Buttons mit der Aufschrift „<“ soll der Datensatz angezeigt werden, der sich im Container
eine Position vor der des aktuell angezeigten Datensatzes befindet.
d) Beim Anklicken des Buttons mit der Aufschrift „>“ soll der Datensatz angezeigt werden, der im Container auf
den aktuell angezeigten Datensatz folgt.
e) Beim Anklicken der Buttons mit der Aufschrift „<<“ bzw. „>>“ soll der erste bzw. letzte Datensatz des
Containers angezeigt werden.
f) Beim Anklicken des Buttons mit der Aufschrift „Löschen“ soll der aktuell angezeigte Datensatz aus dem
Container gelöscht werden.
g) Aktivieren bzw. deaktivieren Sie die Buttons mit der Aufschrift „<<“, „<“, „>“, „>>“ und „Löschen“ in
Abhängigkeit vom aktuellen Datenbestand im Container. Falls der Container leer ist, sollen alle diese Buttons
deaktiviert werden. Andernfalls:
– Die Buttons << und >> sollen immer aktiviert sein.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.4 Strukturen und Klassen
31
– Falls vor dem aktuell angezeigten Datensatz kein weiterer kommt, soll der Button < deaktiviert sein und
andernfalls aktiviert.
– Falls nach dem aktuell angezeigten Datensatz kein weiterer kommt, soll der Button > deaktiviert sein und
andernfalls aktiviert.
Fassen Sie die Aktivierung und Deaktivierung der Buttons in einer Funktion EnableButtons zusammen. Diese
kann dann nach jedem Anklicken eines der Buttons aufgerufen werden.
h) Nach dem Einfügen oder Löschen eines Datensatzes werden die bisherigen Werte der Iteratoren des Containers
ungültig. Damit der Iterator für die aktuelle Position auch nach einer dieser Operationen einen definierten Wert
hat, soll er auf einen plausiblen Wert gesetzt werden. Beispielsweise wäre die Position des letzten Elements ein
plausibler Wert nach dem Speichern eines Datensatzes. Nach dem Löschen kann der Datensatz nach dem
gelöschten angezeigt werden.
i) Geben Sie die Nummer des aktuell angezeigten Datensatzes auf einem Label an. Sie kann als Differenz des
Iterators für die aktuelle Position und des Iterators für das erste Element bestimmt werden. Diese Differenz ist
dann wie bei einer Subtraktion von Zeigern ein Ganzzahlwert.
2. Ein Datensatz zur Darstellung eines Girokontos soll die Elemente
Adresse, Kontonummer, Kontostand und Kreditlimit
enthalten. Die Adresse soll aus den Elementen
Anrede, Vorname, Nachname, Postleitzahl, Ort, Straße, Hausnummer, Ausland, Vorwahl und Telefonnummer
bestehen. Innerhalb der Adresse sollen die folgenden Felder zusammengefasst werden:
Vor- und Nachname zu Name,
PLZ bis Hausnummer zu Anschrift und
Vorwahl und Telefonnummer zu Telefon.
a) Stellen Sie diese Datenstruktur grafisch dar. Die Darstellung soll die Hierarchie der Elemente wiedergeben. Sie
können sich dabei an der grafischen Darstellung einer Kontobewegung als Tabelle auf Seite 287 orientieren.
b) Entwerfen Sie ein struct, das die Struktur dieses Datensatzes wiedergibt.
Geben Sie an, wie die Elemente einer Variablen g dieses Strukturtyps angesprochen werden können, wenn die
Variable
c) allein unter ihrem Namen angesprochen wird,
d) über einen Zeiger angesprochen wird.
3. Entwerfen Sie einen Datentyp, der eine Zeile der Bundesligatabelle darstellt. Datengruppen, die in der Tabelle durch
Überschriften gekennzeichnet sind, sollen als Ganzes angesprochen werden können.
Verein
1 Hamburger SV
2 Werder Bremen
3 Bayern München
...
Punkte
45:15
45:15
41:19
Tore
68:29
66:34
67:25
Heim
38:10
44:13
43:7
Auswärts
30:19
22:21
24:18
4.4.2 Verkettete Listen
Aufgabe 4.4.2
Schreiben Sie ein Programm, das Strings aus einem Edit-Fenster in eine verkette Liste einhängt:
– Beim Anklicken eines Buttons mit der Aufschrift ListInsert soll der Text aus einem Edit-Fenster in eine verkette
Liste am Ende eingehängt werden.
– Beim Anklicken eines Buttons mit der Aufschrift ShowList sollen die Strings der Liste in einem Memo angezeigt
werden.
– Beim Anklicken eines Buttons mit der Aufschrift ClearMem soll der gesamte von der verketteten Liste belegte
Speicherplatz wieder freigegeben werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
32
4 Strukturierte Datentypen und vordefinierte Klassen
4.4.3 „Memory leaks“ und die Überwachung des Heap ⊕
Aufgabe 4.4.3
Prüfen Sie, ob in der Aufgabe 4.4.2 beim Aufruf von clear tatsächlich der gesamte für die Liste belegte Speicherplatz
wieder freigegeben wird. Verwenden Sie dazu CodeGuard, falls der in Ihrer Version des C++Builders enthalten ist.
Andernfalls können Sie die Funktion bytes_allocated_on_heap verwenden.
4.4.4 Datenstrukturen mit generischen Zeigern ⊕
4.4.5 Mit union definierte Klassen ⊕
4.4.6 Die Datentypen TVarRec und Variant ⊕
4.4.7 Bitfelder ⊕
Aufgabe 4.4.7
Entwerfen Sie eine Datenstruktur, mit der man die Datenfelder m (Mantisse), e (Exponent) und s (Vorzeichen) einer
Gleitkommazahl im Format double
Mantisse m
Exponent e
0 (Positionen der Bits)
51 52
s
62 63
direkt ansprechen kann. Schreiben Sie eine Funktion, die das Bitmuster einer als Parameter übergebenen Zahl ausgibt.
Zum Test können Sie die Zahl 0.1 (siehe Seite 148) verwenden. Geben Sie das Bitmuster der Zahl 1.0 und der Summe
einer zehnfachen Additon von 0.1 aus (siehe auch Seite 155).
4.5 Einige Klassen der VCL
4.5.1 Ein wenig Grafik: TCanvas, TImage und TPrinter ⊕
Aufgaben 4.5.1
1. Mehrseitige Memos ausdrucken
Überarbeiten Sie die im Text vorgestellten Anweisungen zum Ausdrucken eines Memos so, dass
a) am Anfang einer jeden Seite eine Überschriftszeile gedruckt wird, die auch die aktuelle Seitenzahl enthält.
b) vor dem Ausdruck einer Zeile geprüft wird, ob sie noch auf die aktuelle Seite passt. Falls das nicht zutrifft, soll
ein Seitenvorschub ausgelöst werden.
Achten Sie insbesondere darauf, dass eine Druckseite nicht allein aus einer Überschriftszeile bestehen kann.
2. Funktionen zeichnen
Schreiben Sie ein Programm, mit dem man mathematische Funktionen der Form y=f(x) zeichnen kann. Sie können
dazu die im Text vorgestellte Funktion Function1Click übernehmen. Erweitern Sie dieses Programm folgendermaßen:
a) Auf die Zeichenfläche soll ein graues Gitternetz gezeichnet werden (Farbe clGray). Dazu kann man ab dem
linken Bildrand (in Weltkoordinaten: x0) jeweils im Abstand dx Parallelen zur y-Achse zeichnen. Entsprechend
ab y0 im Abstand dy Parallelen zur x-Achse. Schreiben Sie dazu eine geeignete Funktion Gitternetz, der alle
notwendigen Werte als Parameter übergeben werden.
b) Falls die x- bzw. y-Achse in den vorgegebenen Weltkoordinaten enthalten sind, soll durch den Nullpunkt ein
schwarzes Koordinatenkreuz gezeichnet werden. Außerdem sollen die Schnittpunkte der Achsen und der Gitterlinien mit den entsprechenden Werten beschriftet werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.5 Einige Klassen der VCL
33
Falls die x- bzw. y-Achse nicht sichtbar ist, soll diese Beschriftung am Rand erfolgen.
c) Damit die Funktion Gitternetz von a) und b) auch in anderen Programmen verwendet werden kann, soll sie
zusammen mit den im Text vorgestellten Funktionen (x_Welt, y_Welt, x_Bildschirm, y_Bildschirm) in eine
eigene Datei mit dem Namen „GraphUtils.cpp“ aufgenommen werden. Wenn man sie dann im Verzeichnis
„\CppUtils“ speichert, kann man sie mit
#include "\CppUtils\GraphUtils.cpp" // nur ein "\"
von einem beliebigen Programm aus verwenden. Da man in der Funktion Gitternetz die Eigenschaften bzw.
Elementfunktionen des Image braucht, auf das man zeichnet, muss man dieses als Parameter übergeben, z.B.:
void Gitternetz(TImage* Image1, double x0, ...
Beim Aufruf dieser Funktion übergibt man dann das Image des aktuellen Formulars z.B. folgendermaßen als
Argument:
Gitternetz(Form1->Image1,x0,x1,dx, ...
d) Testen Sie dieses Programm mit verschiedenen Funktionen, z.B.
1
2
3
4
5
Funktion
y=sin (x*x)
y=exp(x)
y=x*x
y=1/(1+x*x)
y=x*sin(x)
x0
–4
–1
–2
0
–2
y0
–1
0
–2
0
–6
x1
4
6
2
4
8
y1
1
100
4
1
10
dx
1
1
1
1
1
dy
0.2
10
1
0.2
1
Damit die verschiedenen Funktionen gezeichnet werden können, ohne dass größere Programmänderungen
notwendig sind, soll die entsprechende Funktion über eine globale Variable ausgewählt werden.
3. Der Binomialkoeffizient bin(n,k) ist der k-te Koeffizient von (a+b)n in der üblichen Reihenfolge beim
Ausmultiplizieren:
(a+b)1 = 1*a+1*b, d.h. bin(1,0)=1 und bin(1,1)=1
(a+b)2 = 1*a2+ 2*ab+1*b2, d.h. bin(2,0)=1, bin(2,1)=2 und bin(2,2)=1
(a+b)3 = 1*a3+3*a2b+3*ab2+1*b3, d.h. bin(3,0)=1, bin(3,1)=3 usw.
Für einen Binomialkoeffizienten gilt die folgende Formel:
bin(n,k) = n!/(k!*(n–k)!)
a) Schreiben Sie eine Funktion bin(n,k). Zeigen Sie die Werte von bin(n,k) für k=1 bis n und für n=1 bis 30 in
einem Memo-Fenster an.
b) Wenn man als Zufallsexperiment eine Münze wirft und das Ergebnis „Kopf“ mit 0 und „Zahl“ mit 1 bewertet,
sind die beiden Ergebnisse 0 und 1 möglich.
Wirft man zwei Münzen, sind die Ergebnisse
(0,0), (0,1), (1,0) und (1,1)
möglich. Damit ist die Anzahl der Ereignisse, die zu den Summen S=0, S=1 und S=2 führen, durch
bin(2,0) = 1, bin(2,1) = 2 und bin(2,2) = 1
gegeben. Es lässt sich zeigen, dass diese Beziehung ganz allgemein gilt: Beim n-fachen Werfen einer Münze ist
die Anzahl der Ereignisse, die zu der Summe S=k führt, durch bin(n,k) gegeben (Binomialverteilung).
Stellen Sie die Binomialverteilung durch Histogramme grafisch dar:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
34
4 Strukturierte Datentypen und vordefinierte Klassen
Anscheinend konvergieren diese Rechtecke gegen eine stetige Funktion. Diese Funktion wurde von dem
Mathematiker Gauß berechnet und ist seither unter dem Namen Gauß’sche Glockenkurve oder Normalverteilung
bekannt.
4. Einfache Grafikanimationen mit Funktionsscharen
Eine Schar einfacher Figuren (z.B. Geraden, Rechtecken oder Ellipsen) stellt oft eine optisch ansprechende Grafik
dar.
Solche Kurvenscharen erhält man z.B. dadurch, dass man eine globale Variable (z.B. t) vor jedem Zeichnen einer
solchen Kurve hochzählt und dann eine Kurve zeichnet, deren Lage von diesem Parameter t abhängt. Damit die
Kurven nicht zu schnell nacheinander gezeichnet werden, kann man sie als Reaktion auf einen Timertick zeichnen.
a) Zeichnen Sie einige Scharen von Geraden, Rechtecken und Kreisen. Die folgenden Anfangs- und Endpunkte
sollen nur eine Anregung sein. Setzen Sie Ihrer Phantasie keine Grenzen.
Anfangspunkt
(x+r*sin(t),y+r*cos(t))
(x+r*sin(t),y+r*cos(t))
(rand()%ClientWidth,
rand()%ClientHeight)
Endpunkt
(x+r*sin(2*t),y+r*cos(2+t))
(x+r*sin(2*t),y+r*cos(2*t))
(rand()%ClientWidth,
rand()%ClientHeight)
Dabei ist (x,y) der Mittelpunkt des Bildes und r der Radius, auf dem die Endpunkte liegen (z.B. r= ClientHeight
/2).
b) Verwenden Sie für jede neue Figur eine andere Farbe, indem Sie eine Farbtabelle zyklisch durchlaufen.
c) Wenn man von diesen Figuren immer nur eine bestimmte Anzahl Max (z.B. 10 oder 50) stehen lässt und die am
Anfang gezeichneten wieder löscht, erhält man Animationen wie bei manchen Bildschirmschonern.
Damit die Figuren wieder gelöscht werden können, müssen sie gespeichert werden. Dazu kann man ein
zweidimensionales Array mit MaxF*4 Elementen verwenden. In die MaxF Zeilen dieses Arrays trägt man nacheinander die Koordinaten der Anfangs- und Endpunkte der gezeichneten Figuren ein. Sobald man Max Geraden
gezeichnet hat, löscht man die älteste Gerade, bevor man eine neue zeichnet. Die Koordinaten der neuen
Geraden trägt man dann wieder in die entsprechende Zeile des Arrays ein, damit man sie später wieder löschen
kann. Offensichtlich wird so mit einem Array eine FIFO-Liste realisiert: Der zyklisch laufende Index zeigt dabei
immer auf das Element, das gelöscht und dann wieder neu gezeichnet wird.
Eine Figur kann man dadurch löschen, dass man sie in der Hintergrundfarbe neu zeichnet. Dazu kann man den
Hintergrund zuvor auf eine bestimmte Farbe (z.B. clWhite) setzen, indem man das gesamte Rechteck des Image
auf diese Farbe setzt (Brush und Pen).
Dass diese Aktion beim ersten Anklicken mit undefinierten Geraden arbeitet, ist nicht tragisch, da die in der
Hintergrundfarbe gezeichneten Geraden unsichtbar sind.
5. Das so genannte Räuber-Beute-Modell stellt einen quantitativen Zusammenhang zwischen einer Population von
Räuber- und Beutetieren (z.B. Füchsen und Hasen) dar. Bezeichnet man die Anzahl der Räuber mit r und die der
Beutetiere mit b, geht man von folgenden Voraussetzungen aus:
Die Zuwachsrate der Beutetiere soll über einen Faktor zb von ihrer Anzahl b und über einen Faktor fb von der
Anzahl der möglichen Begegnungen von Räuber- und Beutetieren r*b abhängen; zb entspricht einer
zusammengefassten Geburten- und Sterberate.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.5 Einige Klassen der VCL
35
Die Räuber ernähren sich ausschließlich von den Beutetieren. Damit ist ihre Zuwachsrate nur von ihrer Sterberate sr
und ihrem „Jagderfolg“ abhängig, der mit dem Faktor fr wieder von der Anzahl der möglichen Begegnungen
zwischen Räubern und Beutetieren r*b abhängig sein soll:
r = r_alt – sr*r_alt + fr*r_alt*b_alt;
b = b_alt + zb*b_alt – fb*r_alt*b_alt;
Die beiden Gleichungen sind ein nichtlineares System von Differenzengleichungen, das nach seinen Entdeckern
auch als Lotka-Volterra-System bezeichnet wird. Es lässt sich zeigen, dass dieses System nicht analytisch lösbar
ist.
Schreiben Sie ein Programm, das den Verlauf der Populationen grafisch darstellt. Stellen Sie in dieser Grafik
außerdem das Phasendiagramm mit den Wertepaaren (r,b) im zeitlichen Ablauf dar. Mit den Faktoren
const zb = 0.05;
sr = 0.05;
fb = 0.001;
fr = 2*fb;
erhält man bei einer Skalierung der y-Werte auf den Bereich –10 bis 300:
Bei diesem System können schon relativ geringe Änderungen der Parameter zu einem völlig anderen Verlauf der
Populationen führen.
4.5.2 Die Steuerung von MS-Office: Word-Dokumente erzeugen ⊕
4.5.3 Internet-Komponenten ⊕
Aufgabe 4.5.3
Schreiben Sie ein einfaches Programm mit einer CppWebBrowser-Komponente.
a) Beim Anklicken eines Buttons soll eine bestimmte HTML-Seite geladen werden (z.B. „www.yahoo.com“). Mit
weiteren Buttons sollen die Methoden GoBack und GoForward (siehe dazu die Online-Hilfe) aufgerufen werden,
mit denen man zwischen den zuletzt geladenen Seiten navigieren kann.
b) Als Reaktion auf einen weiteren Button soll eine HTML-Seite (z.B. die Seite „index.html“ von „www.unistuttgart.de“) mit einer NMFTP-Komponente auf die Festplatte kopiert und dann mit der CppWebBrowserKomponente angezeigt werden.
4.5.4 Visuell gestaltete Ausdrucke mit QuickReport ⊕
4.5.5 Kaufmännische Rechnungen: Die Klassen Currency und BCD ⊕
4.5.6 Klassen und Funktionen zu Uhrzeit und Kalenderdatum ⊕
Aufgaben 4.5.6
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
36
4 Strukturierte Datentypen und vordefinierte Klassen
1. Legen Sie eine Datei (z.B. mit dem Namen „TimeUtils.cpp“) an, die alle Funktionen und Deklarationen für die
hochauflösende Zeitmessung enthält. Wenn man diese Datei im Verzeichnis „\CppUtils“ ablegt, kann man sie mit
#include "\CppUtils\TimeUtils.cpp"
in ein Programm einbinden und so Ausführungszeiten messen.
2. Prüfen Sie Genauigkeit von Timer-Ereignissen, indem Sie einen Timer starten, der z.B. jede Sekunde tickt. Geben
Sie bei jedem Tick die mit HRTimeInSec gemessene Zeit seit dem Start des Timers in einem Memo-Fenster aus.
Vergleichen Sie die angezeigten mit den erwarteten Werten.
3. Unter Windows NT erhält man mit der Funktion
BOOL GetProcessTimes(
HANDLE hProcess, // Handle des Prozesses
LPFILETIME lpCreationTime,//Zeitpunkt, als der Prozess erzeugt wurde
LPFILETIME lpExitTime, // Zeitpunkt, als der Prozess beendet wurde
LPFILETIME lpKernelTime, // im „kernel mode“ verbrachte Zeit
// im „user mode“ verbrachte Zeit
LPFILETIME lpUserTime);
in lpUserTime die Zeit (in 100 ns Einheiten), die ein Prozess im so genannten „user mode“ verbracht hat. Diese Zeit
ist im Gegensatz zu der mit QueryPerformanceCounter gemessenen Zeit unabhängig davon, wie viel Zeit Windows
anderen Prozessen zugeteilt hat. Unter Windows 9x zeigt der Funktionswert false an, dass diese Funktion nicht
unterstützt wird.
Das Handle des aktuellen Prozesses (für den Parameter hProcess) erhält man mit
HANDLE h=OpenProcess(
PROCESS_QUERY_INFORMATION, // access flag
true, // handle inheritance flag
GetCurrentProcessId() ); // process identifier
Informieren Sie sich in der Online-Hilfe zum Win32-SDK über diese Funktionen und die zugehörigen
Datenstrukturen (FILETIME usw.). Schreiben Sie eine Funktion ProcessTime_NT, die wie die Funktion
HRTimeInSec die Zeit, die der Prozess im user mode verbracht hat, als double-Wert zurückgibt. Die Umwandlung
von Strukturen des Datentyps FILETIME in double ist über den Datentyp LARGE_INTEGER von Seite 341
möglich, indem man die Elemente LowPart- und HighPart einzeln zuweist. Den LARGE_INTEGER-Wert kann
man dann wie in HRTimeInSec in double konvertieren.
4.5.7 Die Klasse Set ⊕
Aufgabe 4.5.7
Schreiben Sie ein Programm, das ein Fenster anzeigt wie
a) Falls die CheckBox fett markiert wird, soll die Schrift im Label in der Schriftart fett angezeigt werden. Wenn diese
Markierung entfernt wird, soll die Schriftart nicht mehr fett angezeigt werden. Entsprechend für die anderen
CheckBoxen.
b) Beim Ereignis OnKeyDown im Edit-Fenster soll im Memo angezeigt werden, ob die Umschalt-, Alt- oder StrgTasten gedrückt wurde.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.6 Dateien
37
4.6 Dateien
4.6.1 Stream-Variablen, ihre Verbindung mit Dateien und ihr Zustand
4.6.2 Fehler und der Zustand von Stream-Variablen
4.6.3 Lesen und Schreiben von Binärdaten mit read und write
Aufgaben 4.6.3
Damit bei diesen Übungen nicht versehentlich Dateien gelöscht werden, wird empfohlen, zunächst ein Testverzeichnis
(z.B. „c:\test“) anzulegen und in dieses einige Dateien zu kopieren, mit denen die folgenden Funktionen dann getestet
werden können. Im jeweiligen OpenDialog soll dieses Verzeichnis und eine dieser Dateien dann als Voreinstellung
gesetzt werden.
Überprüfen Sie bei jeder Aufgabe nach jeder Dateioperation, ob sie erfolgreich war. Wenn nicht, soll z.B. mit
ShowMessage(" ... ") eine Fehlermeldung ausgegeben werden.
1. Legen Sie ein neues Projekt an, das z.B. den Namen DateiUeb erhalten soll. Nehmen Sie in dieses Projekt die
folgenden Funktionen auf, die nach dem Anklicken eines Buttons aufgerufen werden sollen. Die Namen der Dateien
sollen über einen OpenDialog erfragt werden.
a) Eine Funktion CountBytes soll alle Zeichen einer Datei zählen. Die Dateigröße soll dann in einem MemoFenster angezeigt werden. Vergleichen Sie Ihre Ergebnisse bei Textdateien (z.B. „c:\test\config.sys“) und Binärdateien (z.B. „c:\test\command.com“) mit der Anzeige im Windows-Explorer, wenn Sie die Dateien im Binäroder Textmodus öffnen.
b) Schreiben Sie eine Funktion CopyFile, die eine Datei zeichenweise in eine andere Datei kopiert.
c) Schreiben Sie eine Funktion CompareFiles, die für zwei als Parameter übergebene Dateien prüft, ob sie identisch
sind.
d) Schreiben Sie eine Funktion WriteNTString, die einen nullterminierten String (Datentyp char*) einschließlich
des Nullterminators in einen ofstream schreibt. Sowohl der String als auch der Stream sollen als Parameter
übergeben werden. Eine Funktion ReadNTString soll ab der aktuellen Dateiposition in einem als Parameter übergebenen ifstream alle Zeichen bis zum nächsten Nullterminator '\0' lesen und alle Zeichen in einem AnsiString
als Funktionswert zurückgeben.
e) Auch bei modernen Textverarbeitungsprogrammen kommt es gelegentlich vor, dass ein Text nach einem
Programmabsturz nicht mehr gelesen werden kann. Dann kann es nützlich sein, wenn man den zuletzt
geschriebenen Text zumindest teilweise rekonstruieren kann. Schreiben Sie eine Funktion TextFilter, die alle
Zeichen im Bereich „ “ (Leerzeichen) bis „z“ aus einer Datei in eine andere kopiert.
2. Ein einfaches Programm zur Datenverwaltung mit Dateien
Schreiben Sie ein Programm (z.B. mit dem Namen DatVerw), das wie in Aufgabe 4.4.1.1 eine Eingabemaske für
eine Kontobewegung des Datentyps CKontobewegung enthält. Falls Sie diese Aufgabe gelöst haben, können Sie die
Eingabemaske in dieses Programm kopieren (mit gedrückter linker Maustaste die Maske im Formular markieren,
dann mit Strg+C kopieren und mit Strg+V in das neue Formular einfügen).
In einem Menü Datei sollen die folgenden Menüpunkte angeboten werden:
Neu
Öffnen
Schließen
Zufallsdatei
Das Formular soll Buttons mit der Aufschrift „>“ und „speichern“ erhalten, die beim Start des Programms zunächst
alle deaktiviert sind (Eigenschaft Enabled).
a) Daten in eine Datei schreiben
Nach der Auswahl der Menüoption Datei|Neu soll in einem OpenDialog nach einem Dateinamen gefragt werden,
unter dem dann eine neue Datei angelegt wird. Damit nicht jedes Mal mühsam ein neuer Name eingetippt werden
muss, soll der Name "kb.dat" im Verzeichnis "c:\test" vorgeschlagen werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
38
4 Strukturierte Datentypen und vordefinierte Klassen
Außerdem soll ein Button mit der Aufschrift „Speichern“ aktiviert werden. Sobald er angeklickt wird, sollen die
Daten der Eingabemaske in die Datei geschrieben (am Ende angefügt) werden.
Über die Menüoption Datei|Schließen soll die Datei geschlossen werden. Dabei sollen auch alle Buttons deaktiviert
werden.
b) Daten aus einer Datei lesen
Wie unter a) soll unter Datei|Öffnen eine bestehende Datei geöffnet werden. Wenn die Datei Datensätze enthält, soll
der erste Datensatz angezeigt werden.
Nach dem Öffnen der Datei soll der Button mit der Aufschrift „>“ aktiviert werden. Wenn er angeklickt wird, soll
der jeweils nächste Datensatz angezeigt werden. Nachdem alle Datensätze gelesen wurden (f.eof()=true), soll der
Button „>“ deaktiviert werden.
Überlegen Sie, ob es nicht sinnvoll wäre, die Sperre des Buttons speichern zu entfernen, um so den aktuell
angezeigten Datensatz korrigieren und korrigiert in die Datei schreiben zu können.
c) Testdateien anlegen
Unter Datei|Zufallsdatei soll nach einem OpenDialog eine Datei von Kontobewegungen erzeugt werden. Mit einem
InputQuery-Fenster kann zunächst gefragt werden, wie viele Datensätze in die Datei geschrieben werden sollen.
Schreiben Sie dazu eine Funktion Zufalls_KB, die einen Datensatz des Typs CKontobewegung mit zufälligen, aber
plausiblen Daten als Funktionswert hat. Die einzelnen Werte können z.B. folgendermaßen bestimmt werden:
KontoNr:
Tag:
Monat:
Jahr:
BewArt:
Betrag:
ein zufälliger Wert zwischen 1000 und 1099
ein zufälliger Wert zwischen 1 und 31
ein zufälliger Wert zwischen 1 und 12
ein zufälliger Wert zwischen 2000 und 2001
'+' oder '–'
ein zufälliger Wert zwischen 0,01 und 300
Damit einige Ausdrucke, die in späteren Aufgaben erzeugt werden, einigermaßen realistisch aussehen, soll zu einer
bestimmten Kontonummer immer derselbe Kontoinhaber gehören. Das kann man dadurch erreichen, dass man den
Namen des Kontoinhabers aus einem Array von je 10 Vor- und Nachnamen zusammensetzt, wobei die vorletzte
Ziffer der Kontonummer den Nachnamen und die letzte Ziffer den Vornamen bestimmt.
Damit diese Funktion auch in anderen Programmen verwendet werden kann, soll sie in die Datei „KBDecl.cpp“
aufgenommen werden.
d) Linear suchen
Erweitern Sie das Formular um einen Button LinSuchen. Nach dem Anklicken dieses Buttons soll in der Datei ab
der aktuellen Position nach dem nächsten Datensatz gesucht werden, dessen Namen den String enthält, der für den
Namen des Kontoinhabers in einem Edit-Fenster eingegeben wurde.
Falls ein Datensatz gefunden wird, soll er angezeigt werden. Andernfalls soll mit ShowMessage darauf hingewiesen
werden, dass kein solcher Datensatz gefunden wurde.
Stören Sie sich nicht daran, dass wir mit den bisher vorgestellten Sprachelementen noch keine Möglichkeit haben,
wieder an den Anfang der Datei zurückzukommen. Diese Möglichkeit wird in Aufgabe 4.6.8 e) ergänzt.
4.6.4 Lesen und Schreiben von Daten mit den Operatoren << und >>
Aufgabe 4.6.4:
1. Das HTML-Format ist ein Textformat, das unter anderem für Internetseiten verwendet wird. Damit es auf möglichst
vielen verschiedenen Rechner- und Betriebssystemen eingesetzt werden kann, verwendet es nur Zeichen des ASCIIZeichensatzes. Formatangaben werden auch als Markierungen bezeichnet und bestehen aus einem Paar von spitzen
Klammern <>, zwischen denen Schlüsselworte und eventuell noch Parameter stehen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.6 Dateien
39
Ein HTML-Dokument beginnt mit der Markierung <HTML> und endet mit </HTML>. Wie bei diesem Paar von
Markierungen werden Bereiche oft durch Markierungen begrenzt, bei denen die Markierung für das Ende des
Bereichs mit dem Zeichen „/“ beginnt, und bei der das Schlüsselwort in der Ende-Markierung gleich oder ähnlich
ist wie in der Anfangsmarkierung.
Bereiche können verschachtelt werden. So kann ein HTML-Dokument einen durch <HEAD> und </HEAD>
begrenzten Bereich mit Angaben enthalten, die das gesamte Dokument betreffen. In einem solchen Bereich kann
z.B. zwischen <TITLE> und </TITLE> der Text stehen, der in der Titelzeile des Browsers angezeigt wird.
Der im Hauptfenster des Browsers angezeigte Text ist in einem durch <BODY> und </BODY> begrenzten Bereich
des HTML-Dokuments enthalten.
So wird zum Beispiel das HTML-Dokument
<HTML>
<HEAD>
<TITLE>
Mein HTML Dokument
</TITLE>
</HEAD>
<BODY>
Text in meinem
HTML-Dokument
<BR>Neue Zeile
</BODY>
</HTML>
von Netscape folgendermaßen dargestellt:
Die Einrückungen im HTML-Dokument wirken sich nicht auf die Formatierung aus und wurden hier nur zur
besseren Übersichtlichkeit aufgenommen. Zeilenvorschübe im Text werden ebenfalls ignoriert und nur durch die
Markierung <BR> erzeugt.
Da die Umlaute nicht zum ASCII-Zeichensatz gehören, werden sie durch spezielle Zeichenkombinationen
dargestellt:
ä: &auml; ö: &ouml; ü: &uuml; ß: &szlig;
Ä: &Auml; Ö: &Ouml; Ü: &Uuml;
Beispiel: „In M&uuml;nchen steht ein Hofbr&auml;uhaus.“
a) Schreiben Sie eine Funktion TextToHtml, die aus einer Textdatei ein HTML-Dokument erzeugt. Dazu sollen die
notwendigen Markierungen erzeugt und der gesamte Text der Textdatei in einen durch <BODY> und </BODY> begrenzten Bereich kopiert werden. Die Titelzeile des Browsers soll den Dateinamen anzeigen. Die einzelnen Zeilen
der Textdatei sollen im Browser ebenfalls als einzelne Zeilen dargestellt werden. Die Umlaute sollen durch die entsprechenden Zeichenkombinationen ersetzt werden.
b) Durch die Markierungen <TABLE BORDER> und </TABLE> werden Tabellen in einem HTML-Dokument
begrenzt. In einem solchen Bereich wird
– eine Spaltenüberschrift durch <TH> eingeleitet
– eine neue Tabellenzeile durch <TR> eingeleitet
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
40
4 Strukturierte Datentypen und vordefinierte Klassen
– in einer Tabellenzeile ein neues Datenelement durch <TD> eingeleitet.
Alle diese Markierungen brauchen keine Ende-Markierung, da sie durch die nächste Markierung dieser Art
begrenzt werden.
Schreiben Sie eine Funktion KBToHtml, die aus einer Datei von Kontobewegungen (Datentyp CKontobewegung)
eine HTML-Datei erzeugt, die die Daten der Datei in einer Tabelle darstellt.
2. Schreiben Sie eine Funktion Listendruck, die aus einer Datei von Kontobewegungen (Datentyp CKontobewegung)
eine Textdatei erzeugt, die etwa folgendermaßen aussieht:
Datei: c:\test\kb.dat
Kto. Kontoinhaber
1004 Duestrip, Donald
1099 Prince, Charlie
1011 Mieze, Alexander
Seite 1
Betrag
Datum
31. 9.1997
15. 1.1998
6.11.1997
+
–
–
21.75
168.61
174.06
a) Jede Seite soll mit einem „Blattkopf“ beginnen, der in der ersten Zeile den Dateinamen und die aktuelle Seitenzahl
enthält und in der zweiten Zeile die Bedeutung der darunter aufgeführten Daten erläutert. Darauf soll eine Leerzeile
folgen. Die dazu erforderlichen Anweisungen sollen in einer Funktion Blattkopf zusammengefasst werden.
b) Die Anzahl der Zeilen, die auf eine Seite gedruckt werden können, hängt von der Schriftgröße und vom
Papierformat ab. Nehmen Sie deshalb an, dass auf eine Seite 72 Zeilen gedruckt werden können. Davon sollen
maximal 60 bedruckt werden. Damit diese Funktion ohne großen Aufwand an andere Papierformate angepasst
werden kann, sollen die Anzahl der Druckzeilen pro Seite sowie die Anzahl der Zeilen pro Seite als Variablen
vereinbart werden.
Sobald auf eine Seite mehr Zeilen gedruckt sind, als die Variable Druckzeilen_pro_Seite angibt, soll ein
Blattvorschub (z.B. als eine Folge von Leerzeilen) erfolgen, ebenso nach dem Ausdruck des letzten Datensatzes der
Datei. Der Blattvorschub soll in einer Funktion mit dem Namen Blattvorschub ausgeführt werden.
c) Am Ende jeder Seite soll die Summe der Beträge aller Kontobewegungen mit der Bewegungsart '+' und die aller
Kontobewegungen mit der Bewegungsart '–' gedruckt werden.
d) Die letzte Seite soll (außer wenn eine leere Datei ausgedruckt wird) nicht nur aus einem Blattkopf bestehen können.
Da der Ausdruck in eine Datei erfolgt und nicht auf einen Drucker, wird auf alle Feinheiten der Druckgestaltung
verzichtet.
Diese Funktion kann in dem Programm von Aufgabe 4.6.1.2 unter Datei|Drucken angeboten werden. Den Namen der
auszudruckenden Datei kann man in einem OpenDialog erfragen.
4.6.5 Manipulatoren und Funktionen zur Formatierung von Texten
4.6.6 Textbildschirm-Anwendungen
Aufgaben 4.6.6
Alle Programme, die ihre Meldungen in ein Memo ausgeben, können auch als Textbildschirm-Anwendung realisiert
werden. Formulieren Sie z.B. die Lösungen der Aufgaben von Abschnitt 3.5 als Textbildschirm-Programm.
1.
2.
3.
4.
5.
Aufgabe 3.5.1 (Quersumme)
Aufgabe 3.5.2 (Fibonacci-Zahlen)
Aufgabe 3.5.3 (Primzahlen)
Aufgabe 3.5.4 (Pythagoräische Zahlentripel)
Aufgabe 3.5.5 (3n+1-Problem)
Der Anwender soll in einem Menü auswählen können, welche Lösung er sich anzeigen lassen will.
4.6.7 Stringstreams
Aufgaben 4.6.7
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.6 Dateien
41
Wenn ein String den Wert einer Variablen eines bestimmten Datentyps darstellt, kann man diesen Wert mit einem
istringstream aus dem String lesen und so in einen Wert dieses Datentyps umwandeln. Schreiben Sie die Funktionen
double StrToFloat(string s);
int StrToInt(string s);
die einen als Parameter übergebenen String in einen double- oder einen int-Wert umwandeln. Testen Sie diese
Funktionen mit den Strings
"123"
StrToInt:
StrToFloat: "1.23"
"123 "
"123 "
"12 3"
"1.2 3"
"12A3"
"1.2A3"
" 123A"
" 1.23A"
4.6.8 Dateibearbeitung im Direktzugriff ⊕
Aufgaben 4.6.8
Erweitern Sie das Programm von Aufgabe 4.6.3.2 um folgende Optionen:
a) Die Erweiterungen unter b) bis e) sind unabhängig davon, ob eine neue oder eine bereits bestehende Datei geöffnet
wird. Falls Sie eine neue Datei bisher nur zum Schreiben oder eine bestehende Datei nur zum Lesen geöffnet haben,
passen Sie die Modi beim Öffnen der Datei so an, dass die folgenden Erweiterungen in beiden Fällen funktionieren.
b) Beim Anklicken eines Buttons mit der Aufschrift „<“ soll der Datensatz angezeigt werden, der in der Datei vor dem
aktuell angezeigten kommt. Falls der aktuell angezeigte Datensatz der erste in der Datei ist, soll dieser Button
deaktiviert werden.
c) Beim Anklicken eines Buttons mit der Aufschrift „<<“ soll der erste Datensatz der Datei angezeigt werden.
d) Beim Anklicken eines Buttons mit der Aufschrift „>>“ soll der letzte Datensatz der Datei angezeigt werden.
e) Beim Anklicken eines Buttons mit der Aufschrift „Korrektur“ soll der aktuell angezeigte Datensatz an der Position
in die Datei zurückgeschrieben werden, von der er gelesen wurde.
4.6.9 Sortieren, Mischen und Gruppenverarbeitung ⊕
Aufgaben 4.6.9
1. Dateien sortieren
Schreiben Sie eine Funktion SortiereKBDatei, die eine Datei von Datensätzen des Datentyps CKontobewegung in
einen Container einliest (z.B. in ein Array oder in einen vector der Standardbibliothek), diesen sortiert und die
sortierten Daten dann wieder in eine Datei schreibt. Verwenden Sie zum Sortieren einen für Kontobewegungen
überladenen Operator <, der die Datensätze in Abhängigkeit vom Wert einer Variablen Sortierbegriff
enum TSortierbegriff {sbKontoNr, sbName, sbDatum,
sbKontonrUndDatum} Sortierbegriff=sbKontoNr;
folgendermaßen sortiert:
a)
b)
c)
d)
sbKontoNr: nach der Kontonummer
sbName: nach dem Namen des Kontoinhabers
sbDatum: nach dem Datum
sbKontonrUndDatum: nach der Kontonummer, und wenn diese gleich ist, nach dem Datum.
2. Mischen mit Folgeprüfung
Wenn man die Funktion Mischen auf Dateien anwendet, die nicht sortiert sind, wird die gemischte Datei auch nicht
sortiert sein. Erweitern Sie diese Funktion deswegen so, dass die Sortierfolge der Mischdatei beim Schreiben eines
neuen Satzes dadurch geprüft wird, dass dieser neue Satz mit dem zuletzt in die Mischdatei geschriebenen Satz
verglichen wird.
Da beim Schreiben des ersten Satzes kein zuletzt in die Mischdatei geschriebener Satz zum Vergleich auf die
Sortierfolge zur Verfügung steht, soll in diesem Fall eine Prüfung der Sortierfolge unterbleiben. Die Anzahl der Sortierfehler soll mitgezählt und am Schluss am Bildschirm ausgegeben werden.
3. Gruppenwechsel
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
42
4 Strukturierte Datentypen und vordefinierte Klassen
Schreiben Sie eine Funktion GW1, die eine nach der Kontonummer sortierte Datei von Kontobewegungen wie im
Beispielausdruck zum Gruppenwechsel der Stufe 1 ausdruckt. Sie soll die folgenden Anforderungen erfüllen:
1. Jede Seite soll mit einem Blattkopf beginnen (wie in Listenausdruck).
2. Eine Seite bietet Platz für 72 Druckzeilen, von denen nicht mehr als 60 mit Kontobewegungen bedruckt werden
sollen. Ab der 60sten Zeile sollen nur Summenzeilen gedruckt werden.
3. Auf einer neuen Seite dürfen unmittelbar nach dem Blattkopf keine Summenzeilen gedruckt werden.
4. Aufeinander folgende Kontobewegungen mit derselben Kontonummer sollen als Gruppe betrachtet werden. Am
Anfang einer Gruppe sollen die Kontonummer und der Name des Kontoinhabers in einer eigenen Zeile gedruckt
werden.
5. Alle Datensätze in derselben Gruppe sollen ohne die Kontonummer gedruckt werden, außer wenn es der erste
Satz auf einer neuen Seite ist. In diesem Fall soll auch die Kontonummer gedruckt werden.
6. Am Ende einer Gruppe soll der Saldo der Beträge dieser Gruppe ausgedruckt werden.
7. Nach der letzten Kontobewegung soll der Gesamtsaldo aller Kontobewegungen ausgedruckt werden.
Damit (wie in 5. gefordert) die Daten einer Kontobewegung sowohl mit als auch ohne die Kontonummer
ausgedruckt werden können, kann die zu verarbeitende Kontobewegung einer Variablen zugewiesen werden, die
zum Drucken aufbereitet wird. In diesem „Drucksatz“ wird z.B. die Kontonummer auf 0 gesetzt, wenn sie nicht
gedruckt werden soll. Der Drucksatz wird dann durch eine Funktion ausgedruckt, die z.B. Drucke_Zeile heißt. In
dieser Funktion wird die Kontonummer des Drucksatzes nur gedruckt, wenn sie von 0 verschieden ist.
4.6.10 C-Funktionen zur Dateibearbeitung ⊕
4.7 Assoziative Container
4.7.1 Die Container set und multiset
4.7.2 Die Container map und multimap
4.7.3 Iteratoren der assoziativen Container
Aufgaben 4.7
1. Ein Informationssystem soll zu einem eindeutigen Schlüsselbegriff eine zugehörige Information finden, z.B. zu
einer Artikelnummer den zugehörigen Preis.
Schreiben Sie als einfachen Prototyp für ein solches System eine Funktion
bool ValueToKey(KeyType Key,ValueType& Value)
Ihr Funktionswert soll true sein, wenn zum Argument für Key ein passender Wert gefunden wurde. Der gefundene
Wert soll dann als Argument für Value zurückgegeben werden. Falls kein passender Wert gefunden wird, soll der
Funktionswert false sein. Verwenden Sie dazu einen geeigneten Container.
Testen Sie diese Funktion. Damit man leicht sieht, ob der gesuchte Begriff auch tatsächlich gefunden wurde, sollen
der Schlüsselbegriff und die Daten identisch sein. Am einfachsten wählt man 1000 bzw. 1000 000 aufeinander folgende Werte des Datentyps int. Um welchen Faktor dauert die Suche in einem Container mit 1000 000 Elementen
etwa länger als die in einem Container mit 1000 Elementen?
2. Beim wiederholten Aufruf eines Zufallszahlengenerators wie rand oder random kann es vorkommen, dass sich die
erzeugten Zufallszahlen wiederholen. Für manche Anwendungen braucht man allerdings Zufallszahlen, die sich
nicht wiederholen.
Schreiben Sie eine Funktion NewRand, die bei jedem Aufruf eine neue Zufallszahl liefert. Die bisher erzeugten
Zufallszahlen sollen in einem geeigneten Container abgelegt werden.
3. Schreiben Sie eine Rechtschreibprüfung. Dabei soll zuerst ein Wörterbuch aus einer Textdatei erstellt werden,
indem diese Datei zeilenweise eingelesen und alle Wörter daraus mit einer Funktion wie parseString (siehe Aufgabe
4.3.2.3) bestimmt werden. Diese Wörter sollen dann in einem geeigneten Container abgelegt werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.7 Assoziative Container
43
Anschließend soll die zu prüfende Datei als Textdatei zeilenweise eingelesen werden. Auch hier sollen die einzelnen
Wörter mit einer Funktion wie parseString bestimmt werden. Für jedes Wort soll dann geprüft werden, ob es in dem
Container mit den Wörtern aus dem Wörterbuch enthalten ist.
Testen Sie diese Funktion und insbesondere auch ihr Zeitverhalten, indem Sie aus einer relativ großen Textdatei
eine Kopie erstellen und in dieser Datei dann einzelne Wörter verändern. Verwenden Sie die ursprüngliche Datei als
Wörterbuch.
4. Mit dem assoziativen Container multimap kann man leicht eine Konkordanzliste aus einem Text erstellen. Eine
solche Liste ist ein alphabetisch geordnetes Verzeichnis aller Wörter aus einem Text, die zu jedem Wort die
Nummer einer jeden Seite bzw. Zeile enthält, in der es vorkommt. Wenn man z.B. jedes Wort aus dem folgenden
Text zusammen mit seiner Zeilennummer als Paar in einen solchen Container einträgt
"Alle meine Entchen"
"schwimmen in dem See,"
"schwimmen in dem See,"
und dann alle diese Worte zusammen mit den zugehörigen Nummern ausgibt, erhält man diese Konkordanzliste:
Alle 1
Entchen 1
See 2 3
dem 2 3
in 2 3
meine 1
schwimmen 2
3
Eine Konkordanzliste aus dem Quelltext eines Programms bezeichnet man auch als Cross-Reference-Liste. Mit
einer solchen Liste kann man feststellen, in welchen Zeilen eines Programms welche Namen (Variablen usw.) verwendet werden.
Schreiben Sie eine Funktion MakeXRef, die jeden String aus einem vector mit Strings mit der Funktion parseString
(siehe Aufgabe 4.3.2.3) in Worte zerlegt und jedes solche Wort zusammen mit seiner Zeilennummer in eine
geeignete Variable des Typs multimap ablegt. Eine Funktion PrintXRef soll jedes Wort aus dem mit MakeXRef
angelegten multimap ausgeben sowie zu jedem solchen Wort die zugehörigen Zeilennummern.
Testen Sie diese Funktionen mit den Strings von oben. Eine zweite Variante der Funktion MakeXRef soll alle
Zeilen einer Textdatei einlesen und zerlegen.
5. Mit multimaps in einer Schlüsseltabelle suchen und eine Datei sortieren
Wenn man als Schlüsselwerte eines Containers der Klasse multimap die Schlüsselwerte von Datensätzen einer Datei
nimmt und als zugehörige Werte die Dateiposition des jeweiligen Datensatzes, kann man den Datensatz zu einem
Schlüsselwert in einer Datei schnell finden.
a) Schreiben Sie eine Funktion MMReadKeys, die aus einer Datei von Kontobewegungen die Kontonummer und
die Position eines Datensatzes in einen Container des Datentyps multimap einliest.
b) Eine Funktion MMFind soll in diesem Multimap-Container nach einer Kontobewegung mit einer bestimmten
Kontonummer suchen.
c) Wenn man die Datensätze einer Datei im Direktzugriff lesen und schreiben kann ist es zwar technisch möglich,
diese so zu vertauschen, dass sie anschließend sortiert sind. Da jedoch die Lese- und Schreibzugriffe relativ zeitaufwendig sind, kann man auf diese Weise keine optimalen Ausführungszeiten für das Sortieren einer Datei
erwarten.
Schreibt man die Datensätze dagegen in der Reihenfolge in eine neue Datei, die sich aus dem in a) erzeugten
Multimap-Container ergibt, geht das schneller. Die Dateiposition zu einem Datensatz mit einem Schlüsselwert
ergibt sich dann aus dem zweiten Wert eines Wertepaares.
Schreiben Sie eine Funktion MMSort, die auf diese Weise aus einer nicht sortierten Datei von Kontobewegungen
eine nach der Kontonummer sortierte Datei erzeugt.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
44
4 Strukturierte Datentypen und vordefinierte Klassen
4.8 Die numerischen Klassen der Standardbibliothek
4.8.1 Komplexe Zahlen ⊕
Aufgaben 4.8.1
1. Die quadratische Gleichung
ax2+bx+c=0
hat die beiden Lösungen
x = (–b ±sqrt(b 2 – 4ac))/2a
Schreiben Sie ein Programm, das die Lösung dieser Gleichung zu den komplexen Koeffizienten a=2+3i, b=4–5i und
c=–7+8i ausgibt. Außer der Lösung soll auch noch die Probe angezeigt werden.
2. Die Gleichung vom Grad n (n >= 1, ganzzahlig)
xn–1=0
hat n komplexe Lösungen, die mit
w=cos(2π/n) + i*sin(2π/n)
durch x0=1, x1=w, x2=w2, ..., xn–1=wn–1 gegeben sind. Geben Sie (z.B. für n=10) für jede dieser Einheitswurzeln xin
in einem Memo aus.
3. Reelle Funktionen der Form y=f(x) (x und f(x) reelle Zahlen) können deshalb auf die übliche Weise grafisch
dargestellt werden, weil jedes Element (x,f(x)) der Funktion als zweidimensionaler Punkt dargestellt werden kann.
Bei komplexen Funktionen w=f(z) (w und f(w) komplexe Zahlen) ist das nicht möglich, weil sowohl z als auch f(z)
zwei Dimensionen für ihre Darstellung benötigen, so dass für die Darstellung von w=f(z) vier Dimensionen notwendig sind.
Um trotzdem einen gewissen Eindruck vom grafischen Verlauf einer komplexen Funktion f(z) zu bekommen, kann
man die reelle Funktion abs(f(z)) für bestimmte Real- bzw. Imaginärteile zeichnen.
Überarbeiten Sie eine Kopie der Lösung von Aufgabe 4.5.1.2 so, dass die Funktion abs(sin(z)) grafisch dargestellt
wird. Der Imaginärteil von z soll über einen Schieberegler im Bereich von –10 bis +10 eingestellt werden können.
Bei jeder Änderung des Imaginärteils soll die Funktion neu gezeichnet werden.
4.8.2 Valarrays und Slices ⊕
Aufgabe 4.8.2
Bei manchen Experimenten (Physik, Psychologie usw.) besteht ein linearer Zusammenhang der Art
y = a*x + b
zwischen einer unabhängigen Variablen x und einer abhängigen Variablen y. Allerdings sind die Werte von a und b oft
nicht bekannt. Man versucht sie deswegen zu schätzen, indem man das Experiment mit n verschiedenen Werten von x0,
x1, ..., xn–1 wiederholt und dabei die Werte y0, y1, ..., yn–1 für y ermittelt. Falls die Messwerte für y durch Störungen
und/oder Messfehler verfälscht werden, kann man jedoch nicht erwarten, dass die Punkte (x0, y0), (x1, y1), ..., (xn–1, yn–
1) alle auf einer Geraden liegen.
Zur Lösung dieses Problems hat der Mathematiker Gauß vorgeschlagen, die Werte für a und b so zu bestimmen, dass
das Quadrat der Abweichungen
F(a,b)= (y0 – (ax0+b))2 + (y1 – (ax1+b))2 + ... + (yn–1 – (axn–1+b))2
möglichst klein wird (Methode der kleinsten Quadrate). Die so ermittelte Gerade wird auch als Regressionsgerade
bezeichnet. Mit
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
4.8 Die numerischen Klassen der Standardbibliothek
45
sxy = x0y0 + x1y1 + ... + xn–1yn–1
sx = x0 + x1 + ... + xn–1
sy = y0 + y1 + ... + yn–1
sxx = x0x0 + x1x1 + ... + xn–1xn–1
xM = sx/n
yM = sy/n
führt dieser Ansatz auf die folgenden Werte für a und b:
a = (n*sxy – sx*sy)/(n*sxx – sx*sx)
b = yM – a*xM
1. Formulieren Sie diese Gleichungen
a) mit Valarrays und den zugehörigen Funktionen (sum usw.)
b) mit Schleifen und dem Indexoperator wie mit normalen Arrays.
2. Testen Sie die Lösungen für a und b mit n (2, 100, 10 000) Messwerten, die
a) auf einer Geraden y = ax + b liegen. Die berechneten Werte müssen dann die ursprünglichen Werte für a und b
ergeben.
b) um kleine Zufallswerte von der Geraden y = ax + b abweichen. Die berechneten Werte müssen dann in der Nähe
der Werte für a und b liegen.
3. Stellen Sie die Messwerte und die berechnete Gerade in einem TImage grafisch dar. Sie können dazu die GrafikFunktionen von Abschnitt 4.5.1 verwenden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5 Anweisungen und Ausdrücke
5.1 Die Ausdrucksanweisung
5.2 Ausdrücke
5.2.1 Primäre Ausdrücke ⊕
5.2.2 Postfix-Ausdrücke ⊕
5.2.3 Unäre Ausdrücke ⊕
5.2.4 Typkonversionen in Typecast-Schreibweise ⊕
5.2.5 Zeiger auf Klassenelemente ⊕
5.2.6 Multiplikative Operatoren ⊕
5.2.7 Additive Operatoren ⊕
5.2.8 Shift-Operatoren ⊕
5.2.9 Vergleichsoperatoren ⊕
5.2.10 Gleichheitsoperatoren ⊕
5.2.11 Bitweise Operatoren ⊕
5.2.12 Logische Operatoren ⊕
5.2.13 Der Bedingungsoperator ⊕
5.2.14 Konstante Ausdrücke ⊕
5.2.15 Zuweisungsoperatoren
5.2.16 Der Komma-Operator ⊕
5.2.17 L-Werte und R-Werte ⊕
5.2.18 Die Priorität und Assoziativität der Operatoren ⊕
5.2.19 Alternative Zeichenfolgen ⊕
Aufgaben 5.2.19
1. Welchen Datentyp und Wert haben die Ausdrücke rechts vom Gleichheitszeichen nach der Zeile „//Aufgaben“?
Welchen Wert erhält dabei die linke Seite?
int i=0;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.2 Ausdrücke
47
void Aufg1()
{
int i=2,j=i,k=-6, m=8, n=10000, x=0, h[5];
unsigned u=40000u,v=u;
// Aufgaben
int a= k/i/j+n/j;
int b= k/i/j++ + m/j++;
int c= i*4%-k*3;
int d= k/-m-2;
double e= m*n/u/v;
int f= ++::i%++i;
int g = sizeof 17-2;
while(x<=5) h[x]=x++;
}
Diese Aufgabe hat ihren Zweck erfüllt, wenn Sie derartige Ausdrücke in Ihren Programmen möglichst vermeiden.
2. a) Welche Werte werden durch folgende Anweisungen ausgegeben:
int w=1;
for (int i=0; i<8*sizeof(w); i++)
{
Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(w));
w = w<<1;
}
b) Formulieren Sie die for-Schleife um, so dass man mit einer einzigen Anweisung im Schleifenkörper dasselbe
Ergebnis wie in a) erhält.
3. Vergleichen Sie die Bedingung in a) mit der in b) und die in c) mit der in d). Alle Operanden sollen einen Ganzzahldatentyp haben. Geben Sie durch Klammern die Priorität und Assoziativität der Ausdrücke an:
a)
b)
c)
d)
if
if
if
if
(a<=b && c<=d) ...
(a<=b & c<=d) ...
(i&1) ...
(i&1==0) ...
4. Beschreiben Sie das Ergebnis der Anweisungen in a) bis c) und den Datentyp des Ausdrucks mit dem
Bedingungsoperator.
double x = 3.14;
int a=1,b=2;
a) int s = a?b:x;
b) int s = (x > 0) ? 1 : (x < 0) ? -1 : 0;
c) for (int n=0;n<4;n++)
{
char s[100];
sprintf(s,"%d file%c found",n,(n==1)?' ':'s');
Form1->Memo1->Lines->Add(s);
}
d) Überarbeiten Sie die sprintf-Anweisung in c) so, dass der Text in deutscher Sprache ausgegeben wird.
5. Ein C++-Compiler fasst die aufeinander folgenden Zeichen eines Programms so zu Bezeichnern, Schlüsselworten,
Literalen und Operatoren zusammen, dass möglichst lange Sequenzen von solchen Zeichenfolgen entstehen.
Überprüfen Sie, welche der Ausdrücke in den Zuweisungen an die Variable k so interpretiert werden können.
int i,j,k;
i=3;j=1;k=2;
i=3;j=1;k=2;
i=3;j=1;k=2;
i=3;j=1;k=2;
i=3;j=1;k=2;
k=
k=
k=
k=
k=
i+++j;
++i+j++;
-i+++j;
+i+-j;
i--+--j;
Stellen Sie die Priorität der syntaktisch korrekten Ausdrücke durch Klammern dar und geben Sie nach jeder Zeile
den Wert der Variablen i, j und k an.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
48
5 Anweisungen und Ausdrücke
Auch diese Aufgabe hat ihren Zweck erfüllt, wenn Sie solche Ausdrücke in Ihren Programmen möglichst
vermeiden.
6. Im Header <winnt.h> sind unter anderem die folgenden Konstanten definiert:
#define
#define
#define
#define
#define
...
FILE_ATTRIBUTE_READONLY
FILE_ATTRIBUTE_HIDDEN
FILE_ATTRIBUTE_SYSTEM
FILE_ATTRIBUTE_DIRECTORY
FILE_ATTRIBUTE_ARCHIVE
0x00000001
0x00000002
0x00000004
0x00000010
0x00000020
Diese können in den Windows-API Funktionen
BOOL SetFileAttributes(
LPCTSTR lpFileName,
// address of filename
DWORD dwFileAttributes); // address of attributes to set
DWORD GetFileAttributes(
LPCTSTR lpFileName); // address of the name of a file or directory
dazu verwendet werden, die Attribute einer Datei zu setzen bzw. abzufragen. Schreiben Sie mit diesen Funktionen
und Konstanten die folgenden Funktionen, denen jeweils der Name einer Datei als Parameter übergeben werden
soll. Sie können das Ergebnis dieser Funktionen mit „Eigenschaften“ im Windows-Explorer überprüfen. Legen Sie
dazu eigene Dateien in einem Verzeichnis „test“ an.
a) Eine Funktion SetToROH soll die Attribute der Datei auf ReadOnly und Hidden setzen.
b) Eine Funktion isSystemFile soll genau dann denn Wert true zurückgeben wenn die Datei das Attribut System
hat.
c) Eine Funktion SetFileAttributeToHidden soll das Attribut der Datei auf Hidden setzen, ohne dass die anderen
Attribute verändert werden.
d) Die Funktion RemoveFileAttributeHidden soll das Attribut Hidden der Datei löschen, ohne dass die anderen
Attribute verändert werden.
5.2.20 Explizite Typkonversionen ⊕
5.3 Ein wenig Programmierlogik: Symbolische Ausführung
Aufgaben 5.3.4
Überprüfen Sie, ob sich aus den Vorbedingungen in 1. bis 5. die angegebenen Beziehungen herleiten lassen.
1. n ganzzahlig und ungerade, p, x, u und v Ganzzahl- oder Gleitkommadatentypen
// p0*x0n = uv
p = p*x;
n = n/2;
x = x*x;
// p0*x0n = uv
2. n ganzzahlig und gerade, p, x, u und v Ganzzahl- oder Gleitkommadatentypen
// p0*x0n = uv
n = n/2;
x = x*x;
// p0*x0n = uv
3. i ganzzahlig, s Gleitkomma- oder Ganzzahldatentyp
// s = 1 + 2 + ... + i, d.h. s ist die Summe der
// ersten i Zahlen
i = i + 1;
s = s + i;
// s = 1 + 2 + ... + i
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.4 Die Deklarationsanweisung
49
4. i ganzzahlig, s Gleitkomma- oder Ganzzahldatentyp
// s = 1*1 + 2*2 + ... + i*i, d.h. s ist die Summe
// der ersten i Quadratzahlen
i = i + 1;
s = s + i*i;
// s = 1*1 + 2*2 + ... + i*i
5. i ganzzahlig, s Gleitkomma- oder Ganzzahldatentyp
// s = 1*1 + 2*2 + ... + i*i
s = s + i*i;
i = i + 1;
// s = 1*1 + 2*2 + ... + i*i
5.4 Die Deklarationsanweisung
5.5 Die Verbundanweisung und die Blockstruktur von C++
Aufgaben 5.5
1. Mit dieser Aufgabe soll lediglich das Konzept der Lokalität geübt werden. Der hier verwendete Programmierstil
wird nicht zur Nachahmung empfohlen.
a) Geben Sie in der folgenden Funktion nach jeder Anweisung den Datentyp und den Wert aller bisher deklarierten
Variablen an.
void verschachtelt()
{
int i,j,k=7;
AnsiString s,t;
{
char j;
{
float i=0,j=i;
{
AnsiString i,j,k;
i = "ckt"; j = "ra"; k = "vert";
s = s+k+j+i+" ";
}
i = 1; j = 2;
t = FloatToStr(i)+" + "+FloatToStr(j)+
" = "+FloatToStr(k);
{
AnsiString t,j,k,i=s;
{
char t,j,k;
t = 's'; j = 'i'; k = 't';
s = AnsiString(' ')+j+t+k+ ' ';
}
{
char t,j,k;
t = 'a'; j = 'D'; k = 's';
s = AnsiString(j) + t + k+s;
}
t = "nz sch"; j = "ja ga"; k = "ön ";
s = s+j+t+k+i;
}
}
}
Form1->Memo1->Lines->Add(t);
Form1->Memo1->Lines->Add(s);
}
b) Welcher Text wird beim Aufruf dieser Funktion ausgegeben?
2. Welche der globalen Variablen können lokal definiert werden?
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
50
5 Anweisungen und Ausdrücke
AnsiString c,a,b;
void vertausche()
{
c = a;
a = b;
b = c;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
a = 17;
b = 18;
vertausche();
}
5.6 Lebensdauer und Speicherklassenspezifizierer
Aufgabe 5.6
Falls eine Funktion nur für relativ wenige verschiedene Ganzzahlargumente definiert ist und oft aufgerufen wird, kann
man eventuell ein wenig Zeit sparen, wenn man alle ihre Funktionswerte beim ersten Aufruf der Funktion berechnet
und in einem statischen Array ablegt. Bei jedem weiteren Aufruf der Funktion wird dann der Wert aus dem Array
zurückgegeben.
Realisieren Sie diese Vorgehensweise mit den ersten 50 Fibonacci-Zahlen (siehe Aufgabe 3.5.3).
5.7 Auswahlanweisungen
5.7.1 Die if-Anweisung
Aufgaben 5.7.1
1. Welche der folgenden if-Anweisungen sind nach den Definitionen
double x,Punkte;
int i,j;
bool b;
syntaktisch und inhaltlich korrekt?
a)
b)
c)
d)
if (x=17) Edit1->Text = "Volltreffer";
if (i>=1 && i<=10) Edit1->Text = "Volltreffer";
if b && (i=j*x) Edit1->Text = "Volltreffer";
if (Punkte >= 0) Edit1->Text = "Extremes Pech";
else if (Punkte >= 20) Edit1->Text ="Ziemliches Pech";
else if (Punkte >= 40) Edit1->Text =
"Ein wenig Glück gehabt";
Für welche Werte von Punkte werden die entsprechenden Meldungen ausgegeben?
2. In Abhängigkeit vom Wert einer Ganzzahlvariablen Note soll eine Funktion den folgenden Text als Funktionswert
zurückgeben:
1
2
3
4
5,6
sehr gut!!!
gut
na ja
schwach
durchgefallen
Für jeden anderen Wert von Note soll der Text „Undefinierte Note “ mit dem Wert der Variablen Note
zurückgegeben werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.7 Auswahlanweisungen
51
3. In Abhängigkeit vom Wert der beiden Variablen Lagergruppe und Materialgruppe (beide Datentyp char) sollen
gemäß der folgenden Tabelle die Werte der Variablen LA_Summe, LB_Summe usw. um den Wert der Variablen
Summe erhöht werden:
Lagergruppe
'A'
'A'
'A'
'B'
'B'
Materialgruppe
'A'
'B'
'X'
'B'
'D'
Verarbeitung
LA_Summe und MA_Summe um Summe erhöhen
LA_Summe und MB_Summe um Summe erhöhen
LA_Summe und MX_Summe um Summe erhöhen
LB_Summe und MB_Summe um Summe erhöhen
LB_Summe und MD_Summe um Summe erhöhen
Falls Lagergruppe den Wert 'A' hat, aber Materialgruppe nicht einen der Werte 'A', 'B' oder 'X', soll die Meldung
„Unzulässige Materialgruppe in Lager A“
ausgegeben werden; entsprechend für Lagergruppe = 'B'.
Falls Lagergruppe weder den Wert 'A' noch den Wert 'B' hat, soll die Meldung
„Unzulässige Lagergruppe“
erfolgen. In jedem dieser unzulässigen Fälle soll keine Summation durchgeführt werden.
4. Datumsvergleich
Die Ganzzahlvariablen t1, m1 und j1 sowie t2, m2 und j2 sollen zwei Kalenderdaten bezeichnen (z.B. t1=17, m1=5,
j1=1997). Eine boolesche Variable vorher soll den Wert true erhalten, wenn das Datum (t1, m1, j1) zeitlich vor dem
Datum (t2, m2, j2) liegt, und andernfalls den Wert false.
Diese Aufgabe wurde schon in Aufgabe 3.4.7.3 allein mit booleschen Variablen behandelt. Sie kann aber auch mit
if-Anweisungen bearbeitet werden, was oft als einfacher angesehen wird.
Falls die Jahreszahlen in j1 und j2 verschieden sind, gibt der boolesche Ausdruck
j1 < j2
an, ob das erste Datum zeitlich vor dem zweiten liegt:
if (j1 != j2) vorher = (j1 < j2)
Wenn dagegen die Jahreszahlen gleich und die Monate verschieden sind, entscheiden die Monate über die zeitliche
Anordnung der beiden Kalenderdaten. Sind sowohl die Jahre als auch die Monate gleich, entscheidet der Tag.
Wie muss die Lösung geändert werden, wenn eine boolesche Variable vorher_oder_gleich genau dann den Wert
true erhalten soll, wenn das erste Datum vor dem zweiten liegt oder gleich dem zweiten ist?
5. Steuerformel
In § 32 des Einkommensteuergesetzes (EStG 1998) ist festgelegt, wie sich die Einkommensteuer aus dem zu
versteuernden Einkommen berechnet:
§32 a Einkommensteuertarif
(1) Die tarifliche Einkommensteuer bemisst sich nach dem zu versteuernden Einkommen. Sie beträgt vorbehaltlich
der §§ 32b, 34, 34b und 34c jeweils in Deutsche Mark für zu versteuernde Einkommen
1. bis 12365 Deutsche Mark (Grundfreibetrag): 0;
2. von 12366 Deutsche Mark bis 58643 Deutsche Mark:
(91,19*y + 2590)*y
3. von 58644 Deutsche Mark bis 120041 Deutsche Mark:
(151,91*z + 3434)*z + 13938
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
52
5 Anweisungen und Ausdrücke
4. von 120 042 Deutsche Mark an: 0,53*x – 22 843;
„y“ ist ein Zehntausendstel des 12312 Deutsche Mark übersteigenden Teils des abgerundeten zu versteuernden
Einkommens. „z“ ist ein Zehntausendstel des 58590 Deutsche Mark übersteigenden Teils des abgerundeten zu versteuernden Einkommens. „x“ ist das abgerundete zu versteuernde Einkommen.
(2) Das zu versteuernde Einkommen ist auf den nächsten durch 54 ohne Rest teilbaren vollen
Deutsche-Mark-Betrag abzurunden, wenn es nicht bereits durch 54 ohne Rest teilbar ist.
(3) Die zur Berechnung der tariflichen Einkommensteuer erforderlichen Rechenschritte sind in der Reihenfolge
auszuführen, die sich nach dem Horner-Schema ergibt. Dabei sind die sich aus den Multiplikationen ergebenden
Zwischenergebnisse für jeden weiteren Rechenschritt mit drei Dezimalstellen anzusetzen; die nachfolgenden
Dezimalstellen sind wegzulassen. Der sich ergebende Steuerbetrag ist auf den nächsten vollen Deutsche-MarkBetrag abzurunden.
a) Schreiben Sie eine Funktion ESt, die zu dem als Parameter übergebenen zu versteuernden Einkommen die
Einkommensteuer als Funktionswert zurückgibt. Der Wert 151,91 unter 3. ist ein Druckfehler im Gesetzestext.
Verwenden Sie stattdessen den richtigen Wert 151,96.
Welche Datentypen sind hier angemessen?
Zum Testen können Sie den folgenden Auszug aus der Steuertabelle verwenden:
x
10 800
16 200
21 600
27 000
32 400
37 800
43 200
Est
0
1 020
2 484
4 000
5 570
7 193
8 870
x
48 600
54 000
59 400
64 800
70 200
75 600
81 000
Est
10 599
12 381
14 217
16 129
18 129
20 218
22 396
x
86 400
91 800
97 200
102 600
108 000
113 400
118 800
Est
24 663
27 018
29 461
31 994
34 615
37 324
40 123
b) In § 32, Absatz 5 des EStG ist festgelegt, wie die Einkommensteuer nach dem Splitting-Verfahren berechnet wird:
(5) Für Ehegatten, die nach den §§ 26, 26b zusammen zur Einkommensteuer veranlagt werden, beträgt die
tarifliche Einkommensteuer vorbehaltlich der §§ 32b, 34 und 34b das Zweifache des Steuerbetrags, der sich für
die Hälfte Ihres gemeinsam zu versteuernden Einkommens nach den Absätzen (1) bis (3) ergibt (SplittingVerfahren).
Die Funktion Splitting soll als Funktionswert die nach dem Splitting-Verfahren berechnete Einkommensteuer
zurückgeben. Verwenden Sie zur Lösung dieser Aufgabe die Lösung von a).
5.7.2 Die switch-Anweisung
Aufgaben 5.7.2
Lösen Sie die Aufgaben aus Abschnitt 5.7.1 mit switch- anstelle von if-Anweisungen. Falls eine der Aufgaben nicht
lösbar ist, geben Sie den Grund dafür an.
1. Aufgabe 4 (Material- und Lagergruppe)
2. Aufgabe 5 (Datumsvergleich)
3. Aufgabe 6 (Steuerformel)
5.7.3 Ein wenig Programmierlogik für Auswahlanweisungen
Aufgaben 5.7.3
1. Überprüfen Sie mit Wahrheitstafeln, ob die folgenden Formeln richtig sind:
a) p && (q || r) <==> (p && q) || (p && r)
b) p || (q && r) <==> (p || q) && (p || r)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.8 Wiederholungsanweisungen
53
c) (p && q) || r <==> p && (q || r)
2. Geben Sie Bedingungen in Form von einfach verknüpften Bedingungen an, die bei der Ausführung von S2
vorausgesetzt werden können.
a) if ((x < 0) || (x > 100)) S1;
else S2;
b) if ((x != 1) && (c == 'j')) S1;
else S2;
c) if(!((i < 1) && (i > 10))) S1;
else S2;
3. Unter welchen Bedingungen werden S1, S2, S3 und S4 ausgeführt:
if (1 <= x && x <= 10) S1;
else if (5 <= x && x <= 15) S2;
else if (x > 12) S3;
else S4;
4. Begründen oder widerlegen Sie, dass die Variable max nach der Ausführung eines jeden Zweigs der if-Anweisung
den maximalen Wert von x, y und z hat:
if ((x >= y) && (x >= z)) max = x;
else if ((y >= x) && (y >= z)) max = y;
else max = z;
5. Überprüfen Sie, ob unter der Vorbedingung n>0 die Beziehung
p*xn = uv
auch noch nach der Ausführung von
if (n&1) p = p*x;
n = n/2;
x = x*x;
gilt, wenn sie vor der Ausführung dieser Anweisungen erfüllt war.
6. Begründen oder widerlegen Sie, dass die Funktion median immer den mittleren der drei als Parameter übergebenen
Werte liefert:
T median (T a, T b, T c) // T ein Datentyp, z.B. int
{
if (a < b)
if (b < c)
return b;
else if (a < c)
return c;
else return a;
else if (a < c)
return a;
else if (b < c)
return c;
else return b;
}
5.8 Wiederholungsanweisungen
5.8.1 Die while-Anweisung
5.8.2 Die do-Anweisung
5.8.3 Die for-Anweisung
Aufgaben 5.8.3
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
54
5 Anweisungen und Ausdrücke
1. Formulieren Sie die Funktion Zinsen aus Abschnitt 5.8.1 mit einer do-Schleife.
2. Ein Hypothekendarlehen über einen Betrag von k DM mit einem Zinssatz von p% und einer Tilgung von t% sei
als Annuitätendarlehen vereinbart. Dabei hat der Schuldner jedes Jahr am Jahresende eine gleich bleibende Rate
von (p+t)% zu leisten.
Von dieser konstanten Rate entfallen p% der Restschuld auf die Zinsen. Der Rest ist dann die Tilgung, die wegen
der im Laufe der Jahre abnehmenden Restschuld jährlich größer wird.
Schreiben Sie eine Funktion, die die Restschuld, Zinsen und Tilgungsraten für jedes Jahr der Laufzeit des
Hypothekendarlehens ausgibt. Die Zinsen und die Tilgungsraten sollen jeweils am Jahresende fällig werden.
3. Der Schleifenkörper der for-Schleife
for (T d=0;d<=1;d+=0.1) // T ein Gleitkommadatentyp
wird mit dem Datentyp float 10 Mal und mit double 11 Mal wiederholt.
a) Ersetzen Sie diese for-Schleife durch eine for-Schleife, die durch Ganzzahlausdrücke kontrolliert wird und die
genau die 11 Werte 0, 0.1, 0.2, ..., 0.9 und 1 ausgibt.
b) Ersetzen Sie die for-Schleife mit den Gleitkommawerten ug, og und d
for (double f=ug;f<=og;f+=d) // <= !!
durch eine for-Schleife, die durch Ganzzahlausdrücke kontrolliert wird und die den Schleifenkörper mit den
Werten ug, ug+d, ug+2d, ..., og-d, og durchläuft.
5.8.4 Endlosschleifen, Abbruchbedingungen und Windows
Aufgaben 5.8.4
1. Unter welchen Bedingungen werden die folgenden Schleifen nicht zu Endlosschleifen (Datentyp von i und n: int, d:
double):
a) while (n != 0)
b) while (i > 0)
{
{
.
n = n+2;
.
.
i = i/2;
.
}
}
c) while (i < n)
{
.
i++;
.
d)
d = 0;
do d = d + 0.1;
...
while (d != 10);
}
e) while (i > 0) n = 2*n;
// keine weiteren Zuweisungen an i, r und n
f) Die Schleife von b), wenn man i > 0 durch i >= 0 ersetzt?
2. Geben Sie möglichst strenge Bedingungen an, die nach dem Verlassen der folgenden Anweisungen gelten:
a) while ((n > 0) && (c != 'q'))
{
n-–;
...
// keine weiteren Veränderungen von n
};
b) n = 10;
while ((n > 0) && (c != 'q'))
{
n-–;
...
// keine weiteren Veränderungen von n
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.8 Wiederholungsanweisungen
55
3. Die bekannten Fraktalbilder der Mandelbrot-Menge (nach dem Mathematiker Benoit Mandelbrot) entstehen
dadurch, dass man mit den Koordinaten eines Bildpunktes (x,y) nacheinander immer wieder folgende Berechnungen durchführt:
x = x2 – y2 + x
y = 2xy + y
Dabei zählt man mit, wie viele Iterationen notwendig sind, bis entweder x2 + y2 > 4 gilt oder bis eine vorgegebene
maximale Anzahl von Iterationen (z.B. 50) erreicht ist.
In Abhängigkeit von der Anzahl i dieser Iterationen färbt man dann die Koordinaten desjenigen Bildpunktes ein,
mit dem man die Iteration begonnen hat: Falls die vorgegebene maximale Anzahl von Iterationen erreicht wird,
erhält dieser üblicherweise die Farbe Schwarz (clBlack). In allen anderen Fällen erhält der Bildpunkt einen von i
abhängigen Farbwert..
Färbt man so alle Bildpunkte von x0 = –2; y0 = 1.25; (links oben) bis x1 = 0.5; y1 = –1.25; (rechts unten) ein, erhält
man das „Apfelmännchen“.
a) Schreiben Sie eine Funktion ZeichneFraktal, die ein solches Fraktal in ein Image (aus der Komponentenpalette,
Seite Zusätzlich, Datentyp TImage) zeichnet. Diese Funktion soll nach dem Anklicken des Buttons „Zeichne“
aufgerufen werden. Sie können dazu folgendermaßen vorgehen:
Ein Image hat links oben die Koordinaten (0,0) und rechts unten die Koordinaten (Width, Height).
Transformieren Sie jeden Bildpunkt (px,py) des Image in die Koordinaten des Rechtecks mit den Eckpunkten
(x0,y0) {links oben} und (x1,y1) {rechts unten}. Sie können dazu die Funktionen x_Welt und y_Welt aus
„\CppUtils\GraphUtils.cpp“ (siehe Abschnitt 4.5.1) verwenden:
double x = x_Welt(px,x0,x1,Image->Width);
double y = y_Welt(py,y1,y0,Image->Height);
Führen Sie dann mit jedem so erhaltenen Punkt (x,y) die oben beschriebenen Berechnungen durch. Als Farbwert
zu der so bestimmten Anzahl i von Iterationen wählt man im einfachsten Fall mit PALETTEINDEX einen
Farbwert aus der Standardpalette der Windows-Farben:
color = PALETTEINDEX(1 + i%16);
Das Pixel zur Koordinate (px,py) des Image setzt man dann mit der Eigenschaft Pixels auf diese Farbe:
Image1->Canvas->Pixels[px][py] = color;
b) Falls
für
den
Bildschirm
mehr
als
256
Farben
kann
man
mit
dem
Makro
platz|Systemsteuerung|Anzeige|Einstellungen),
eingestellt
sind
(Arbeits-
COLORREF RGB(BYTE bRed, // red component of color
BYTE bGreen, // green component of color
BYTE bBlue); // blue component of color
auf einfache Weise wesentlich mehr Farben als mit PALETTEINDEX erzeugen. Es setzt die als Parameter
übergebenen Farbanteile zu einem Farbwert zusammen. Erzeugt man z.B. mit den folgenden Anweisungen einen
Farbwert zur Anzahl i der Iterationen, erhält man farblich wesentlich differenziertere Bilder als mit
PALETTEINDEX:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
56
5 Anweisungen und Ausdrücke
i=i%256; // maximal 255 Farben
int r30=i%30;
// i->[0..29]
int t=255-5*r30; // [0..29]->[255..145]
if (i<30)
return RGB(255-8*i,255,255);
else if (i<60) return RGB(0,255-8*r30,255);
else if (i<90) return RGB(0,0,t);
else if (i<120) return RGB(0,t,0);
else if (i<150) return RGB(t,0,0);
else if (i<180) return RGB(t,t,0);
else if (i<210) return RGB(t,0,t);
else if (i<240) return RGB(0,t,t);
else
return RGB(t,t,t); // Graustufen
Hier werden z.B. Werte von i zwischen 60 und 90 auf verschiedene Blautöne abgebildet. Dabei werden Farben
vermieden, die nahe bei schwarz liegen. Diese Anweisungen sind aber nur als Anregung gedacht. Mit anderen
Farbzuordnungen kann man interessante Variationen erhalten.
c) Bei größeren Fenstern kann der Zeichnen eines Fraktals leicht einige Minuten dauern. Deshalb soll das Zeichnen
des Bildes durch das Anklicken eines Buttons mit der Aufschrift „Abbruch“ abgebrochen werden können.
Damit das Anklicken dieses Buttons in der Schleife erkannt wird, muss die Funktion Application>ProcessMessages() aufgerufen werden. Da auch dieser Aufruf Zeit benötigt, sollte sie nur in der äußeren der
beiden Schleifen aufgerufen werden, damit das Programm nicht zu langsam wird. Durch diesen Aufruf sieht
man dann insbesondere auch, wie das Bild aufgebaut wird.
d) Ausschnitte eines Fraktalbildes kann man folgendermaßen vergrößern („Zoom in“): Man verwendet die
aktuellen Mauskoordinaten beim Drücken bzw. Loslassen der Maustaste (Ereignisse OnMouseDown bzw.
OnMouseUp) als Eckpunkte (links oben bzw. rechts unten) des neuen Bildes. Diese Koordinaten werden dann
mit denselben Formeln wie unter a) in die neuen Weltkoordinaten (x0,y0) und (x1,y1) umgerechnet. Nach dem
Anklicken des Buttons „Zeichne“ wird das Fraktalbild dann mit den neuen Werten (x0,y0) und (x1,y1)
gezeichnet.
e) Bei den Fraktalbildern der Julia-Menge (nach dem französischen Mathematiker Gaston Julia) verwendet man
anstelle der am Anfang dieser Aufgabe angegebenen Formeln die folgenden:
x = x2 – y2 + jx
y = 2xy + jy
Der einzige Unterschied zu den Formeln für die Mandelbrot-Fraktale ist der, dass jx und jy konstante Werte sind
(z.B. jx=-0.745 und jy=0.1125).
Erweitern Sie das Programm so, dass in Abhängigkeit von einer globalen Variablen Mandelbrot- oder JuliaFraktale gezeichnet werden.
f) In je einer Listbox sollen Parameter für verschiedene Mandelbrot- und Julia-Fraktale zur Auswahl angeboten
werden. Beim Anklicken eines Eintrags der Listbox sollen dann die entsprechenden Parameter für die Eckpunkte
übernommen werden. Dazu kann man eine Struktur wie
struct TMandelbrotParam {
char* Name;
double x0; double y0;
double x1; double y1;
};
definieren und dann ein Array mit solchen Strukturen mit den entsprechenden Parametern initialisieren:
const int nMBAW=13;
TMandelbrotParam MBPar[nMBAW]=
{
{"Apfelmännchen", -2,
1.25,
0.5, -1.25},
{"Seepferdchen",
-0.88, 0.18, -0.70, 0.0},
// ...
{"Spirale",
-0.750, 0.105, -0.740, 0.095}
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.8 Wiederholungsanweisungen
57
5.8.5 Ein wenig Programmierlogik für Schleifen
Aufgaben 5.8.5
1. Erstellen Sie für die folgenden Anweisungen mit den angegebenen Anfangswerten Ablaufprotokolle.
a) n = 4
f = 1;
for (int i=2; i<=n; i++)
f = f*i;
b) u = 3, v = 5
x = u;
y = v;
z = 0;
while (x != 0)
{
if (x&1) z = z + y;
y = y * 2;
x = x/2;
}
c) wie b), mit u = 0, v = 5
d) u = 3, v = 2
p = 1;
n = v;
x = u;
while (n > 0)
{
if (n&1) p = p * x;
n = n/2;
x = x*x;
}
2. Bestimmen Sie eine Invariante für die Schleife in der Funktion Potenz und weisen Sie deren Ergebnis unter der
Vorbedingung n>=0 nach. Sie können dazu die Ergebnisse der Aufgabe 5.7.3.5 verwenden.
double Potenz(double x, int n)
{
double p = 1;
while (n > 0)
{
if (n&1) p = p*x;
n = n/2;
x = x*x;
}
return p;
}
3. Für zwei ganze Zahlen a und b teilt a die Zahl b, falls es eine Zahl k gibt, so dass a*k = b ist.
Der größte gemeinsame Teiler ggT von zwei Zahlen a, b ist der größte Teiler, der sowohl a als auch b teilt.
Beispiele:
ggT(4,30) = 2, ggT(2,7) = 1, ggT(0,5) = 5
Aus dieser Definition der Teilbarkeit ergibt sich unmittelbar:
1. ggT(a,b) = ggT(b,a)
2. ggT(a,–b) = ggT(a,b)
3. ggT(a,0) = a
Wegen 2. kann man sich ohne Beschränkung der Allgemeinheit auf a > 0 und b >= 0 beschränken. Aus
a = (a/b) * b + a%b
folgt
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
58
5 Anweisungen und Ausdrücke
a – (a/b) * b = a%b
Damit ist jeder gemeinsame Teiler t von a und b auch ein Teiler von b und a%b und umgekehrt, da
t*ka=a und t*kb=b und t*ka – (a /b)*t*ka = a %b
d.h.
ggT(a,b) = ggT(b,a % b)
Dabei ist a%b kleiner als b.
Beweisen Sie mit einer geeigneten Invariante, dass die Funktion ggT den größten gemeinsamen Teiler der
Argumente als Funktionswert hat.
int ggt(int x, int y)
{ // x=x0,y=y0
while (y != 0)
{
int r = x%y;
x = y;
y = r;
}
return x; // ggT(x0,y0)==x;
}
4. Beim Hornerschema wird der Funktionswert eines Polynoms
h = p[n]*xn + p[n–1]*xn–1 + ... + p[1]*x + p[0]
dadurch berechnet, dass die Klammern in
h = (...((p[n]*x + p[n–1])*x + p[n–2]) ... + p[1])*x + p[0]
von innen nach außen ausmultipliziert werden. Dabei werden nur n+1 Multiplikationen benötigt. Mit der Funktion
Potenz wären es (n+2)*(n+1)/2.
double Horner(double x, int n, double p[])
{ // p: Array mit den Koeffizienten des Polynoms
double s = 0; // n: Der Grad des Polynoms.
int i = 1;
while (i <= n+1)
{
s = s*x + p[n–i+1];
i++;
}
return s;
}
a) Beweisen Sie mit Invarianten, dass dieser Algorithmus für ein Polynom mit den Koeffizienten p[n], p[n–1], ...,
p[0] den Funktionswert berechnet:
s = p[n]*xn + p[n–1]*xn–1 + ... + p[1]*x + p[0]
b) Erweitern Sie diesen Algorithmus so, dass in einer weiteren Variablen die Ableitung des Polynoms berechnet
wird.
5. Übertragen Sie das in diesem Abschnitt verifizierte Multiplikationsverfahren auf die Multiplikation von zwei
positiven ganzen Zahlen, deren Ziffern in einem String dargestellt werden. Zur Addition solcher Zahlen kann die
Lösung von Aufgabe 4.1.6 verwendet werden. Testen Sie diese Funktion mit einer Funktion, die die Fakultät berechnet. Dabei ergibt sich z.B.
45!=119622220865480194561963161495657715064383733760000000000
Sie können die Fakultäten der Stringzahlen auch mit denen von double-Werten vergleichen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.9 Die Sprunganweisungen goto, break und continue
59
5.9 Die Sprunganweisungen goto, break und continue
Aufgabe: Hier gibt es (außer nichts zu üben) nichts zu üben.
5.10 Exception-Handling
5.10.1 Die try-Anweisung
5.10.2 Exception-Handler und Exceptions der Standardbibliothek
5.10.3 Vordefinierte Exceptions der VCL
5.10.4 Der Programmablauf bei Exceptions
5.10.5 Das vordefinierte Exception-Handling der VCL
5.10.6 throw-Ausdrücke und selbst definierte Exceptions
5.10.7 Fehler, Exceptions und die Korrektheit von Programmen
5.10.8 Die Freigabe von Ressourcen bei Exceptions
Aufgaben 5.10.8
1. Geben Sie an, welche Anweisungen beim Aufruf der Funktion f
void f()
{
try { try {
f1();
f2();
}
catch(...)
{
f3();
}
f4();
}
catch (...)
{
f5();
}
}
ausgeführt werden, wenn
a)
b)
c)
d)
e)
in keiner der Funktionen f1, f2 usw. eine Exception ausgelöst wird
in f1 eine Exception ausgelöst wird
in f2 eine Exception ausgelöst wird
in f1 und f3 eine Exception ausgelöst wird
in f1 und f4 eine Exception ausgelöst wird.
2. Definieren Sie eine Funktion mit einem int-Parameter, die in Abhängigkeit vom Wert des Arguments eine
Exception des Datentyps
a) int
b) char
c) double
d) char*
e) exception
f) logic_error
auslöst und zeigen sie den jeweils übergebenen Wert bzw. den der Funktion what in einem Exception-Handler an.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
60
5 Anweisungen und Ausdrücke
3. Die Funktion f soll in Abhängigkeit vom Wert des Arguments eine Exception der Datentypen exception,
logic_error, range_error oder out_of_range auslösen. Welche Ausgabe erzeugt ein Aufruf der Funktion in a)? Von
welcher Klasse wird die Funktion what in b) bis d) aufgerufen?
a) void g1(int i)
{
try { f(i); }
catch(logic_error& e)
{ Form1->Memo1->Lines->Add("logisch"); }
catch(out_of_range& e)
{ Form1->Memo1->Lines->Add("range"); }
catch(exception& e)
{ Form1->Memo1->Lines->Add("exception"); }
};
b) void g2(int i)
{
try { f(i); }
catch(logic_error& e)
{ Form1->Memo1->Lines->Add(e.what()); }
catch(out_of_range& e)
{ Form1->Memo1->Lines->Add(e.what()); }
catch(exception& e)
{ Form1->Memo1->Lines->Add(e.what()); }
};
c) void g3(int i)
{
try { f(i); }
catch(exception e)
{ Form1->Memo1->Lines->Add(e.what()); }
};
d) void g4(int i)
{
try { f(i); }
catch(exception& e)
{ Form1->Memo1->Lines->Add(e.what()); }
catch(...)
{ Form1->Memo1->Lines->Add("irgendeine Exception"); }
};
4. Erweitern Sie die Funktionen StrToFloat und StrToInt von Aufgabe 4.6.7 so, dass eine Exception ausgelöst wird,
wenn beim Lesen des Strings ein Fehler auftritt. Testen Sie, welche der folgenden Argumente von StrToInt und
StrToFloat
"123",
" 123 ",
"1 23",
"12A3", " 123A",
" "
eine Exception auslösen, wenn die Elementfunktion exceptions mit den folgenden Argumenten aufgerufen wurde:
a)
b)
c)
d)
0
ios::failbit
ios::eofbit
ios::badbit
5. Definieren Sie eine von der Klasse exception abgeleitete Klasse MeineException, deren Elementfunktion what einen
im Konstruktor angegebenen Text zurückgibt. Lösen Sie in einer Funktion eine Exception dieser Klasse aus und
zeigen Sie den Text an.
6. In der Standardbibliothek von C++ wird den Konstruktoren der Exception-Klassen ein string übergeben. Spricht
etwas dagegen, anstelle eines Strings des Datentyps string einen Zeiger auf einen nullterminierten String zu übergeben?
7. Beurteilen Sie die Funktion Sqrt:
class ENegative {};
double Sqrt(double d)
{
try {
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
5.10 Exception-Handling
61
if (d<0) throw ENegative();
return sqrt(d);
}
catch(...)
{
ShowMessage("negativ");
return 0;
}
}
8. Eine Eingabemaske wie
soll verschiedene Edit-Fenster mit Strings enthalten. Bei der Übernahme der Daten aus dieser Maske sollen die
Strings mit Funktionen wie IntToStr, FloatToStr und DateToStr in Ganzzahl-, Gleitkomma- und Datumswerte
umgewandelt werden. Diese Funktionen sollen eine Exception auslösen, wenn die Umwandlung nicht möglich ist.
Entwerfen Sie eine Funktion getData, die die Daten aus der Eingabemaske übernimmt und in die entsprechenden
Datentypen konvertiert. Diese Funktion soll von einer Funktion saveData aufgerufen werden können, die die
konvertierten Daten z.B. in einem Container speichert. Falls die Konvertierung nicht möglich ist, soll der Anwender
durch eine Meldung darauf hingewiesen werden.
a) In einer ersten Variante der Funktion getData soll der Anwender nur darauf hingewiesen werden, dass eine der
Umwandlungen nicht möglich war.
b) In einer zweiten Variante der Funktion getData soll der Anwender gezielt auf die Umwandlung hingewiesen
werden, die nicht möglich war.
9. Bei einem Aufruf der Funktion f soll gelegentlich die Gefahr bestehen, dass eine Exception ausgelöst wird. Stellen
Sie sicher, dass der von new reservierte Speicher auch wieder freigegeben wird.
void h()
{
vector<int*> v;
int* p=new int(17);
v.push_back(p);
f();//falls hier eine Exception auftritt, Speicherleck
for (vector<int*>::iterator i=v.begin(); i!=v.end();
i++)
delete i;
}
5.10.9 Exceptions in <math.h> und die Funktion _matherr ⊕
5.10.10 Die Klasse auto_ptr ⊕
5.10.11 Exception-Spezifikationen
5.10.12 Die Funktion terminate ⊕
5.10.13 Das Win32-Exception-Handling mit try-__except ⊕
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6 Funktionen
6.1 Die Definition und der Aufruf von Funktionen
6.2 Die Verwaltung von Funktionsaufrufen über den Stack
6.3 Funktionen mit Parametern
6.3.1 Werteparameter
6.3.2 „Call by reference“ mit Referenzparametern
6.3.3 Zeiger als Parameter
6.3.4 Konstante Parameter
6.3.5 Seiteneffekte und die Reihenfolge von Auswertungen
Aufgaben 6.3.5
1. Die folgenden Anweisungen wurden ohne Fehler übersetzt. Nach dem Aufruf der Funktion f hat die Variable a
allerdings immer noch denselben Wert wie vorher. Wieso?
int a=0;
void Init1()
{
a=1; // globale Variable
}
void f()
{
Init1;
}
2. Die folgende Funktion soll einen Zeiger auf das erste Zeichen von s nach dem Komma als Funktionswert
zurückgeben. Allerdings erhält man meist nicht das erwartete Ergebnis. Wieso?
char* Nachkommastellen(char *s)
{
char w[200];
strcpy(w, s);
return strstr(w, ",")+1;
}
3. Stellen Sie sich vor, Sie arbeiten an einem größeren Projekt und haben eine Funktion geschrieben, die häufig
aufgerufen wird. Zunächst sind Sie davon ausgegangen, dass diese Funktion die ihr übergebenen Parameter nicht
verändert. Nachdem das Projekt schon weit fortgeschritten ist, stellen Sie fest, dass es doch notwendig ist, einen
Parameter zu verändern. Vergleichen Sie den Aufwand für diese Programmänderung, wenn Sie
a) diesen Parameter als Referenzparameter definieren,
b) wie in C einen Zeiger auf die zu verändernde Variable übergeben.
4. Ein als Parameter übergebener Zeiger soll in einer Funktion verändert werden. Schreiben Sie eine solche Funktion
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6.3 Funktionen mit Parametern
63
a) mit einem Referenzparameter
b) mit einem Zeiger, wie man das auch in C machen muss.
Welche Funktion ist Ihrer Meinung nach einfacher?
5. Schreiben Sie eine Funktion zero, die den von einer Variablen eines beliebigen Datentyps belegten Speicherplatz
mit Nullen belegt.
6. Bestimmen Sie für die Funktionen
int f(int& x)
{
x++;
return x;
}
int g(int& x)
{
x=2*x;
return x;
}
das Ergebnis der Ausdrücke
x = 0;
int y=f(x)+g(x);
x = 0;
y=g(x)+f(x);
a) wenn x den Datentyp int hat.
b) wenn x den Datentyp double hat.
c) wenn x den Datentyp char hat.
6.3.6 Default-Argumente
6.3.7 Der Datentyp einer Funktion
6.3.8 Zeiger auf Funktionen
Aufgaben 6.3.8
1. Durch eine Funktion InitPunkt soll eine Variable des Datentyps
struct C2DPunkt {
int x, y;
};
initialisiert werden. Falls diese Funktion ohne Parameter aufgerufen wird, sollen beide Koordinaten auf 0 gesetzt
werden. Bei einem Aufruf mit einem Parameter soll x den Wert des Parameters erhalten und y den Wert 0. Beim
Aufruf mit zwei Parametern soll x den Wert des ersten und y den Wert des zweiten Parameters erhalten.
2. In je einem Array sollen Funktionen dargestellt werden, die denselben Datentyp haben wie die Funktionen
i)
ii)
iii)
iv)
void f1(void){};
int f2(int,int){};
double f3(double){};
char* f4(char*){};
a) Definieren Sie für jeden dieser Funktionstypen ein solches Array mit 10 Elementen. Verwenden Sie dazu
a1) mit typedef deklarierte Namen für die Funktionstypen.
a2) keine mit typedef deklarierte Namen für die Funktionstypen.
b) Weisen Sie die 4 Funktionen f1, f2 f3 und f4 jeweils dem ersten Element der unter a) und b) definierten Arrays
zu und rufen Sie diese Funktionen über die Arrayelemente auf. Überprüfen Sie mit dem Debugger, ob jedes Mal
die richtige Funktion aufgerufen wird.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
64
6 Funktionen
c) Geben Sie die Namen der Datentypen der Funktionen f1, f2, f3 und f4 mit typeid in einem Memo-Fenster aus.
Sie erhalten diese Namen über die Elementfunktion name von typeid, wenn Sie die Header-Datei <typeinfo> mit
einer #include-Anweisung in das Programm eingebunden haben.
3. Beim Newton-Verfahren zur Bestimmung einer Nullstelle der Funktion f:R → R ersetzt man einen Näherungswert
x0 für die Lösung durch die Nullstelle der Tangente im Punkt (x0,f(x0)):
y = f'(x0)(x–x0) + f(x0) = 0
Damit wird x0 durch den folgenden Wert ersetzt:
x = x0 – f(x0)/f'(x0)
Diese Schritte werden so lange wiederholt, bis man einen genügend guten Näherungswert für die Lösung hat oder
bis eine maximale Anzahl von Iterationen durchgeführt ist. Wenn das Newton-Verfahren konvergiert, verdoppelt
sich ab einer genügend guten Näherung die Anzahl der richtigen Stellen bei jeder Iteration.
Implementieren Sie das Newton-Verfahren unter Verwendung von Funktionszeigern in den folgenden zwei
Varianten. Der erste Näherungswert soll als Parameter übergeben werden.
a) Die Ableitung wird durch einen Näherungswert ersetzt.
b) Die Ableitung wird ebenfalls als Funktion übergeben.
c) Testen Sie die beiden Verfahren z.B. mit der Funktion f(x) = x2 – r und vergleichen Sie die Ergebnisse mit der
vordefinierten Funktion sqrt(r). Als erster Näherungswert kann hier der Wert r verwendet werden.
Die Iterationen sollen abgebrochen werden, wenn sich zwei aufeinanderfolgende Werte um weniger als das z.B.
100fache der minimal darstellbaren Differenz zu 1 unterscheiden. Dieser Wert ist in der Standardbibliothek von
C++ definiert
std::numeric_limits<double>::epsilon();
und steht nach den folgenden Anweisungen zur Verfügung:
#include <limits>
using namespace std;
4. Um einen Näherungswert für das Integral einer Funktion f:R→ R im Intervall von a bis b zu finden, kann man
folgendermaßen vorgehen:
– Man ersetzt die Funktion f durch eine Gerade durch die Punkte (a,f(a)) und (b,f(b)) und berechnet die Fläche des
so erhaltenen Trapezes (Trapezregel).
Zur Erhöhung der Genauigkeit kann man das Intervall [a,b] in n–1 Teilintervalle [a,a+h], [a+h,a+2h] ... usw.
mit h = (b–a)/n zerlegen. Summiert man die Trapezflächen der Teilintervalle auf, erhält man die Trapezsumme
(siehe auch Aufgabe 3.7.6):
Tn = h*[f(a)/2 + f(a+h) + ... + f(b–h) + f(b)/2]
– Man ersetzt die Funktion f durch ein quadratisches Polynom durch die Punkte (a,f(a)), ((a+b)/2,f((a+b)/2) und
(b,f(b)) (Simpson-Regel). Zerlegt man das Intervall wie bei der Trapezregel in Teilintervalle und summiert man
diese Flächen auf, erhält man die Summe
Sn = h*[f(a)+4f(a+h)+2f(a+2h)+4f(a+3h)+...+2f(b–2h)+4f(b–h)+f(b)]/3
a) Schreiben Sie die Funktionen Trapezsumme und Simpsonsumme. Dabei sollen a, b, n und f als Parameter
übergeben werden.
b) Da man vorab oft kaum entscheiden kann, wie gut die so berechneten Näherungswerte sind, erhöht man n
sukzessive (z.B. durch Verdoppeln), bis sich zwei aufeinander folgende Näherungswerte um weniger als eine
vorgegebene Schranke unterscheiden.
Schreiben Sie eine Funktion iterate, die diese Iterationen für die Funktionen Trapezsumme und Simpsonsumme
durchführen. Beide sollen als Parameter übergeben werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6.3 Funktionen mit Parametern
65
c) Testen Sie Ihr Programm, indem Sie die so erhaltenen Näherungswerte mit den exakten Werten vergleichen, die
man über die Stammfunktion erhält (z.B. für ein quadratisches Polynom).
5. Schreiben Sie eine Funktion PlotFunction, die eine als Parameter übergebene Funktion des Typs „double (*)
(double)“ in ein Image (Datentyp TImage, Seite Zusätzlich in der Komponentenpalette) zeichnet. Sie können sich
dazu an den Ausführungen in Abschnitt 4.5.1 und Aufgabe 4.5.1.2 orientieren.
a) Die Zeichenfläche soll zunächst mit einem weißen Rechteck „gelöscht“ werden.
b) Der Funktion PlotFunction soll die linke und rechte Grenze des Bereichs, in dem sie gezeichnet werden soll, als
Parameter übergeben werden. Der minimale und maximale Funktionswert soll in PlotFunktion berechnet
werden. Die zu zeichnende Funktion soll das Image von unten bis oben ausfüllen.
c) In das Image soll außerdem ein Gitternetz mit z.B. 10 Gitterlinien in x- und y-Richtung gezeichnet werden.
Dazu soll die Funktion Gitternetz aus „\CppUtils\GraphUtils.cpp“ verwendet werden.
d) Bieten Sie in einer Listbox verschiedene Funktionen (z.B. sin, cos usw.) zum Zeichnen an. Sie sollen durch
Anklicken in der Listbox zum Zeichnen ausgewählt werden.
6. In Aufgabe 5.8.4.3 (Fraktalbilder) wurden die Iterationen mit den Formeln
x = x2 – y2 + x
y = 2xy + y
bzw.
x = x2 – y2 + jx
y = 2xy + jy
berechnet. Mit der komplexwertigen Funktion
f(z) = z2 // z = x+iy, in C++: z=Complex(x,y)
lassen sich diese Iterationen auch beschreiben durch
z = f(z) + z
bzw.
z = f(z) + Complex(jx,jy)
Hier wurde die folgende Definition verwendet:
#include <complex>
typedef complex<double> Complex;
a) Überarbeiten Sie die Funktion ZeichneFraktal aus Aufgabe 5.8.4.3 zu einer Funktion ZeichneFraktalComplex.
Sie soll eine Funktion des Typs
Complex f(const Complex&);
als Parameter haben, mit der dann die Iterationen durchgeführt werden. Wenn man ZeichneFraktalComplex mit
dem Argument
Complex CFQuadr(const Complex& z)
{
return z*z;
}
aufruft, müssen sich dieselben Fraktale wie mit ZeichneFraktal ergeben. Interessante Fraktale erhält man aber
auch mit anderen Funktionen wie z.B.
f(z) = z3
f(z) = z2 – 0.5z
f(z) = z3 + 0.3x
b) Mit dem in Aufgabe 3 vorgestellten Newton-Verfahren kann man auch die Nullstellen von komplexen
Funktionen wie in a) bestimmen. Die Anweisungen sind dieselben. Der einzige Unterschied ist, dass mit
komplexen und nicht mit reellen Zahlen gerechnet wird.
Beim Newton-Verfahren stellt sich die Frage, für welche Anfangswerte es gegen eine Lösung konvergiert. Die
Antwort lässt sich veranschaulichen, indem man für „alle“ Werte aus einem rechteckigen Ausschnitt der komplexen Ebene überprüft, ob das Verfahren mit diesem Startwert konvergiert. Wie bei den Fraktalbildern färbt
man dann den zum Startwert gehörigen Bildpunkt entsprechend ein:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
66
6 Funktionen
– Falls zwei aufeinander folgende Näherungswerte genügend nahe beieinander liegen, nimmt man an, dass
eine Lösung gefunden wurde. Der Bildpunkt erhält dann eine Farbe, die sich aus der Anzahl der Iterationen
ergibt (nicht Schwarz).
– Falls nach einer maximalen Anzahl von Iterationen keine Lösung gefunden wurde, färbt man den Punkt zu
diesem Startwert schwarz ein.
Überarbeiten Sie die Funktion ZeichneFraktalComplex so zu einer Funktion ZeichneNewtonFraktal, dass jeder
Punkt der Zeichenfläche nach diesem Schema eingefärbt wird.
Wie die nächsten beiden Bilder für die Funktion f(z)=z3–z zeigen, ergeben sich dabei interessante Muster (links
der Bereich (–10,10) bis (10,10), rechts ein Ausschnitt):
Man sieht, dass schon geringe Änderungen der Startwerte zu einer deutlich langsameren Konvergenz führen
können. Verwendet man bei dieser Darstellung eine größere Anzahl von Farben, erhält man wesentlich differenziertere Strukturen.
c) Die Nullstellen der komplexen Funktion
f(z)=zn – 1
sind die so genannten Einheitswurzeln:
Complex(cos(2*M_PI*double(i)/n),
sin(2*M_PI*double(i)/n));
Wählen Sie für solche Funktionen die Farben in b) so, dass man an der Farbe sieht, gegen welche Einheitswurzel
der Startwert konvergiert.
In den folgenden beiden Bildern wurden die Konvergenzbereiche der Einheitswurzeln von f(z)=z3–1 in
verschiedene Graustufen eingefärbt (links der Bereich (–10, –10) bis (10,10), rechts ein Ausschnitt).
Hier sieht man, dass an den Grenzen der Konvergenzbereiche schon geringe Änderungen der Startwerte zu einer
anderen Nullstelle führen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6.4 Schrittweise Verfeinerung als Entwurfstechnik
67
6.3.9 Unspezifizierte Anzahl von Argumenten ⊕
6.3.10 Die Funktionen main bzw. WinMain und ihre Parameter
6.3.11 Der Aufruf von Funktionen aus Delphi im C++Builder ⊕
6.3.12 Traditionelle K&R-Funktionsdefinitionen ⊕
6.3.13 Aufrufkonventionen ⊕
6.4 Schrittweise Verfeinerung als Entwurfstechnik
6.5 Etwas Programmierlogik und -stil für Funktionen
Aufgaben 6.5
1. Überprüfen Sie, ob die Funktionen in den Aufgaben von Abschnitt 3.5 eine klar definierte Aufgabe haben und ob die
Namen dieser Funktionen ihre Aufgabe einigermaßen klar beschreiben. Ergänzen Sie diese Funktionen so, dass sie
auf Argumente, für die sie ihre Aufgabe nicht erfüllen können, angemessen reagieren.
2 Versuchen Sie, einen passenden Namen für die folgende Funktion zu finden:
double doit_babe(double i, int *p1, int p2, float p3)
{
int y=0;
if (p2==1)
for (int j=0;j<=i;j++) y=y+i;
else if (i==2) y=Mehrwertsteuer(p2,5);
else if (i==3) y=druckeAdressaufkleber(p2);
return y;
};
3. Für die in Abschnitt 5.8.5 behandelten Funktionen
int ggt(int x, int y) // Aufgabe 5.8.5.3
double Horner(double x, int n, double p[]) // Aufgabe 5.8.5.4
soll nachgewiesen werden, dass nach ihrem Aufruf die als Kommentar angegebenen Beziehungen gelten:
double y=ggt(a,b);
// y ist der größte gemeinsame Teiler von a und b
double z=Horner(x,n,p);
// z= p[n]*xn + p[n–1]*xn–1 + ... + p[1]*x + p[0]
Welche Schritte sind für einen solchen Nachweis erforderlich?
6.6 Rekursion
6.6.1 Quicksort
6.6.2 Ein rekursiv absteigender Parser
6.6.3 Rekursiv definierte Kurven ⊕
6.6.4 Indirekte Rekursion ⊕
Aufgaben 6.6.4
1. Schreiben Sie die folgenden Funktionen als rekursive Funktionen. Alle diese Aufgaben sind lediglich Übungen zur
Formulierung rekursiver Funktionen. Keine der so erhaltenen Lösungen ist bezüglich der Effizienz mit der iterativen Lösung vergleichbar.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
68
6 Funktionen
a) Fakultät (siehe auch Aufgabe 3.7.4).
b) Fibonacci-Zahlen (siehe auch Aufgabe 3.5.3).
c) Die Funktion ggT soll den größten gemeinsamen Teiler von zwei Werten als Funktionswert zurückgeben (siehe
auch Aufgabe 5.8.5.3).
d) Schreiben Sie für jede dieser Funktionen ein kleines Testprogramm, das die Ergebnisse der rekursiven mit der
iterativen Lösung vergleicht.
2. Die Ackermann-Funktion

ack(n,m)= 

m+1 für n=0
ack(n–1,1) für m=0
ack(n–1,ack(n,m–1)) sonst
setzt mit dem Index n (ab n=1) in gewisser Weise die „Folge" Addition, Multiplikation, Potenzierung fort. Für die
ersten Werte von n gilt:
ack(0,m) = m + 1
ack(1,m) = m + 2
ack(2,m) = 2*m + 3
ack(3,m) = 2m+3 – 3
Definieren Sie die rekursive Funktion ack und vergleichen Sie ihre Werte bis n=3 und m=10 mit den expliziten
Formeln. Wegen
ack(4,m) = ack(3,ack(4,m–1)) = 2^(ack(4,m–1)+3) – 3 // 2ack(4,m–1) + 3 – 3
= 2^{2^(ack(4,m–2)+3) – 3} + 3) – 3
ergibt sich für ack(4,m) ein Wert in der Größenordnung
2
2
...
ack(4,m) = 2
wobei der „Turm der Potenzen“ m+3 Glieder hoch ist. Offensichtlich wächst diese Funktion sehr schnell: Bereits
ack(4,2) hat 19729 Dezimalstellen. Mit n>4 erhält man ein noch schnelleres Wachstum.
3. Erweitern Sie den Rechner aus Abschnitt 6.6.2 um Funktionen wie sin, cos usw. Testen Sie diese Erweiterungen.
4. Ein Verzeichnis (Laufwerk, Unterverzeichnis) kann mit den beiden Funktionen
HANDLE FindFirstFile(
LPCTSTR lpFileName, // Zeiger auf Pfad und Suchmaske
LPWIN32_FIND_DATA lpFindFileData) // Zeiger auf struct
BOOL FindNextFile(
HANDLE hFindFile, // Handle - Rückgabewert von FindFirstFile
LPWIN32_FIND_DATA lpFindFileData) // Zeiger auf struct
nach allen Dateien durchsucht werden, deren Name einem bestimmten Muster entspricht (z.B. „*.*“ oder „*.cpp“).
Dazu wird zuerst FindFirstFile aufgerufen, wobei das Argument für lpFileName auf einen nullterminierten String
mit dem Pfad und der Suchmaske zeigt (z.B. auf "c:\*.*").
Falls dabei eine solche Datei gefunden wird, gibt diese Funktion einen von INVALID_HANDLE_VALUE
verschiedenen Wert zurück. Er wird bei einem Aufruf von FindNextFile für den Parameter hFindFile eingesetzt.
Diese Funktion sucht dann nach der nächsten Datei mit diesem Muster. Falls eine Datei mit diesem Muster
gefunden wurde, ist ihr Funktionswert von Null verschieden und andernfalls Null.
Die zurückgegebene Datenstruktur
typedef struct _WIN32_FIND_DATA { // wfd
DWORD
dwFileAttributes; // Dateiattribute
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6.6 Rekursion
69
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh; // Dateigröße (obere 32 Bit)
DWORD nFileSizeLow; // Dateigröße (untere 32 Bit)
TCHAR cFileName[MAX_PATH];
// langer Dateiname
TCHAR cAlternateFileName[14]; // 8.3-Dateiname
} WIN32_FIND_DATA;
enthält unter anderem in dwFileAttributes die Attribute und in cFileName den Namen der Datei. Wenn die
gefundene Datei ein Verzeichnis ist, ist in den Attributen dasselbe Bit gesetzt wie in der vordefinierten Konstanten
FILE_ATTRIBUTE_DIRECTORY. Dieses Verzeichnis kann man dann rekursiv durchsuchen, ebenso alle weiteren
Unterverzeichnisse. Die Verzeichnisse mit den Namen „.“ und „..“ muss man dabei ignorieren.
Nachdem man ein Verzeichnis durchsucht hat, sollte man das mit FindFirstFile reservierte Handle mit FindClose
wieder schließen und freigeben:
BOOL FindClose(HANDLE hFindFile) // file search handle
Weitere Informationen zu diesen Funktionen findet man in der Online-Hife zum Windows-SDK.
a) Schreiben Sie eine Funktion SearchSubdirs, die alle Dateien eines Laufwerks in einem RichEdit-Fenster anzeigt
(ein Memo ist zu klein, wenn das Laufwerk viele Dateien enthält). Das Laufwerk soll über eine DriveComboBox
(Komponentenpalette Seite Win.3.1) ausgewählt werden können. Zählen Sie die Anzahl der gefundenen Dateien
und vergleichen Sie diese Anzahl mit dem Ergebnis der Suchfunktion von Windows (unter Windows
Start|Suchen).
FindFirstFile findet immer nur im aktuellen Verzeichnis Dateien mit dem angegebenen Muster. Wenn man z.B.
nach Dateien mit dem Muster *.cpp“ sucht, werden diese nur in solchen Unterverzeichnissen gefunden, deren
Name auch nach diesem Muster aufgebaut ist. Deshalb sollte man nur das Muster „*.*“ bzw. „*“ verwenden,
wenn man alle Dateien in allen Unterverzeichnissen finden will.
b) Überarbeiten Sie die Funktion SearchSubdirs so zu einer Funktion SearchSubdirsTV, dass alle Verzeichnisse
eines Laufwerks wie im Windows-Explorer in einem TreeView angezeigt werden. Dabei kann wie in der
Abbildung auf Icons usw. verzichtet werden:
Die Baumknoten für die Laufwerke (z.B. „c:\“) erhält man z.B. mit
TreeView1->Items->Add(TreeView1->TopItem, "c:\\");
Die Knoten der ersten Ebene („CBuilder5“ usw. ) erhält man mit
TTreeNode* T=
TreeView1->Items->Item[TreeView1->Items->Count-1]
Form1->TreeView1->Items->AddChild(T,"CBuilder5");
und die der zweiten Ebene (z.B. „Bin“, „Lib“) z.B. mit
T=T->Item[T->Count-1];
Form1->TreeView1->Items->AddChild(T,"Bin");
Der übergeordnete Knoten zu einem Knoten T ist T->Parent.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
70
6 Funktionen
5. Die folgenden Figuren entstehen rekursiv aus einem gleichseitigen Dreieck. Falls die Abbruchbedingung nicht
erreicht ist, wird das Dreieck in 4 weitere Teildreiecke unterteilt, von denen die drei äußeren gezeichnet werden
(ähnlich wie die Koch’schen Kurven).
Schreiben Sie ein Programm, das diese Dreiecke zeichnet. Zum Zeichnen eines Dreiecks kann die Funktion
Polygon des Canvas verwendet werden.
6.6.5 Rekursive Datenstrukturen und binäre Suchbäume
6.6.6 Rekursive Datenstrukturen in der Standardbibliothek von C++
Aufgaben 6.6.6
1. Schreiben Sie ein Programm, das Strings aus einem Edit-Fenster in einen Binärbaum einhängt:
– Beim Anklicken eines Buttons mit der Aufschrift TreeInsert soll der Text aus einem ersten Edit-Fenster als
Schlüsselwert in einen binären Suchbaum eingetragen werden. Der Text aus einem zweiten Edit-Fenster soll im
Baumknoten in eine verkette Liste am Ende eingehängt werden.
– Beim Anklicken eines Buttons mit der Aufschrift ShowTree sollen die Strings der Liste in einem Memo
angezeigt werden.
2. Schreiben Sie ein Programm, das eine Liste mit allen verschiedenen Wörtern aus einem Text erstellt
(Konkordanzliste). Diese Wörter sollen dann alphabetisch ausgegeben werden, wobei auf jedes Wort die Nummern
der Zeilen folgen sollen, in denen es vorkommt.
Dazu können Sie folgendermaßen vorgehen: Lesen Sie die Textdatei mit der globalen Funktion getline (siehe
Abschnitt 4.6.4) zeilenweise ein. Zerlegen Sie jede Zeile mit der Funktion parseString von Aufgabe 4.3.2.3 in einzelne Wörter. Tragen Sie dann jedes so gefundene Wort als Schlüsselwert in einen binären Suchbaum ein. Zu jedem
Schlüsselwert sollen die Zeilennummern in einer verketteten Liste gespeichert werden.
In Aufgabe 4.7.4 wurde eine solche Konkordanzliste mit dem Container multimap gelöst. Vergleichen Sie den
Aufwand für diese beiden Lösungen.
3. Schreiben Sie ein Programm, das alle doppelten Dateien auf einer oder mehreren Festplatten findet. Für das
Durchsuchen aller Dateien auf einem Laufwerk können Sie sich an Aufgabe 6.6.4.4 orientieren.
In diesem Zusammenhang stellt sich die Frage, wann zwei Dateien als gleich betrachtet werden sollen. Der Name
ist dafür nicht unbedingt als Kriterium geeignet: Zwei Dateien mit demselben Namen können verschiedene Inhalte
haben und zwei Dateien mit verschiedenen Namen denselben.
Am sichersten wäre es, wenn man alle Dateien mit derselben Größe zeichenweise vergleichen würde. Das wäre
allerdings sehr zeitaufwendig. Ein pragmatischer Kompromiss ist ein Schlüssel, bei dem die Dateigröße und der
Dateiname zu einem einzigen String zusammengesetzt werden (z.B. „325config.sys“, wenn die Datei „config.sys“
325 Bytes groß ist). Mit diesem Schlüssel werden nur diejenigen Dateien als gleich betrachtet, die denselben Namen
und dieselbe Dateigröße haben.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6.7 Inline-Funktionen
71
a) Lösen Sie diese Aufgabe mit einem binären Suchbaum, der in jedem Knoten den Namen einer Datei enthält.
Doppelte Dateien werden dann in eine verkettete Liste eingehängt.
b) Lösen Sie diese Aufgabe mit dem assoziativen Container multimap aus der Standardbibliothek von C++ (siehe
Abschnitt 4.7).
4. Ein Verzeichnis auf einem Laufwerk enthält Dateien und Unterverzeichnisse. Es bietet sich deshalb an, ein
Laufwerk bzw. Verzeichnis durch rekursive Datenstrukturen darzustellen.
a) Entwerfen Sie geeignete rekursive Datenstrukturen. Sie können dazu so vorgehen:
Die Liste der Dateien und Unterverzeichnisse eines Verzeichnisses kann man in einer verketteten Liste mit
einem Zeiger auf die erste und letzte Datei verwalten. Ein Knoten für ein Verzeichnis besteht dann aus zwei
Zeigern auf den ersten und letzten Knoten mit den Dateinamen. Außerdem kann man den Namen des
Verzeichnisses in den Knoten aufnehmen.
Da ein Verzeichnis wiederum Verzeichnisse enthalten kann, muss man in einen Knoten für eine Datei nicht nur
deren Namen, sondern auch einen Zeiger auf ein Verzeichnis aufnehmen. Falls der Knoten dann eine Datei
darstellt, wird dieser Zeiger auf 0 gesetzt, während er bei einem Unterverzeichnis auf einen Verzeichniseintrag
zeigt.
Schreiben Sie außerdem eine Funktion, die eine Datei in ein Verzeichnis einhängt, sowie eine Funktion, die
einem Verzeichnisknoten ein neues Verzeichnis zuweist, das ein leeres Verzeichnis darstellen soll.
b) Überarbeiten Sie eine Kopie der Funktion SearchSubdirs aus Aufgabe 6.5.4 so, dass alle Dateien und
Unterverzeichnisse eines Laufwerks bzw. Verzeichnisses in die unter a) entworfene Datenstruktur eingehängt
wird.
c) Schreiben Sie eine Funktion ShowDirTree, die die Verzeichnisse der in b) gesammelten Daten wie im Explorer
von Windows in einem TreeView anzeigt. Siehe dazu die Ausführungen zu Aufgabe 6.6.4.4 b).
d) Erweitern Sie die Lösung der Aufgaben a) bis c) so, dass im TreeView mit jedem Knoten die Anzahl der in allen
Unterverzeichnissen belegten Bytes angezeigt wird:
6.7 Inline-Funktionen
6.8 Überladene Funktionen
Aufgaben 6.8
1. a) Definieren Sie überladene Funktionen Abs, die für Argumente der Datentypen int, double und long double den
Absolutbetrag als Funktionswert desselben Datentyps wie das Argument ergeben.
b) Mit welchen der folgenden Datentypen können die Funktionen Abs aufgerufen werden? Welche dieser
Funktionen wird dabei aufgerufen?
b1) short
b4) enum {e1= –1, e2=1}e;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
72
6 Funktionen
b2) unsigned
b3) unsigned long
b5) float
b6) char*
2. Überarbeiten Sie das Newton-Verfahren von Aufgabe 6.3.8.3 so, dass es für Argumente des Datentyps double bzw.
long double einen Funktionswert desselben Datentyps liefert.
3. In der Datei „include\stddefs.h“ sind die Konstanten TRUE und FALSE so definiert:
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
Welche der beiden Funktionen
void f(bool b) { }
void f(int i) { }
wird durch die folgenden Ausdrücke aufgerufen?
a) f(true);
b) f(TRUE);
4. Welche der folgenden Funktionsaufrufe können aufgelöst werden? Welche Funktionen werden jeweils aufgerufen?
a) Nach den Definitionen
void g(char *) { }
void g(const char *) { }
const char* s1="abc";
char* s2="abc";
char* const s3="abc";
const char s4[20]="abc";
char s5[20]="abc";
char const s6[20]="abc";
in den folgenden Ausdrücken:
g(s1);
g(s2);
g(s3);
g(s4);
g(s5);
g(s6);
b) Nach den Definitionen
void h(char *, double d=0) { }
void h(const char *, int i=0) { }
in den folgenden Ausdrücken (s1 bis s6 wie in a)):
h(s1,1);
h(s2,1);
h(s3,1);
h(s4,1);
h(s5,1);
h(s6,1);
6.9 Überladene Operatoren mit globalen Operatorfunktionen
6.9.1 Globale Operatorfunktionen
6.9.2 Die Inkrement- und Dekrementoperatoren
6.9.3 Referenzen als Funktionswerte
6.9.4 Die Ein- und Ausgabe von selbst definierten Datentypen
Aufgaben 6.9
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
6.9 Überladene Operatoren mit globalen Operatorfunktionen
73
1. Auf den ersten Blick erscheint es vielleicht als naheliegend, mit dem Operator ^ Potenzen zu realisieren, so dass x^n
für xn steht. Welcher Wert würde sich dabei für den folgenden Ausdruck ergeben?
x^n – 1
2. Eine rationale Zahl (ein Bruch im Sinne der Bruchrechnung) besteht aus einem ganzzahligen Zähler und einem
Nenner und kann deshalb durch die folgende Klasse dargestellt werden:
struct CBruch {
int z,n; // z: Zähler, n: Nenner
};
Die Bruchrechnung ist auf Rechnern nicht sehr verbreitet, da die Rechnungen leicht zu einem Überlauf führen. Sie ist
aber ein einfaches Beispiel dafür, wie man überladene Operatoren definieren kann.
Zur Vereinfachung werden Brüche im Folgenden durch Wertepaare wie (1,2) dargestellt und nicht mit einem
Bruchstrich wie in ½.
a) Zwei Brüche (pz,pn) und (qz,qn) sind gleich, wenn pz*qn==pn*qz gilt. Definieren Sie die Operatoren „==“ und
„!=“ für Operanden des Datentyps CBruch. Stellen Sie sicher, dass zwischen diesen Operatoren die üblichen
Beziehungen gelten.
b) Der Bruch (pz,pn) ist kleiner als (qz,qn), wenn pz*qn<pn*qz gilt. Definieren Sie die Operatoren „<“, „>=“,
„<=“ und „>“ für Operanden des Datentyps CBruch. Stellen Sie sicher, dass zwischen diesen Operatoren die
üblichen Beziehungen gelten.
c) Definieren Sie Operatorfunktionen für die folgenden Operationen.
(pz,pn) + (qz,qn) = (pz*qn + pn*qz, pn*qn)
(pz,pn) – (qz,qn) = (pz*qn – pn*qz, pn*qn)
(pz,pn) * (qz,qn) = (pz*qz,pn*qn)
(pz,pn) / (qz,qn) = (pz*qn,pn*qz)
Bei diesen Operationen sollen der Zähler und Nenner gekürzt werden, indem man beide durch den größten
gemeinsamen Teiler dividiert. Dazu kann die Funktion ggT (siehe Aufgabe 5.8.5.3) verwendet werden:
int ggT(int a, int b)
{
int x=a;
int y=b;
while (y != 0)
{
int r = x%y;
x = y;
y = r;
}
return x; // ggT(a,b)==x;
}
d) Testen Sie diese Operatoren. Vergleichen Sie dazu die Werte, die sich bei der geometrischen Reihe
1 + p + p2 + ... pn = (pn+1 –1)/(p–1) // p=z/n
durch Aufsummieren (linke Seite) und mit der Summenformel (rechte Seite) ergeben. Sie können diese Werte
außerdem mit dem Wert der Summenformel vergleichen, indem Sie den Bruch einsetzen.
3. Definieren Sie für die Klasse CBruch aus Aufgabe 2 überladene Operatoren << und >>, so dass ein Bruch sowohl
aus einem istream eingelesen als auch über einen ostream (z.B. cin oder cout) ausgegeben werden kann.
Verwenden Sie diese Operatoren in zwei Funktionen BruchToStr und StrToBruch, die einen CBruch über einen
ostringstream in einen string der Standardbibliothek von C++ umwandeln bzw. über einen istringstream einen
Bruch aus einem string einlesen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
7 Modulare Programmierung und Namensbereiche
7.1 Separate Kompilation und statische Bibliotheken
7.1.1 Projekte im C++Builder ⊕
7.1.2 Bindung ⊕
7.1.3 Deklarationen und Definitionen ⊕
7.1.4 Die „One Definition Rule“ ⊕
7.1.5 Header-Dateien und Bibliotheken ⊕
7.1.6 Der Aufruf von in C geschriebenen Funktionen ⊕
Aufgaben 7.1
1. Die folgenden beiden Dateien enthalten einige Inkonsistenzen. Welche werden vom Compiler bzw. Linker entdeckt,
wenn Unit0.cpp dem Projekt hinzugefügt und
a) Unit0.h mit einer #include-Anweisung in Unit0.cpp aufgenommen wird,
b) die #include-Anweisung in Unit0.cpp vergessen wird.
Unit0.cpp
#include "Unit0.h" // in b) weglassen
double x0;
double x1=1;
const double c=3;
extern const double d=3;
void f(double)
{
x0++;
};
Unit0.h
#ifndef Unit1H
#define Unit1H
int x0;
//
int x1=1;
//
const int c=3;
//
extern int d=3;
//
extern const int e=3; //
void f(int);
//
#endif
1.
2.
3.
4.
5.
6.
2. Erzeugen Sie für die folgenden Deklarationen eine Header-Datei „Unit3.h“ und eine cpp-Datei „Unit3.cpp“, so dass
die Header-Datei mit #include in beliebig viele Quelltextdateien eines Projekts eingebunden werden kann. In allen
diesen Dateien sollen die so deklarierten Namen dieselben Variablen, Funktionen usw. bezeichnen:
struct TSpielkarte {
char* Farbe;
int Wert;
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
7.1 Separate Kompilation und statische Bibliotheken
75
const int AnzahlKartenProHand=5;
typedef TSpielkarte THand[AnzahlKartenProHand];
const int AnzahlSpielkarten=16;
TSpielkarte Spielkarten[AnzahlSpielkarten] = {
{"Pik",9},{"Pik",10}, {"Pik",11}, {"Pik",12},
{"Herz",9},{"Herz",10}, {"Herz",11}, {"Herz",12},
{"Kreuz",9},{"Kreuz",10}, {"Kreuz",11}, {"Kreuz",12},
{"Karo",9},{"Karo",10}, {"Karo",11}, {"Karo",12}
}; // zur Vereinfachung nur wenig Karten
int AnzahlVerteilteKarten=0;
int AnzahlMitspieler=2;
const int MaxAnzahlMitspieler=100;
THand Hand[MaxAnzahlMitspieler];
void zeigeHand(TMemo* Memo, THand H)
{
Memo->Clear();
for (int i=0; i<AnzahlKartenProHand; i++)
Memo->Lines->Add(AnsiString(H[i].Farbe)+
" "+H[i].Wert);
}
inline void verteileKarten(TSpielkarte* K, THand H)
{
for (int i=0; i<AnzahlKartenProHand; i++)
H[i]=K[AnzahlVerteilteKarten+i];
AnzahlVerteilteKarten+=AnzahlKartenProHand;
}
#include <algorithm> // für swap
#include <stdlib>
// für rand()
void MischeKarten(TSpielkarte* K)
{
for (int i=0; i<50;i++)
{
int p=rand()%AnzahlSpielkarten;
int q=rand()%AnzahlSpielkarten;
std::swap(K[p],K[q]);
}
}
Verwenden Sie diese Definitionen in einem Projekt mit zwei Formularen. In jedem Formular sollen beim Anklicken
eines Buttons die folgenden Anweisungen ausgeführt werden:
void __fastcall TForm1::Button1Click(TObject *Sender)
{ // in Form2: Hand[1] anstelle Hand[0]
MischeKarten(Spielkarten);
if (AnzahlVerteilteKarten+AnzahlKartenProHand<
AnzahlSpielkarten)
verteileKarten(Spielkarten, Hand[0]);
Label1->Caption="n="+IntToStr(AnzahlVerteilteKarten);
zeigeHand(Memo1,Hand[0]); // in Form2: Memo2,Hand[1]
}
3. Schon in früheren Abschnitten haben wir Hilfsfunktionen in eigenen Dateien definiert und diese dann mit einer
#include-Anweisung übernommen:
\CppUtils\GraphUtils.cpp (Abschnitt 4.5.1)
\CppUtils\TimeUtils.cpp (Aufgabe 4.5.6.1)
Da wir diese Dateien bisher immer nur in eine einzige Quelltextdatei eines Projekts eingebunden haben, hat das
auch funktioniert, obwohl sie Funktionsdefinitionen und nicht nur Deklarationen enthalten.
a) Teilen Sie jede dieser Dateien so in eine Header-Datei mit der Endung „.h“ und in eine Datei mit der Endung
„.cpp“ auf, dass die Header-Dateien in mehrere Quelltextdateien eines Projekts eingebunden werden können,
ohne dass Namenskonflikte entstehen. Damit die alten Projekte auch nach diesen Änderungen übersetzt werden
können, sollen diese Dateien in einem anderen Verzeichnis als „\CppUtils“ abgelegt werden (z.B. „\CppLib“).
Testen Sie diese Bibliotheken, indem Sie diese wie unter b), c) und d) verwenden. Sie können dazu z.B. die
Lösung der Aufgaben von Abschnitt 4.5.1 bzw. 6.3.8.5 oder ein eigenes Programm verwenden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
76
7 Modulare Programmierung und Namensbereiche
b) Nehmen Sie die in Aufgabe a) erzeugten Dateien mit der Endung „*.cpp“. mit Projekt|Dem Projekt hinzufügen
in das Projekt auf und erzeugen Sie ein lauffähiges Programm.
c) Nach der erfolgreichen Lösung von b) hat der Compiler aus den „*.cpp“-Dateien Object-Dateien mit der Endung
„*.obj“ erzeugt. Entfernen Sie die unter b) in das Projekt aufgenommenen cpp-Dateien wieder aus dem Projekt
und fügen Sie statt dessen die Object-Dateien dem Projekt hinzu. Sie können diese auch mit „#pragma link“ in
das Projekt aufnehmen.
d) Erzeugen Sie aus den Object-Dateien von b) mit dem Hilfsprogramm TLib eine Bibliotheksdatei und fügen Sie
diese anstelle der Object-Dateien dem Projekt hinzu.
7.2 Dynamic Link Libraries (DLLs)
7.2.1 DLLs erzeugen ⊕
7.2.2 Implizit geladene DLLs ⊕
7.2.3 Explizit geladene DLLs ⊕
7.2.4 Hilfsprogramme zur Identifizierung von Funktionen in DLLs ⊕
7.2.5 DLL-Funktionen mit visuell gestalteten Komponenten ⊕
7.2.6 Projektgruppen ⊕
7.2.7 Batch-Dateien ⊕
Aufgabe 7.2
Legen Sie eine Projektgruppe an (z.B. mit dem Namen DLLPrGrp) und fügen Sie ihr die folgenden Projekte hinzu:
a) ein DLL-Projekt GraphDLL. Die dabei erzeugte DLL soll die folgenden Funktionen (siehe Aufgabe 7.1.3, bzw.
Abschnitt 4.5.1) zur Verfügung stellen:
double x_Welt(int px, double x0, double x1, double W)
double y_Welt(int py, double y0, double y1, double H)
int x_Bildschirm(double x, double x0, double x1,double W)
int y_Bildschirm(double y, double y0, double y1,double H)
void Gitternetz(TImage* Image1, double x0, double x1,
double dx, int W, double y0,double y1,double dy, int H)
void ClearImage(TImage* Image1)
void PlotFunction(double x0, double x1, real_func f,
TImage* Image1)
b) ein DLL-Projekt TimeDLL. Die dabei erzeugte DLL soll die folgende Funktion zur Verfügung stellen (siehe
Aufgaben 1.2 bzw. 4.5.6.1):
double HRTimeInSec()
c) eine Anwendung UseDLLImpl, die diese DLL implizit lädt und die Funktionen aufruft:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Start1=HRTimeInSec();
PlotFunction(-10, 10, sin, Image1);
double End1=HRTimeInSec();
Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
}
d) eine Anwendung UseDLLExpl, die diese DLLs explizit lädt und die Funktionen wie in c) aufruft.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
7.3 Namensbereiche
77
7.3 Namensbereiche
7.3.1 Die Definition von benannten Namensbereichen
7.3.2 Die Verwendung von Namen aus Namensbereichen
7.3.3 Aliasnamen für Namensbereiche
7.3.4 Unbenannte Namensbereiche
7.3.5 Module und das Geheimnisprinzip
Aufgaben 7.3
1. Geben Sie an, welche Funktionen in den mit a), b), c) und d) gekennzeichneten Zeilen gemeint sind:
void f(){};
void g(){};
namespace A {
void f(){};
void g(){};
}
namespace B {
using ::f; // a)
using A::g; // b)
}
void h()
{
using namespace B;
f();
// c)
B::g();
// d)
}
2. In der Lösung der Aufgabe 7.1.3 wurden die Funktionen zur hochauflösenden Zeitmessung folgendermaßen auf
zwei Dateien verteilt:
TimeUtils.h
double HRTimeInSec(); // Deklaration, keine Definition
TimeUtils.cpp // die zugehörigen Definitionen
double GetHRFrequency(){ /* wie in Aufgabe 4.5.6.1 */}
double HRTimeInSec()
{ /* wie in Aufgabe 4.5.6.1 */}
double HRFrequency=GetHRFrequency();
Überarbeiten Sie diese Lösung so, dass die Funktion HRTimeInSec in einem Namensbereich „TimeUtils“ zur
Verfügung steht. Die anderen beiden Namen GetHRFrequency und HRFrequency sollen außerhalb ihrer
Quelltextdatei nicht verfügbar sein.
3. Zwei verschiedene Quelltextdateien sollen zwei verschiedene Funktionen mit demselben Namen f enthalten. Beide
Funktionen sollen in einem Namensbereich enthalten sein, der ebenfalls in jeder Datei gleich ist:
f1.cpp:
namespace N{
int f(const int i)
{
return i;
}
} // end of namespace
f2.cpp:
namespace N{
int f(const int i)
{
return i+1;
f1.h
namespace N{
int f(const int i);
} // end of namespace
f2.h
namespace N{
int f(const int i);
} // end of namespace
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
78
7 Modulare Programmierung und Namensbereiche
}
} // end of namespace
Kann man die beiden Funktionen f gemeinsam in einem Programm verwenden, ohne diese Quelltextdateien zu
ändern?
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8 Objektorientierte Programmierung
8.1 Klassen
8.1.1 Datenelemente und Elementfunktionen
8.1.2 Der Gültigkeitsbereich von Klassenelementen
8.1.3 Objekte und die Zugriffsrechte private und public
8.1.4 Der Aufruf von Elementfunktionen und der this-Zeiger
8.1.5 Konstruktoren und Destruktoren
Aufgaben 8.1.5
1. Beschreiben Sie die Aufgaben eines Konstruktors und Destruktors. Werden diese Aufgaben in der folgenden Klasse
erfüllt?
class C {
int n, max;
int* a;
public:
C()
{
max=100;
a=new int[max];
}
C(int i)
{
n=1;
a=new int[100];
a[0]=i;
};
C(int i,int j)
{
n=2;
max=100;
a=new int[100];
a[1]=i;
a[2]=j;
};
void add_data(int d)
{
if (n<max-1)
{
++n;
a[n]=d;
}
}
void show_data()
{
for (int i=0; i<n; ++i)
Form1->Memo1->Lines->Add(IntToStr(a[i]));
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
80
8 Objektorientierte Programmierung
};
2. Welche der folgenden Klassen benötigen einen Destruktor?
a) class C1 {
int x,y,z;
public:
C1(int x_=0, int y_=0, int z_=0)
{
x=x_; y=y_; z=z_;
}
};
b) class C2 {
int* x;
public:
C2(int n)
{
x=new int[n];
}
};
c) #include <fstream>
class C3 {
ifstream f;
public:
C3(char* FileName)
{
f.open(FileName);
}
};
3. Definieren Sie die folgenden Klassen. Jede soll geeignete Konstruktoren sowie bei Bedarf auch einen Destruktor
enthalten.
a) Die Klasse CKreis soll einen Kreis darstellen und als private Datenelement den Radius (Datentyp double)
enthalten. Ein Konstruktor und die Elementfunktion setzeRadius sollen einen Parameter des Datentyps double
haben, der den Radius setzt. Die Elementfunktion Radius() soll den Radius als Funktionswert zurückgeben. Alle
Elementfunktionen von CKreis sollen innerhalb der Klasse definiert werden.
b) Die Klasse CQuadrat soll ein Quadrat darstellen und als private Datenelement die Seitenlänge (Datentyp
double) enthalten. Ein Konstruktor und die Elementfunktion setze_Seitenlaengen sollen einen Parameter des
Datentyps double haben, der die Seitenlänge setzt. Die Elementfunktion Seitenlaenge() soll die Seitenlänge als
Funktionswert haben. Alle Elementfunktionen sollen außerhalb der Klasse definiert werden.
c) Die Klasse CRechteck soll ein Rechteck darstellen und als private Datenelemente die Seitenlängen a und b
(Datentyp double) enthalten, die durch einen Konstruktor initialisiert werden. Diese sollen auch mit der
Funktion setze_Seitenlaengen gesetzt werden können, die zwei Parameter des Datentyps double hat. Die
jeweiligen Seitenlängen sollen als Funktionswert der Funktionen Seitenlaenge_a() und Seitenlaenge_b()
zurückgegeben werden.
d) Ergänzen Sie jede der unter a) bis c) definierten Klassen um die Elementfunktionen Flaeche(), Umfang() und
toStr(). Die Funktion toStr() soll einen String der Art „Kreis mit Radius 5“ oder „Rechteck mit a=6 und b=7“
zurückgeben.
e) Legen Sie mit jedem Konstruktor der unter a) bis c) definierten Klassen zwei Objekte an. Das erste soll durch
eine Definition und das zweite mit new angelegt werden. Rufen Sie jede Funktion aus d) für jedes dieser Objekte
auf.
4. Ein einfaches Programm zur Verwaltung von Immobilien soll die Klassen CGrundstueck, CEigentumswohnung
usw. enthalten, mit denen Grundstücke usw. dargestellt werden können.
Jede dieser Klassen soll ein Datenfeld Anschrift des Datentyps char* und ein Datenfeld Kaufpreis des Datentyps
double enthalten. Zusätzlich zu diesen beiden Feldern sollen diese Klassen auch noch die folgenden Datenelemente
enthalten:
– Die Klasse CGrundstueck soll das Datenelement Flaeche enthalten.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.1 Klassen
81
– Die Klasse CEigentumswohnung soll die Datenelemente Wohnflaeche und AnzahlZimmer enthalten.
– Die Klasse CSchloss soll das int-Element AnzahlSchlossgeister enthalten.
– Die Klasse CEinfamilienhaus soll die Datenelemente Wohnflaeche, Grundstuecksgroesse und AnzahlZimmer
enthalten.
– Die Klasse CGewerbeobjekt soll das Datenelement Nutzflaeche und Nutzungsart (Datentyp char*, z.B. für
„Büro“, „Restaurant“) enthalten.
a) Definieren Sie diese Klassen. Jede Klasse soll einen Konstruktor haben, der alle Datenelemente initialisiert. Falls
die Klasse einen Destruktor benötigt, soll dieser ebenfalls definiert werden.
b) Für jede Klasse soll eine Elementfunktion toStr die Datenelemente der Klasse zu einem String zusammenfassen.
Verschiedene globale Funktionen mit dem Namen display sollen toStr in einem Memo ausgeben.
5. a) Welche Ausgabe erzeugt ein Aufruf der Funktion test1?
void display(AnsiString s, int i=-1)
{
if (i>=0) s=s+IntToStr(i);
Form1->Memo1->Lines->Add(s);
}
class C{
int x;
public:
C (int x_=0)
{ // Beim Aufruf ohne Argument ein Standardx=x_;
// konstruktor
display("Konstruktor: ", x);
}
~C ()
{ display("Destruktor: ",x); }
};
void f1(C c)
{
display(" in f1(): Werteparameter");
};
void f2(const C& c)
{
display(" in f2(): Referenzparameter");
};
C f3(int i)
{
display(" in f3(): return-Wert");
return C(i);
};
void test1()
{
C x(1);
C* z=new C(2);
display("vor x=C(3)");
x=C(3);
display("vor f1(4)");
f1(4);
display("vor f2(x)");
f2(x);
display("vor f3(5)");
x=f3(5);
delete z;
display("Ende von test()");
}
Vergleichen Sie ihre Vermutungen anschließend mit dem Ergebnis eines Programms, das die Funktion test1
aufruft.
b) Wenn Speicher mit new[] reserviert wird, muss er mit delete[] wieder freigegeben werden. Gibt man mit new
reservierten Speicher mit delete[] wieder frei, ist das Ergebnis undefiniert, ebenso, wie wenn man mit new[]
reservierten Speicher mit delete wieder freigibt. Beschreiben Sie zunächst, welche Ausgabe Sie von einem
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
82
8 Objektorientierte Programmierung
Aufruf der Funktion test2 erwarten. Vergleichen Sie Ihre Vermutungen dann mit dem Ergebnis eines Programms, das die Funktion test2 aufruft.
void test2()
{
display("vor p1");
C* p1=new C[2];
delete[] p1;
display("vor p2");
C* p2=new C[2];
delete p2;
display("vor p3");
C* p3=new C;
delete[] p3;
display("vor p4");
C* p4=new C(4);
delete[] p4;
display("Ende von test()");
}
6. Definieren Sie für die Klasse MeinString eine Elementfunktion c_str. Sie soll wie bei den Stringklassen string bzw.
AnsiString den Zeiger auf den internen nullterminierten String zurückgeben und damit auch Argumente des Typs
MeinString bei den Stringfunktionen wie strcpy usw. ermöglichen.
7. In der folgenden Klasse soll der Datentyp T ein großer Datentyp sein. Um den Zeitaufwand für die Funktion data zu
minimieren, gibt diese als Funktionswert eine Referenz und keine Kopie des Elements x zurück. Beurteilen Sie
diesen Ansatz.
class C {
T x;
public:
C(T x_) { x=x_; }
T& data() // T data() wäre zu langsam
{
return x;
}
};
8. Die Funktionen (siehe Abschnitt 4.5.6)
double GetHRFrequency()
{
LARGE_INTEGER f;
BOOL bf=QueryPerformanceFrequency(&f);
if (bf) return f.QuadPart; // Datentyp von QuadPart:
else return -1;
// __int64
}
double HRFrequency=GetHRFrequency();
double HRTimeInSec()
{
LARGE_INTEGER c;
BOOL bc=QueryPerformanceCounter(&c);
if (bc) return c.QuadPart/HRFrequency;
else return -1;
}
kann man folgendermaßen zur hochauflösenden Zeitmessung verwenden:
double Start1=HRTimeInSec();
s=Sum1(n);
double End1=HRTimeInSec();
Memo1->Lines->Add("t="+FloatToStr(End1-Start1));
Schreiben Sie eine Klasse CHRTimer mit den Elementfunktionen Start, End und TimeStr, die ohne die globale
Variable HRFrequency auskommt. Beim Aufruf von Start und End sollen entsprechende Datenelemente der Klasse
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.1 Klassen
83
auf die aktuelle Zeit gesetzt werden. Die Funktion TimeStr soll die zwischen den letzten Aufrufen von Start und End
vergangene Zeit als String ausgeben (in Sekunden) und folgendermaßen verwendet werden können:
CHRTimer t;
t.Start();
s=Sum1(n);
t.End();
Memo1->Lines->Add("t1="+t.TimeStr());
9. Was halten Sie von dem folgenden Trick, den ich im Internet gefunden habe (http://home.att.net/~robertdunn/CodeSamples/CheapTricks.html): „Borland, for some unfathomable reason, decided to make
member data private in many classes. Here is a rather clever way to get around it:"
#define private public
#define protected public
#include <theworld.h>
#undef private
#undef public
10. Schreiben Sie eine Klasse MeinStack, die einen Stack mit einem Array und den folgenden Elementfunktionen
realisiert.
a) Das Array soll im Konstruktor mit einer als Parameter übergebenen Elementanzahl dynamisch reserviert
werden.
b) Die Funktion push soll einen als Argument übergebenen Wert auf die nächste freie Position ablegen.
c) Die Funktion pop soll den zuletzt auf den Stack gelegten Wert als Funktionswert zurückgeben.
d) Die boolesche Funktion full soll den Wert true zurückgeben, wenn alle Elemente des Arrays belegt sind, und
andernfalls den Wert false.
e) Die boolesche Funktion empty soll angeben, ob der Stack leer ist.
11. Eine verkettete Liste wird im einfachsten Fall aus Knoten wie
typedef int T;
// T: der Datentyp der "Nutzdaten"
struct list_node { // Knoten einer verketteten Liste
T data;
list_node* next;
};
zusammengesetzt (siehe auch Abschnitt 4.4.2). Wenn man eine solche Liste mit einem Zeiger auf das erste und
letzte Element verwaltet, kann man ein neues Element mit der folgenden Funktion einfügen:
list_node *first=0,*last=0;
void insert_list_node(list_node*& first,
list_node*& last, T data)
{ /* Erzeugt einen neuen Listen-Knoten und fügt diesen
nach last ein. Last zeigt anschließend auf das
letzte und first auf das erste Element der Liste. */
list_node* tmp=new list_node;
tmp->data = data;
tmp->next = 0;
if (last == 0) first = tmp;
else last->next = tmp;
last = tmp;
}
Die Elemente der Liste kann man dann mit der Funktion show() ausgeben:
void show()
{
for (list_node* tmp=first; tmp != 0; tmp = tmp->next)
Form1->Memo1->Lines->Add(tmp->data);
}
Definieren Sie eine Klasse MeineListe für eine solche Liste. Im Destruktor soll der von der Liste belegte
Speicherplatz wieder freigegeben werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
84
8 Objektorientierte Programmierung
8.1.6 OO Analyse und Design: Der Entwurf von Klassen
8.1.7 Ein wenig Programmierlogik: Klasseninvarianten und Korrektheit
Aufgabe 8.1.7
1. a) Geben Sie Klasseninvarianten für die Klassen von Aufgabe 8.1.5.3 an.
b) Was halten Sie davon, die Fläche und den Umfang bei diesen Klassen nicht in entsprechenden
Elementfunktionen zu berechnen, sondern sie in Datenelementen zu speichern und deren Wert zurückzugeben?
2. Geben Sie eine Vorbedingung an, mit der die Funktion replace die Klasseninvariante der Klasse MeinString erhält:
void MeinString::replace(int from, char* x)
{ // ersetze die Zeichen ab 'from' durch die von x
int xn=strlen(x);
for (int i=0; from+i<n && i<xn; i++)
s[from+i]=x[i];
}
8.1.8 UML-Diagramme für Klassen und Objekte
8.2 Klassen als Datentypen
8.2.1 Der Standardkonstruktor
8.2.2 Objekte als Klassenelemente und Elementinitialisierer
Aufgaben 8.2.2
1. Die Klasse E soll in einem Standardkonstruktor die Meldung „Standardkonstruktor“ und in einem Konstruktor mit
einem int-Parameter die Meldung „int-Konstruktor“ ausgeben.
class C {
E e1,e2;
public:
C() { }
C(int i):e1(i) { }
C(int i,int j):e1(i),e2(j) { }
};
Welche Meldungen erhält man dann durch die folgenden Definitionen:
C c0;
C c1(1);
C c2(1,2);
2. Überarbeiten Sie die Klasse CKreis aus Aufgabe 8.1.5.3 zu einer Klasse C2DKreis. Sie soll als Element einen
C2DPunkt enthalten, der die Position eines Kreises beschreibt. Definieren Sie für diese Klasse Konstruktoren, bei
denen für die Position ihre Koordinaten oder ein C2DPunkt angegeben werden können. Falls nur ein Argument für
den Radius übergeben wird, soll die Position der Nullpunkt sein. Ein Standardkonstruktor soll den Radius 1 setzen.
Verwenden Sie für möglichst viele Elemente Elementinitialisierer.
3. Im Konstruktor der Klasse CRechteck1 soll der Mittelpunkt des Rechtecks angegeben werden. In der Klasse soll
allerdings nicht der Mittelpunkt, sondern der linke obere Eckpunkt gespeichert werden:
class CRechteck1 {
C2DPunkt LinksOben; // Eckpunkt links oben
double a,b; // Seitenlängen
public:
CRechteck1(C2DPunkt Mittelpunkt, double a_,
double b_): a(a_),b(b_),
LinksOben(Mittelpunkt.x-a/2, Mittelpunkt.y-b/2){ }
AnsiString toStr()
{
return FloatToStr(LinksOben.x)+"|"+
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.2 Klassen als Datentypen
85
FloatToStr(LinksOben.y);
}
};
Mit dieser Definition erhält der Punkt LinksOben jedoch nicht den beabsichtigten Wert. Finden Sie die Ursache
dieses Fehlers und korrigieren Sie die Definition so, dass ein Objekt dieser Klasse das gewünschte Ergebnis hat.
4. In Abschnitt 8.2.1 wurde als Ergänzung zu den Konstruktoren aus Abschnitt 8.1.5 der folgende Standardkonstruktor
für die Klasse MeinString definiert:
class MeinString {
char* s;
int n; // Länge des Strings
public:
MeinString()
{
n=0;
s=new char[n+1];
*s='\0';
};
};
Da hier nur Platz für ein einziges Zeichen '\0' reserviert wird, hätte man ebenso gut den folgenden
Standardkonstruktor verwenden können:
class MeinString {
// ...
MeinString():n(0)
{
s=new char('\0');
};
};
Vergleichen Sie diese beiden Konstruktoren. Ist einer besser als der andere?
8.2.3 friend-Funktionen und -Klassen
8.2.4 Überladene Operatoren als Elementfunktionen
Aufgaben 8.2.4
1. In Aufgabe 6.9.2 wurde ein Bruch durch eine Struktur
struct CBruch {
int z,n; // z: Zähler, n: Nenner
};
dargestellt, bei der alle Datenelemente public sind. Überarbeiten Sie die Lösung dieser Aufgabe so, dass die
Datenelemente private sind. Ergänzen Sie diese Klasse um geeignete Konstruktoren.
2. Ein assoziativer Container ist eine Datenstruktur, die Paare von Schlüsselwerten und Daten verwaltet. In der Klasse
AssozContainer heißen die Schlüsselwerte und Daten wie in der Standardbibliothek first und second:
class AssozContainer { // alles sehr einfach
int n; // Anzahl der Elemente im Container
typedef AnsiString T1;// Datentyp der Schlüsselwerte
typedef AnsiString T2;// Datentyp der Daten
struct Paar {
T1 first; // Schlüsselwert
T2 second; // Daten
};
Paar a[100]; // nur zur Vereinfachung so einfach
public:
AssozContainer():n(0) {};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
86
8 Objektorientierte Programmierung
void show_all()
{
for (int i=0; i<n; ++i)
Form1->Memo1->Lines->Add(a[i].first+
": "+a[i].second);
}
};
Die Arbeit mit einem solchen Container ist mit einem Indexoperator besonders einfach, der zu einem als Index
verwendeten Schlüsselwert die zugehörigen Daten liefert. Mit einem Index, zu dem keine Daten gehören, soll ein
neues Paar im Container abgelegt werden.
Die folgenden Beispiele sollen die Arbeit mit einem solchen Operator illustrieren. Die Ergebnisse der Operationen
sind jeweils anschließend als Kommentar angegeben:
AssozContainer a;
a.show_all();
// Keine Ausgabe, da der Container leer ist.
a["Luigi Mafiosi"]="[email protected]";
a.show_all();
// Luigi Mafiosi: [email protected]
a["Karl Erbschleicher"]="[email protected]";
a.show_all();
// Luigi Mafiosi: [email protected]
// Karl Erbschleicher: [email protected]
a["Luigi Mafiosi"]="[email protected]"; // palermo.net
// war zu langsam
a.show_all();
// Luigi Mafiosi: [email protected]
// Karl Erbschleicher: [email protected]
Memo1->Lines->Add(a["Karl Erbslaicher"]); // Schreiba.show_all();
// fehler
// Luigi Mafiosi: [email protected]
// Karl Erbschleicher: [email protected]
// Karl Erbslaicher:
// Nicht schön: Karl ist ohne Adresse eingetragen.
// Aber bei std::map ist das auch nicht anders.
a) Definieren Sie zu der Klasse AssozContainer einen Indexoperator, der dieselben Ergebnisse wie in diesem
Beispiel hat.
b) Ein Iterator ist eine Klasse, die eine Position in einem Container darstellt. Sie enthält meist
– einen Zeiger (der die Position darstellt) auf ein Element im Container
– einen Konstruktor, der den Zeiger mit einem anderen Zeiger für eine Position in diesem Container
initialisiert
– einen Operator ++, der den Zeiger auf das nächste Element im Container setzt
– einen Operator – –, der den Zeiger auf das vorangehende Element im Container setzt
– einen Operator *, der das Element im Container zum Zeiger im Iterator liefert
– einen Operator ->, der den Zeiger im Iterator liefert
– einen Operator !=, der die Zeiger zweier Iteratoren vergleicht
Für die Klasse AssozContainer ist die Klasse iterator ein solcher Iterator:
class iterator {
Paar* p;
public:
iterator(Paar* p_):p(p_) { }
bool operator!= (const iterator& y);
iterator& operator++(int);
Paar& operator* ();
Paar* operator-> ();
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.2 Klassen als Datentypen
87
Nehmen Sie diese Klasse lokal in die Klasse AssozContainer auf. Definieren Sie die Operatorfunktionen so, dass
man den Container wie in dem folgenden Beispiel mit dem Operator ++ durchlaufen kann:
AssozContainer::iterator i=a.begin();
for (i=a.begin(); i!=a.end();++i)
s=i->second;// ebenso: Paar p=*i;
Außerdem sollen in der Klasse AssozContainer noch die Elementfunktionen begin und end definiert werden. Sie
sollen einen iterator zurückliefern, der auf das erste Element im bzw. nach dem Container zeigt.
8.2.5 Der Copy-Konstruktor
8.2.6 Der Zuweisungsoperator = für Klassen
Aufgaben 8.2.6
1. Begründen Sie für jede der folgenden Klassen, ob sie einen Standardkonstruktor, einen Copy-Konstruktor, einen
überladenen Zuweisungsoperator oder einen Destruktor benötigen. Falls eine solche Funktion notwendig ist, definieren Sie diese. Ist es ein Fehler, diese Funktionen zu definieren, wenn sie nicht notwendig sind?
a)
b)
c)
d)
Die Klassen CKreis, CQuadrat und CRechteck von Aufgabe 8.1.5.3
Die Klasse CBruch von Aufgabe 8.2.4.2
Die Klassen CGrundstueck, CEigentumswohnung, CEinfamilienhaus und CGewerbeobjekt von Aufgabe 8.1.5.4
Wie wäre ihre Antwort, wenn die Strings in den Klassen von c) nicht mit char*, sondern durch einen String der
Klassen string oder AnsiString definiert wäre.
e) Wie wäre ihre Antwort, wenn die Strings in den Klassen von b) nicht mit char*, sondern durch ein Array von
Zeichen (z.B. char Anschrift[100]) definiert wäre.
f) Die Klasse MeinStack von Aufgabe 8.1.5.10
g) Wie wäre ihre Antwort, wenn das Array in MeinStack durch einen vector aus der Standardbibliothek ersetzt
würde.
2. Beschreiben Sie am Beispiel der Funktionen test1 und test2, wann welche Konstruktoren der folgenden Klassen
aufgerufen werden:
void display(AnsiString s, int i=-1)
{
if (i>=0) s=s+IntToStr(i);
Form1->Memo1->Lines->Add(s);
}
class C{
int x;
public:
C (int x_=0)
{ // Beim Aufruf ohne Argument ein Standardx=x_;
// konstruktor
display(" Konstruktor: ", x);
}
C (const C& c)
{
x=c.x;
display(" Kopierkonstruktor: ", x);
}
C& operator=(const C& c)
{
x=c.x;
display(" operator=: ", x);
return *this;
}
~C ()
{
display("
}
Destruktor: ",x);
friend int f3(C c);
friend int f4(const C& c);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
88
8 Objektorientierte Programmierung
};
C f1(int i)
{
return C(i);
}
C f2(int i)
{
C tmp(i);
return tmp;
}
int f3(C c)
{
return c.x;
}
int f4(const C& c)
{
return c.x;
}
a) Welche Ausgabe erzeugt ein Aufruf der Funktion test1:
void test1()
{
display("vor C x=C(1)");
C x=C(1);
C y=x;
display("vor x=y");
x=y;
display("vor C z(x)");
C z(x);
display("vor f1(2)");
f1(2);
display("vor f2(3)");
f2(3);
display("vor f3(4)");
f3(4);
display("vor f3(x)");
f3(x);
display("vor f4(4)");
f4(4);
display("vor f4(x)");
f4(x);
display("Ende von test1");
}
b) Welche Ausgabe erzeugt ein Aufruf der Funktion test2:
class D {
C c1;
C c2;
};
void test2()
{
display("vor D d1");
D d1;
display("vor D d2=d1");
D d2=d1;
display("vor d2=d1");
d2=d1;
display("nach d2=d1");
}
3. Definieren Sie die Operatoren +=, –=, *= und /= für die Klasse CBruch aus Aufgabe 8.2.4.2. Führen Sie die
Operatoren +, –, * und / auf diese Operatoren zurück, so dass die üblichen Beziehungen wie
x += y ist gleichwertig mit x = x + y
gelten. Nur am Rande sei darauf hingewiesen, dass die Operatoren +, –, * und / bei dieser Definition keine friendFunktionen der Klasse sein müssen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.2 Klassen als Datentypen
89
4. In der Programmiersprache C werden ganze Arrays wie
T a[10], b[10]; // T ein Datentyp
oft mit
memcpy(a,b,10*sizeof(T));
kopiert. Beurteilen Sie dieses Vorgehen, wenn der Datentyp T eine Klasse ist.
5. Das Ergebnis des Präfixoperators ++ ist das veränderte Objekt. Mit einer Variablen x eines vordefinierten Datentyps
kann der Ausdruck ++x auch auf der linken Seite einer Zuweisung verwendet werden. Deshalb ist der Funktionswert dieses Operators meist ein Referenztyp. Für eine Klasse C wird der Operator meist mit einer
Elementfunktion nach diesem Schema definiert:
C& C::operator++() // Präfix ++
{
// erhöhe das Objekt
return *this;
}
Der Postfix-Operator liefert dagegen das ursprüngliche Objekt und kann nicht auf der linken Seite einer Zuweisung
verwendet werden. Er wird meist nach diesem Schema definiert:
C C::operator++(int) // Postfix ++
{
C temp=*this;
++(*this); // Aufruf des Präfix-Operators
return temp;
}
Vergleichen Sie die Laufzeit der beiden Operatoren.
6. Für kaufmännische Rechnungen sind Gleitkommadatentypen ungeeignet, da ihre Ergebnisse nicht exakt sind.
Deswegen sollten Geldbeträge in ganzzahligen Pfennigbeträgen dargestellt werden.
Definieren Sie eine Klasse Festkomma64, die Dezimalzahlen mit bis zu 4 Nachkommastellen durch das 10000-fache
ihres Wertes ganzzahlig darstellt. Verwenden Sie dazu den 64-bit Ganzzahldatentyp __int64. Die Grundrechenarten
+, –, * und / sollen durch überladene Operatoren zur Verfügung gestellt werden. Dazu können die
Rechenoperationen von __int64 verwendet werden. Geeignete Konstruktoren sollen Argumente der Datentypen int
und double in diesen Datentyp konvertieren. Eine Funktion toStr soll eine solche Festkommazahl als String
darstellen.
Testen Sie diesen Datentyp mit der Funktion
Festkomma64 Est(Festkomma64 x)
{ // Einkommensteuertarif nach § 32a (1)
Festkomma64 est;
x=(x/54)*54;
if (x <= 12365) est = 0;
else if (x <= 58643)
{
Festkomma64 y = (x-12312)/10000;
est = (91.19*y+2590)*y;
}
else if (x <= 120041)
{
Festkomma64 z = (x-58590)/10000;
est = (151.96*z + 3434)*z +13938;
} // Druckfehler im Gesetz: 151,96 anstelle 151,91
else est = 0.53*x -22843;
return 10000*(est/10000);
};
Dabei müssen sich zu den Werten unter x die jeweils rechts davon unter Est angegebenen Werte ergeben (aus der
Grundtabelle zum Einkommensteuergesetz):
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
90
8 Objektorientierte Programmierung
x
10 800
16 200
21 600
27 000
32 400
37 800
43 200
Est
0
1 020
2 484
4 000
5 570
7 193
8 870
x
48 600
54 000
59 400
64 800
70 200
75 600
81 000
Est
10 599
12 381
14 217
16 129
18 129
20 218
22 396
x
86 400
91 800
97 200
102 600
108 000
113 400
118 800
Est
24 663
27 018
29 461
31 994
34 615
37 324
40 123
7. Für eine Klasse ohne einen explizit definierten Copy-Konstruktor bzw. Zuweisungsoperator erzeugt der Compiler
diese Funktionen, falls sie verwendet werden. Da diese Funktionen leicht aufgerufen werden, ohne dass man es bemerkt, kann es sinnvoll sein, ihren Aufruf zu unterbinden, um flache Kopien zu vermeiden. Wie kann man den
Aufruf dieser Funktionen verhindern?
8. Wenn in einem Ausdruck ein temporäres Objekt erzeugt wird, ruft der Compiler einen Konstruktor für das
Objekt auf. Am Ende der Auswertung des Ausdrucks wird dann der Destruktor für jedes so erzeugte Objekt aufgerufen. Deshalb können auch bei einfachen Ausdrücken relativ viele Konstruktoren und Destruktoren aufgerufen
werden.
Beschreiben Sie die Ausgabe der Funktion test3 für die Klasse C aus Aufgabe 2, wenn für C der Operator + definiert
wird:
C operator+(const C& c1,const C& c2)
{ // friend in der Klasse C
display(" operator+: ", c1.x+c2.x);
return C(c1.x+c2.x);
}
void test3()
{
C s1,s2,s3,s4;
display("1. vor +
s1=C(1)+C(3);
display("2. vor +
C x=C(5),y=C(7);
s2=x+y;
display("3. vor +
s3=C(9);
s3=s3+C(11);
display("4. vor +
s4=x;
s4=s4+y;
display("Ende von
}
");
");
");
");
test");
8.2.7 Benutzerdefinierte Konversionen
8.2.8 Explizite Konstruktoren ⊕
8.2.9 Statische Klassenelemente
8.2.10 Konstante Klassenelemente und Objekte
8.2.11 Weitere Deklarationsmöglichkeiten in Klassen ⊕
8.2.12 Klassen und Header-Dateien
8.2.13 Überladene Operatoren für new und delete ⊕
Aufgaben 8.2.13
1. Zerlegen Sie die Klassen CQuadrat und CKreis aus Aufgabe 8.1.5.3 jeweils in eine Header-Datei mit der
Klassendefinition und in eine Datei mit den Funktionsdefinitionen. Die Header-Dateien sollen die Namensendung
„.h“ und die Dateien mit den Funktionsdefinitionen die Endung „.cpp“ bekommen. Nehmen Sie die cpp-Dateien in
ihr Projekt auf und binden Sie die Header-Dateien mit einer #include-Anweisung ein.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.3 Vererbung
91
2. Kennzeichnen Sie möglichst viele Elementfunktionen der Klassen CQuadrat und CKreis aus Aufgabe 1 als const.
3. Eine Elementfunktion einer Klasse kann sowohl außerhalb als auch innerhalb der Klasse definiert werden. Im
letzten Fall ist sie dann eine inline-Funktion. Nach der One Definition Rule (siehe Abschnitt 7.1.4) sind diese
beiden Möglichkeiten gleichwertig. Insbesondere kann man eine Header-Datei mit einer Klassendefinition,
innerhalb der Funktionen definiert werden, in mehreren Quelltexten eines Projekts mit einer #include-Anweisung
verwenden.
Vergleichen Sie für diese beiden Alternativen den Aufwand für den Compiler, wenn eine Elementfunktion geändert
und die Header-Datei in vielen Dateien eines Projekts verwendet wird.
4. Entwerfen Sie eine Klasse, die mitzählt, wie oft von ihr mit new Objekte angelegt wurden. Der Zähler soll beim
Aufruf von delete wieder reduziert werden.
5. Definieren Sie eine Klasse Singleton, von der nur ein einziges Objekt erzeugt werden kann. Dazu soll sie eine
Funktion Instance haben, die einen Zeiger auf dieses Objekt zurückliefert. Beim ihrem ersten Aufruf soll Instance
ein neues Objekt erzeugen.
Die Verwaltung von Daten in einer solchen Klasse kann eine Alternative zu einer globalen Definition der Daten
sein. Dadurch kann sichergestellt werden, dass mehrere (auch lokale) Definitionen (durch einen Aufruf von
Instance) immer dieselben Daten verwenden. Siehe dazu Gamma (1995, S. 127).
8.3 Vererbung
8.3.1 Die Elemente von abgeleiteten Klassen
8.3.2 Zugriffsrechte auf die Elemente von Basisklassen
8.3.3 Die Bedeutung von Elementnamen in einer Klassenhierarchie
8.3.4 using-Deklarationen in abgeleiteten Klassen ⊕
8.3.5 Konstruktoren, Destruktoren und implizit erzeugte Funktionen
Aufgaben 8.3.5
1. Welche Ausgabe erhält man durch einen Aufruf der Funktion test?
class C {
int i,j;
public:
C(int x,int y): i(x),j(y)
{
Form1->Memo1->Lines->Add("Konstruktor C");
}
C(): i(0),j(0)
{
Form1->Memo1->Lines->Add("Standardkonstruktor C");
}
~C()
{
Form1->Memo1->Lines->Add("Destruktor C");
}
};
class D : public C {
int k,a,b;
C c;
public:
D(int x):C(x,17),a(x),b(0),c(x,1),k(19)
{
Form1->Memo1->Lines->Add("Konstruktor-1 D");
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
92
8 Objektorientierte Programmierung
D(int x,int y, int z):C(x,y),a(1),b(2),c(x,y),k(z)
{
Form1->Memo1->Lines->Add("Konstruktor-2 D");
}
~D()
{
Form1->Memo1->Lines->Add("Destruktor D");
}
};
class E : public D {
int m;
C c;
public:
E(int x,int y, int z):D(x,3,z),m(z)
{
Form1->Memo1->Lines->Add("Konstruktor E");
}
~E() {
Form1->Memo1->Lines->Add("Destruktor E");
}
};
void test()
{
C c(1,2);
D d(1,2,3);
E e(1,2,3);
}
2. Einen eindimensionalen Punkt kann man sich als Zahl auf einem Zahlenstrahl vorstellen. Definieren Sie analog zu
den Beispielen im Text eine Klasse C1DPunkt, die eine Zahl darstellt. Von dieser Klasse soll C2DPunkt und von
C2DPunkt soll C3DPunkt abgeleitet werden. Definieren Sie für jede dieser Klassen Konstruktoren, die alle
Koordinaten initialisieren, sowie Funktionen toStr und display wie im Text.
3. Überarbeiten Sie die Lösung von Aufgabe 8.1.5.4.
a) Definieren Sie für die Klassen CGrundstueck usw. eine Klassenhierarchie.
b) Welche dieser Klassen benötigt einen explizit definierten Copy-Konstruktor, Zuweisungsoperator und
Destruktor. Definieren Sie diese bei Bedarf.
c) Vergleichen Sie diese Hierarchie mit den Klassen aus Aufgabe 8.1.5.4.
d) Welche dieser Klassen würde einen explizit definierten Copy-Konstruktor, Zuweisungsoperator und Destruktor
benötigen, wenn Strings der Klassen string bzw. AnsiString statt nullterminierten Strings verwendet würden.
4. Manchmal hat man mehrere Möglichkeiten, verschiedene Klassen voneinander abzuleiten:
a) Da ein Quadrat eine und ein Rechteck zwei Seitenlängen hat, kann man eine Klasse für ein Rechteck von einer
Klasse für ein Quadrat ableiten und so die Seitenlänge des Quadrats im Rechteck verwenden.
b) Man kann ein Quadrat aber auch als Rechteck mit zwei gleichen Seiten betrachten. Definieren Sie eine
Basisklasse für ein Rechteck und leiten Sie von dieser eine Klasse für ein Quadrat ab, bei der im Konstruktor die
beiden Seitenlängen auf denselben Wert gesetzt werden.
c) Vergleichen Sie die Vor- und Nachteile der beiden Hierarchien.
5. Eine Klasse C soll einen explizit definierten Standardkonstruktor, einen Copy-Konstruktor, einen überladenen
Zuweisungsoperator und einen Destruktor haben. Für eine von C abgeleitete Klasse D soll keine dieser Funktionen
explizit definiert sein.
a) Welche Funktionen werden dann durch die folgenden Anweisungen aufgerufen?
D d1;
D d2=d1;
d2=d1;
b) Überprüfen Sie Ihre Vermutungen mit einer Klasse C, deren Konstruktoren bzw. deren Zuweisungsoperator eine
Meldung ausgeben, dass sie aufgerufen wurden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.3 Vererbung
93
6. Die Klassen der C++-Standardbibliothek kann man ebenso wie jede andere Klasse als Basisklasse für eigene
abgeleitete Klassen verwenden. Da die Klasse string z.B. keinen Konstruktor hat, der ein int- oder double-Argument
in einen String umwandelt, kann man eine abgeleitete Klasse mit einem solchen Konstruktor definieren. Welche
Vor- und Nachteile sind mit einer solchen Erweiterung verbunden?
8.3.6 Vererbung bei Formularen im C++Builder
8.3.7 OO Design: public Vererbung und „ist ein“-Beziehungen
8.3.8 OO Design: Komposition und „hat ein“-Beziehungen
8.3.9 Konversionen zwischen public abgeleiteten Klassen
Aufgaben 8.3.9
1. Besteht zwischen den Konzepten unter a) bis d) eine „ist ein“- oder eine „hat ein“-Beziehung? Da es oft nicht
einfach ist, sich für eine der beiden zu entscheiden, sollen Sie möglichst für beide Sichtweisen Argumente suchen.
a)
b)
c)
d)
Automobil, Motor, Räder
Katze, Hund, Tier
Fahrzeug, Landfahrzeug, Wasserfahrzeug, Automobil, Segelboot
Mitarbeiter, Abteilungsleiter, Sekretärin
2. In Aufgabe 8.3.5.4 wurde eine Klasse für ein Quadrat von einer Klasse für ein Rechteck abgeleitet und auch
umgekehrt. In Abschnitt 8.3.7 wurde gezeigt, dass die Ableitung eines Quadrats von einem Rechteck mit einer
Elementfunktion wie setze_Seitenlaengen nicht unproblematisch ist. Prüfen Sie, ob die Elementfunktionen Flaeche
und Umfang aus der Basisklasse in jeder der beiden Hierarchien auch in der abgeleiteten Klasse korrekt sind?
3. Zur Lösung der Aufgabe 8.3.5.3 wird oft diese Hierarchie vorgeschlagen:
CImmobilie
CGrundstueck
CEigentWohnung
CSchloss CGewerbeobj
CEinFamHaus
a) Zwischen welchen voneinander abgeleiteten Klassen scheint die „ist ein“-Beziehung auch für die Konzepte
gerechtfertigt zu sein?
b) Ändern Sie diese Hierarchie so ab, dass alle voneinander abgeleiteten Klassen Konzepte darstellen, zwischen
denen eine „ist ein“-Beziehung besteht?
4. Ein C2DQuadrat soll wie in Abschnitt 8.3.8 ein Quadrat mit einer Position (ein C2DPunkt) darstellen. Damit das
C2DQuadrat durch die Seitenlänge und die Position eindeutig bestimmt ist, sollen die Seiten parallel zu den Koordinatenachsen und die Position der Mittelpunkt sein.
Wenn ein C2DPunkt Funktionen wie Abstand (Abstand vom Nullpunkt) oder drehe(double Winkel) (dreht den
Punkt um den Winkel Winkel um den Nullpunkt) hat, dann sind diese nur bei einer Ableitung, aber nicht bei einer
Komposition in einem C2DQuadrat verfügbar. Betrachten Sie die Alternative zwischen einer Komposition und
einer Vererbung unter dem Gesichtspunkt, ob das sinnvoll ist.
5. Wieso gibt es keine implizite Konversion einer Basisklasse in eine abgeleitete Klasse?
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
94
8 Objektorientierte Programmierung
8.3.10 protected und private abgeleitete Klassen ⊕
8.3.11 Mehrfachvererbung und virtuelle Basisklassen
Aufgaben 8.3.11
1. Eine Klasse CKreis soll die Funktionen Flaeche, Umfang und toStr haben, und ein C2DPunkt die Funktionen Abstand und toStr. Vergleichen Sie die folgenden Design-Alternativen für eine Klasse, die einen Kreis zusammen mit
seiner Position darstellt:
a)
b)
c)
d)
Die Klasse C2DKreis soll durch Mehrfachvererbung von einem CKreis und einem C2DPunkt abgeleitet werden.
Die Klasse C2DKreis soll einen CKreis und einen C2DPunkt als Element enthalten.
Die Klasse C2DKreis soll von einem CKreis abgeleitet werden und einen C2DPunkt als Element enthalten.
Die Klasse C2DKreis soll wie in Aufgabe 8.2.2.2 den Radius und die Position des Kreises als Element des Typs
C2DPunkt enthalten.
Definieren Sie die Klassen in a) bis d) mit Konstruktoren, die wie in Aufgabe 8.2.2.2 aufgerufen werden können.
Welche dieser Alternativen ist am besten geeignet, einen Kreis mit seiner Position darzustellen?
2. a) Definieren Sie die Klassenhierarchie:
C
D3
C
D4
D5
E
b) In welcher Reihenfolge werden die Konstruktoren und Destruktoren der Basisklassen beim Erzeugen eines
Objekts der Klasse E aufgerufen?
8.4 Virtuelle Funktionen, späte Bindung und Polymorphie
8.4.1 Der statische und der dynamische Datentyp
8.4.2 Virtuelle Funktionen
8.4.3 Die interne Realisierung von virtuellen Funktionen: vptr und vtbl
Aufgabe 8.4.3
1. Überarbeiten Sie die Klassen C1DPunkt, C2DPunkt und C3DPunkt der Lösung von Aufgabe 8.3.5.2 so, dass die in
jeder Klasse definierte Funktion toStr virtuell ist. Wie in den Beispielen in diesem Abschnitt soll die Elementfunktion display nur in C1DPunkt definiert sein und toStr() ausgeben.
a) Rufen Sie display mit p1, p2 und p3 auf. Verfolgen Sie im Debugger (schrittweise Ausführung mit F7), welche
der Funktionen toStr dabei aufgerufen werden.
C1DPunkt p1(1);
C2DPunkt p2(1,2);
C3DPunkt p3(1,2,3);
b) Erweitern Sie diese Klassen um die virtuellen Funktionen
double C1DPunkt::length()
{
return fabs(x); // benötigt #include <math.h>
};
double C2DPunkt::length()
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.4 Virtuelle Funktionen, späte Bindung und Polymorphie
95
return sqrt(x*x + y*y);
};
double C3DPunkt::length()
{
return sqrt(x*x + y*y + z*z);
};
Welche Funktionen werden dann mit der globalen Funktion
void show(C1DPunkt& p)
{
Form1->Memo1->Lines->Add(FloatToStr(p.length()));
};
und p1, p2 und p3 von a) aufgerufen durch
show(p1);
show(p2);
show(p3);
2. Die Klassen C1DPunkt usw. sollen wie in Aufgabe 1 definiert sein. Sie sollen alle um eine Funktion setze erweitert
werden, die einen Punkt an die als Argument übergebene Position setzen, wie z.B.:
void C1DPunkt::setze(C1DPunkt Ziel);
void C2DPunkt::setze(C2DPunkt Ziel);
Kann man diese Funktionen virtuell definieren und so erreichen, dass immer die richtige Funktion zu einem Objekt
aufgerufen wird?
3. Überarbeiten Sie die Klassenhierarchie der Lösung von Aufgabe 8.3.5.3 mit den Klassen CImmobilie, CGrundstueck
usw.
a) Eine einzige globale Funktion display soll beliebige Objekte der Hierarchie mit ihrer Elementfunktion toStr in
ein Memo ausgeben.
b) In einer Object-Datei können auch Klassen mit virtuellen Funktionen enthalten sein. Legen Sie mit
Datei|Neu|Neu|Unit eine neue Unit an. Übertragen Sie die Definition der Basisklasse CImmobilie in die HeaderDatei und die Definitionen der Elementfunktionen in die Cpp-Datei dieser Unit. Beim Kompilieren des Projekts
wird dann eine Object-Datei erzeugt. Entfernen Sie dann mit den entsprechenden Menüoptionen unter Projekt
die Cpp-Datei aus dem Projekt und fügen Sie diesem die Object-Datei hinzu.
4. Welche Werte erhalten i und j bei den Initialisierungen:
struct C {
virtual int f(int i=1)
};
{ return i; }
struct D:public C {
virtual int f(int i=2)
};
{ return i; }
C* pc=new D;
int i=pc->f();
C* pd=new D;
int j=pd->f();
5. Damit eine Funktion in einer abgeleiteten Klasse eine gleichnamige Funktion in einer Basisklasse überschreibt,
muss die Parameterliste in beiden Funktionen identisch sein. Falls einer der Parameter einen anderen Datentyp hat,
verdeckt die Funktion in der abgeleiteten Klasse die der Basisklasse. Das gilt insbesondere auch dann, wenn der
Datentyp des Parameters eine abgeleitete Klasse des Parameters der Basisklasse ist.
Welche Probleme könnten entstehen, wenn eine Funktion f in einer abgeleiteten Klasse D eine gleichnamige
Funktion in einer Basisklasse C überschreiben würde und ein Parameter von D::f eine abgeleitete Klasse des
entsprechenden Parameters von C::f ist? Sie können dazu die folgenden Klassen und die Funktion g verwenden:
struct C {
virtual void f(C& c) { };
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
96
8 Objektorientierte Programmierung
struct D : public C {
int e;
void f(D& d) { d.e=0; };
};
void g(C& x, C& y)
{
x.f(y);
};
8.4.4 OO-Design: Der Einsatzbereich von virtuellen Funktionen
8.4.5 Komposition und private Mehrfachvererbung ⊕
8.4.6 Virtuelle Konstruktoren und Destruktoren
8.4.7 Virtuelle Funktionen in Konstruktoren und Destruktoren ⊕
8.4.8 Virtuelle Funktionen und Erweiterbarkeit
8.4.9 Rein virtuelle Funktionen und abstrakte Klassen
8.4.10 OO-Design: Virtuelle Funktionen und abstrakte Basisklassen
Aufgabe 8.4.10
1. In Aufgabe 8.4.4.1 wurde gezeigt, dass sowohl die Ableitung eines Quadrats von einem Rechteck als auch die eines
Rechtecks von einem Quadrat problematisch sein kann. Entwerfen Sie eine Hierarchie für diese Klassen, die diese
Probleme vermeidet. In dieser Hierarchie sollen die Funktionen Flaeche, Umfang und toStr zur Verfügung stehen.
2. Ein Programm soll Zeichnungen mit Geraden, Kreisen, Rechtecken usw. verwalten und zeichnen können. Für diese
Figuren sollen die Klassen CGerade, CKreis und CRechteck entworfen werden.
a) Alle diese Klassen sollen die Funktionen zeichne und toStr haben, die eine Figur auf einem TImage zeichnen
bzw. als String darstellen.
b) Eine Zeichnung mit solchen Figuren soll durch eine Klasse CZeichnung dargestellt werden, in der die
verschiedenen Figuren in einem Container (z.B. einem Array oder einem vector) enthalten sind. Ihre Elementfunktion zeichne soll alle Figuren der Zeichnung zeichnen. Falls das Zeichenprogramm später einmal um
weitere Figuren erweitert wird, sollen auch diese ohne eine Änderung des Quelltextes der Klasse CZeichnung
gezeichnet werden können. Testen Sie diese Funktionen mit einer einfachen Zeichnung.
c) Damit eine Zeichnung in einer Datei gespeichert werden kann, sollen die Klassen um eine Funktion write
erweitert werden. Diese Funktion soll bei einer Figur die Figur und bei einer Zeichnung alle Figuren der
Zeichnung in einen ofstream schreiben. Damit man beim Lesen der Datei erkennen kann, zu welcher Figur die
Daten gehören, soll vor den Daten eines Objekts der Name der Figur stehen (z.B. im Klartext „Gerade“, „Kreis“
usw.).
d) Eine mit den Funktionen von c) angelegte Datei soll mit einer Funktion LeseZeichnung der Klasse CZeichnung
gelesen werden. Dabei muss immer zuerst der Name der Figur gelesen werden. Danach können die Daten zur
Figur gelesen werden. Die entsprechende Figur kann mit einem Konstruktor erzeugt werden, der die Daten aus
der Datei liest.
Entwerfen Sie zu diesen Klassen eine Hierarchie, so dass das Programm später auch noch um weitere Figuren
erweitert werden kann.
3. Für welche der Klassen in a) bis c) ist ein virtueller Destruktor notwendig? Ergänzen Sie die Klassen
gegebenenfalls:
a) die Klassen CKreis, CQuadrat und CRechteck aus Aufgabe 8.1.5.3.
b) die Klassen CGrundstueck, CEigentumswohnung, CEinfamilienhaus und CGewerbeobjekt aus Aufgabe 8.4.3.2.
c) die Klasse MeinString.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.4 Virtuelle Funktionen, späte Bindung und Polymorphie
97
8.4.11 Protokollklassen und Programmgerüste
Aufgabe 8.4.11
1. Ergänzen Sie das Programm aus Aufgabe 8.4.10.2 um die folgenden Funktionen:
a) Die Funktion loesche soll eine auf einem TImage gezeichnete Figur dadurch löschen, dass sie diese in der
Hintergrundfarbe zeichnet.
b) Die Funktion verschiebe_um(const C2DPunkt& delta) soll eine Figur zunächst löschen und dann an der Position
neu zeichnen, die sich aus der Verschiebung um die Koordinaten delta.x und delta.y ergibt. Mit einer
gleichnamigen Funktion soll auch eine Zeichnung verschoben werden können.
2. Eine Bibliothek soll Funktionen zur Datenübertragung über verschiedene Protokolle (z.B. ISDN, Analogmodems,
UMTS usw.) zur Verfügung stellen. Bei jedem Protokoll soll zunächst eine Verbindung hergestellt werden. Dann
sollen die Daten übertragen und anschließend die Verbindung beendet werden. Skizzieren Sie eine geeignete
Programmstruktur, die sich leicht auf zukünftige Protokolle erweitern lässt.
8.4.12 Muster (Patterns)
Aufgabe 8.4.12
Gamma illustriert das Muster Abstract Factory am Beispiel einer Bibliothek, die Elemente für verschiedene grafische
Benutzeroberflächen (z.B. MS-Windows, XWindow, MacWindows) zur Verfügung stellen soll. Eine Anwendung soll
mit ihr Programme für verschiedene Benutzeroberflächen erzeugen können.
Die abstrakte Factory wird dabei durch eine Klasse GUIFactory dargestellt:
class GUIFactory {
public:
virtual Button* CreateButton()=0;
virtual Scrollbar* CreateScrollbar()=0;
~
virtual CFileHandler() {}
};
Diese enthält die Schnittstelle, die abstrakte Produkte wie Button, Scrollbar usw. konstruiert. Von dieser sind konkrete
Factorys (MSWindowsFactory, XWindowFactory usw.) abgeleitet, die jedes konkrete Produkt (MSWindowsButton,
MSWindowsScrollbar usw.) für jede Benutzeroberfläche erzeugen.
Eine Anwendung soll dann allein über die abstrakten Produkte der abstrakten Factory definiert werden können:
void CreateAppWindow(GUIFactory* guiFactory)
{//verwendet nur abstrakte Factory und abstrakte Produkte
Button* b=guiFactory->CreateButton();
b->OnClick();
Scrollbar* s=guiFactory->CreateScrollbar();
}
Das Argument für den Parameter guiFactory legt dann fest, für welche Benutzeroberfläche die Anwendung erzeugt
wird:
CreateAppWindow(new MSWindowsFactory);
// bzw. CreateAppWindow(new XWindowFactory);
Simulieren Sie eine solche Architektur, indem Sie in den einzelnen Funktionen entsprechende Meldungen ausgeben.
8.4.13 UML-Diagramme für Vererbung und Komposition
8.4.14 Zeiger auf Klassenelemente ⊕
Aufgabe 8.4.14
Definieren Sie eine Klassenhierarchie mit einer Basisklasse und zwei davon abgeleiteten Klassen, ähnlich wie die
Klassen CBasisklasse, CBestellung und CLagerbestand im Text. Alle Elementfunktionen der Basisklasse sollen rein
virtuell sein, denselben Datentyp haben und in den abgeleiteten Klassen überschrieben werden. Bei ihrem Aufruf sollen
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
98
8 Objektorientierte Programmierung
sie eine Meldung ausgeben, aus der hervorgeht, welche Funktion aufgerufen wurde und zu welcher Klasse sie gehört. In
einer Funktion wie dispatch soll eine dieser Funktionen über ein Array von Zeigern auf Elementfunktionen aufgerufen
werden.
Rufen Sie dispatch für alle Kombinationen von Klassen und Elementfunktionen auf. Überprüfen Sie, ob so auch
tatsächlich die erwarteten Funktionen aufgerufen werden.
8.5 Laufzeit-Typinformationen
8.5.1 Typinformationen mit dem Operator typeid ⊕
8.5.2 Typkonversionen mit dynamic_cast ⊕
8.5.3 Anwendungen von Laufzeit-Typinformationen ⊕
8.5.4 static_cast mit Klassen ⊕
8.5.5 Laufzeit-Typinformationen für die Klassen der VCL ⊕
Aufgaben 8.5
1. Die folgenden Teilaufgaben verwenden die Klassen:
class C {
virtual void f(){}; // damit C1 polymorph ist
} c;
class D1:public C {
} d1;
class D2:public C {
} d2;
a) Geben Sie den Wert der booleschen Variablen b1, ..., b9 an:
c=d1;
bool b1= typeid(c)==typeid(d1);
c=d2;
bool b2= typeid(c)==typeid(d2);
D1* pd1=&d1;
D2* pd2=&d2;
C* pc=pd1;
bool b3= typeid(*pc)==typeid(d1);
bool b4= typeid(pc)==typeid(pd1);
pc=&c;
bool b5= typeid(*pc)==typeid(d1);
C& rc=c;
bool b6= typeid(rc)==typeid(d1);
bool b7= typeid(c)==typeid(d1);
rc=d1;
bool b8= typeid(rc)==typeid(d1);
C& rc1=d1;
bool b9= typeid(rc1)==typeid(d1);
b) Welche Ergebnisse würde man in Aufgabe a) erhalten, wenn die Funktion f in der Klasse C nicht virtuell wäre?
c) Zusätzlich zu den Klassen von oben sollen die Klassen E1 und E2 sowie die Zeiger pd1 usw. definiert sein:
class E1:public D2 {
} e1;
class E2:public D2 {
} e2;
D1* pd1=&d1;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
8.5 Laufzeit-Typinformationen
99
D2* pd2=&d2;
E1* pe1=&e1;
E2* pe2=&e2;
C* pc=&c;
Geben Sie an, ob die Zeiger p1, ..., p4 nach den folgenden Anweisungen den Wert 0 (Null) oder einen anderen
Wert haben:
D2* p1=
pc=pd1;
D2* p2=
pc=pe1;
D2* p3=
pc=pe2;
D2* p4=
dynamic_cast<D2*>(pc);
dynamic_cast<D2*>(pc);
dynamic_cast<D2*>(pc);
dynamic_cast<D2*>(pc);
d) Welche der folgenden dynamic_cast-Ausdrücke lösen eine Exception aus?
C&
C&
C&
C&
C&
D2
D2
D2
D2
D2
rc=c;
rd1=d1;
rd2=d2;
re1=e1;
re2=e2;
x1= dynamic_cast<D2&>(c);
x2= dynamic_cast<D2&>(rd1);
x3= dynamic_cast<D2&>(rd2);
x4= dynamic_cast<D2&>(re1);
x5= dynamic_cast<D2&>(re2);
rd1=e1;
D2 x6= dynamic_cast<D2&>(rd1);
2. Was wird beim Aufruf der Funktion test ausgegeben:
class C {
public:
virtual void f()
{
Form1->Memo1->Lines->Add(typeid(*this).name());
};
C() { f(); }
~C() { f(); }
};
class D:public C {
public:
D() { f(); }
~D() { f(); }
};
void test()
{
D d;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
9 Die Bibliothek der visuellen Komponenten (VCL)
9.1 Besonderheiten der VCL
9.2 Visuelle Programmierung und Properties (Eigenschaften)
9.2.1 Lesen und Schreiben von Eigenschaften
Aufgabe 9.2
Definieren Sie eine einfache Klasse mit einer Eigenschaft mit Lese- und Schreibmethoden. Verfolgen Sie im Debugger
schrittweise, zu welchen Aufrufen Zuweisungen von und an diese Eigenschaft führen.
9.2.2 Array-Properties
9.2.3 Indexangaben
9.2.4 Speicherangaben
9.2.5 Überschriebene Eigenschaften
9.3 Die Klassenhierarchie der VCL
Aufgabe 9.3
Verändert man die Größe eines Formulars während der Laufzeit eines Programms, behalten die Komponenten dieses
Fensters ihre ursprüngliche Position und Größe. Dann kann ein Button, der beim Entwurf des Programms in der Mitte
des Formulars zentriert war, völlig außerhalb der Mitte liegen.
Schreiben Sie eine Unit ResizeUnit mit einer Klasse TResize und einer Methode Resize. Beim ersten Aufruf von Resize
sollen die Positionen Top, Left, Width und Height aller Komponenten des Formulars in einem Array gespeichert
werden. Bei jedem Aufruf von Resize sollen dann die Positionsangaben aufgrund der aktuellen Größe des Fensters neu
berechnet und gesetzt werden (z.B. mit SetBounds).
Ruft man Resize beim Ereignis OnResize auf, werden die Positionen aller Komponenten bei jeder Änderung der Größe
des Formulars angepasst.
9.4 Selbst definierte Komponenten und ihre Ereignisse
Aufgabe 9.4
1. Die Reaktion auf ein Ereignis kann während der Laufzeit eines Programms dadurch verändert werden, dass man
dem Methodenzeiger für dieses Ereignis eine andere Methode zuweist.
Realisieren Sie dies mit einem Formular, das einen Button und drei RadioButtons enthält. Durch das Anklicken
eines der RadioButtons soll eine von drei Ereignisbehandlungsroutinen für das Ereignis OnClick des Buttons ausgewählt werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
9.5 MDI-Programme
101
2. Definieren Sie eine Klasse MyForm, deren Konstruktor ein Formular wie das Folgende ohne die Hilfsmittel der
visuellen Programmierung erzeugt:
Als Reaktion auf ein Anklicken des Buttons mit der Aufschrift
– Eingabe löschen soll jedes Edit-Fenster mit Clear gelöscht werden.
– Daten speichern soll von jedem Edit-Fenster das Textfeld gelesen werden, ohne die Daten weiter zu verwenden.
– Programm beenden soll das Formular durch einen Aufruf von Close geschlossen werden.
9.5 MDI-Programme
Aufgabe 9.5
1. Entwerfen Sie analog zum Beispiel mit den MDI-Memos ein Programm, das in einem MDI-Fenster ein Bild auf
einem TImage (aus der Seite „Zusätzlich“ der Komponentenpalette) anzeigt. Sie können zur Auswahl des Bildes
einen OpenPictureDialog verwenden und es mit Picture->LoadFromFile laden.
Die Menüleiste soll lediglich die Option „Datei“ mit dem Unterpunkt „Öffnen“ und die Option „Fenster“ mit dem
Unterpunkt „cascade“ anbieten.
2. Der C++Builder erzeugt nach der Auswahl von MDI-Anwendung in der Objektgalerie (unter Datei|Neu|Projekte)
den Rahmen für eine MDI-Anwendung mit einem Memo auf dem Child-Formular. Ändern Sie eine solche
Anwendung so ab, dass anstelle eines Memos eine RichEdit-Komponente (Seite Win32 der Komponentenpalette).
9.6 Klassenreferenztypen und virtuelle Konstruktoren
Aufgabe 9.6
Zeigen Sie wie in ShowBaseNames für eine Klasse (z.B. TButton) und alle ihre Basisklassen den jeweils von einem
Objekt der Klasse belegten Speicherplatz an. Sie können dazu die Funktion InstanceSize von TMetaClass verwenden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
102
9 Die Bibliothek der visuellen Komponenten (VCL)
9.7 Botschaften (Messages)
9.7.1 Die Message Queue und die Window-Prozedur
9.7.2 Botschaften für eine Anwendung
9.7.3 Die Behandlung von Botschaften in der VCL
9.7.4 Selbst definierte Message-Handler für Windows-Steuerelemente
9.7.5 Botschaften versenden
Aufgaben 9.7
1. Wie schon erwähnt wurde, stellt Windows einen großen Teil seiner Funktionen über Botschaften zur Verfügung.
Dazu gehören für Eingabefenster (TEdit und TMemo) die Botschaften
WM_COPY // kopiert den markierten Bereich in die Zwischenablage
// löscht den markierten Bereich
WM_CUT
WM_PASTE // kopiert die Zwischenablage in das Eingabefenster
Schreiben Sie Funktionen, die diese Operationen mit SendMessage realisieren. Die letzten beiden Argumente für
SendMessage sind dabei 0.
2. In einem Formular mit verschiedenen Edit-Fenstern soll nach dem Drücken der Enter-Taste automatisch das
nächste Edit-Fenster den Fokus erhalten, ohne dass extra die Tab-Taste gedrückt wird.
Die Weitergabe des Fokus kann man dadurch erreichen, dass man dem Formular die Botschaft WM_NEXTDLGCTL
sendet, wobei die Parameter wParam und lParam den Wert 0 bekommen. Diese Botschaft kann man von jedem
Edit-Fenster an das Formular senden, wenn das Ereignis OnKeyPress eintritt und die Enter-Taste (CharCode = 13)
gedrückt wird.
Noch einfacher ist es, wenn man für das Formular die Eigenschaft KeyPreview auf true setzt und diese Botschaft
beim Ereignis OnKeyPress versendet, wenn die Enter-Taste gedrückt wird.
3. Schreiben Sie eine von TEdit abgeleitete Komponente TTabEdit. Über eine boolesche Eigenschaft EnterNextDlgCtl
soll entschieden werden können, ob das Drücken der Enter-Taste den Fokus auf das nächste Dialogfeld setzt oder
nicht. Sie können sich dazu an der letzten Aufgabe orientieren.
4. Schreiben Sie eine von TEdit abgeleitete Komponente TFocusColorEdit. Diese soll automatisch eine auswählbare
Hintergrundfarbe erhalten, sobald sie den Fokus erhält. Verliert sie den Fokus, soll sie wieder die in Color festgelegte Hintergrundfarbe erhalten und ansonsten mit TEdit identisch sein. Sie können dazu die Botschaften
WM_SetFocus und WM_Killfocus abfangen. Diese werden einem Dialogelement immer dann zugesandt, wenn es
den Fokus erhält oder verliert.
5. Schreiben Sie eine von TMemo abgeleitete Komponente TResizableMemo. Wenn die linke Maustaste über dieser
Komponente gedrückt wird, soll der rechte Rand bei jeder Mausbewegung an die aktuelle x-Koordinate der Mausposition angepasst werden.
6. Schreiben Sie eine von TCustomLabel abgeleitete Komponente TColBorderLabel, die ein Label mit einem farbigen
Rand darstellt. TCustomLabel hat einen Canvas, in den man die Randlinien zeichnen kann. Die Farbe der
Randlinien soll durch eine Eigenschaft BorderColor und deren Dicke durch die Eigenschaft BorderWidth dargestellt
werden. Die boolesche Eigenschaft ShowBorder soll entscheiden, ob der farbige Rand dargestellt wird oder nicht.
Falls der Eigenschaft BlinkIntervall (Datentyp int) ein Wert größer Null zugewiesen wird, soll der Rand blinken.
Dazu kann ein Timer verwendet werden, der mit dem in BlinkIntervall angegebenen Zeitintervall tickt: Bei jedem
Tick wird der Rand neu gezeichnet.
7. Viele Zeichenprogramme verwenden zum Zeichnen von Figuren (Linien, Rechtecken, Kreisen usw.) so genannte
„Gummibandfiguren“. Dabei merkt sich das Programm beim Drücken der linken Maustaste die Anfangsposition der
zu zeichnenden Figur. Bei jeder Mausbewegung wird dann die zuvor gezeichnete Figur wieder gelöscht und bis zur
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
9.8 Die Erweiterung der Komponentenpalette
103
aktuellen Mausposition neu gezeichnet. Durch dieses Neuzeichnen bei jeder Mausbewegung entsteht der Eindruck,
dass die Figur mit einem Gummiband gezogen wird.
Das Löschen und Neuzeichnen der Figur ist besonders einfach, wenn für Canvas.Pen.Mode der Wert pmNot
gewählt wird (siehe dazu außerdem die Beschreibung von SetROP2 in der Online-Hilfe zum Windows SDK). Dieser
Modus bewirkt, dass anschließende Zeichenoperationen mit der inversen Bildschirmfarbe durchgeführt werden.
Damit bewirken zwei aufeinander folgende Zeichenoperationen, dass der ursprüngliche Zustand wieder hergestellt
wird, ohne dass man sich um die Hintergrundfarbe kümmern muss (was bei einem mehrfarbigen Hintergrund recht
mühsam sein kann).
Entwerfen Sie eine Komponente TRubberShape als Nachfolger von TImage, auf der man so Linien, Kreise und
Rechtecke zeichnen kann. Diese Komponente ist bereits ein kleines Grafikprogramm, mit dem man einfache Zeichnungen erstellen kann:
9.8 Die Erweiterung der Komponentenpalette
Aufgabe 9.8
Erweitern Sie die Komponentenpalette um die in den Aufgaben des letzten Abschnitts entwickelten Komponenten
TTabEdit
TColBorderLabel
TFocusColorEdit
TRubberShape
TResizableMemo
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
10 Templates und die STL
10.1 Generische Funktionen: Funktions-Templates
10.1.1 Die Deklaration von Funktions-Templates mit Typ-Parametern
10.1.2 Spezialisierungen von Funktions-Templates
10.1.3 Funktions-Templates mit Nicht-Typ-Parametern ⊕
10.1.4 Explizit instanziierte Funktions-Templates ⊕
10.1.5 Explizit spezialisierte und überladene Templates
10.1.6 Rekursive Funktions-Templates
Aufgabe 10.1
1. Überarbeiten Sie die Funktion Auswahlsort
void AuswahlSort(int A[],int n)
{
for (int i=0;i<n-1;i++)
{
int iMin = i;
for (int j=iMin+1;j<n;j++)
if (kleiner(A[j],A[iMin])) iMin=j;
vertausche(A[iMin],A[i]);
}
}
zu einem Funktions-Template, mit dem man Arrays beliebiger Elementtypen sortieren kann, wenn für die Elemente
der Operator < und die Funktions-Templates kleiner wie oben definiert sind. Die Funktion kleiner ist hier deswegen
notwendig, weil der Operator < für die fundamentalen Datentypen nicht überladen werden kann.
Da in der Version 5 des C++Builders allerdings überladene Funktions-Templates nicht richtig ausgewählt werden,
kann diese Lösung hier nicht getestet werden.
2. Definieren Sie mit Hilfe der Stringstream-Klassen (siehe Abschnitt 4.6.7) zwei Funktions-Templates ToStr und
StrTo, die den Wert eines Arguments in einen String umwandeln bzw. einen Wert aus einem String lesen. Der
Funktion StrTo soll der Datentyp, in den die Umwandlung erfolgen soll, als explizit spezifiziertes TemplateArgument übergeben werden.
Überzeugen Sie sich am Beispiel einer selbstdefinierten Klasse mit überladenen Ein- und Ausgabe-Operatoren (z.B.
der Klasse CBruch aus Aufgabe 6.9.3) davon, dass diese Funktionen auch mit Objekten dieser Klassen aufgerufen
werden können.
3. Definieren Sie globale Operatorfunktionen als Funktions-Templates, die
a) den Operator „!=“ auf den Operator „==“ zurückführen und
b) die Operatoren „<=“, „>“ und „>=“ auf den Operator „<“zurückführen.
Überzeugen Sie sich am Beispiel einer selbstdefinierten Klasse mit den Operatoren „==“ und „<“ (z.B. der Klasse
CBruch aus Aufgabe 6.9.2) davon, dass die anderen Operatoren über die Templates verfügbar sind.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
10.2 Generische Klassen: Klassen-Templates
105
4. In Aufgabe 6.8.1 haben die verschiedenen überladenen Funktionen Abs alle dieselben Anweisungen. Da sie sich nur
im Datentyp unterscheiden, liegt es nahe, sie als Funktions-Template zu definieren.
a) Definieren Sie die Funktion Abs als Funktions-Template.
b) Wie viele Spezialisierungen von Abs werden mit den folgenden Aufrufen erzeugt:
long L=1;
unsigned int u=1;
int i1=Abs('c');
int i3=Abs(L);
int i5=Abs(u+L);
int i7=Abs(1.5L);
int i2=Abs(1);
int i4=Abs(u);
int i6=Abs(1.5);
Überprüfen Sie Ihre Überlegungen, indem Sie in Abs den Datentyp des Parameters anzeigen. Sie können dazu
nach #include <typeinfo> die Funktion typeid(T).name() verwenden.
c) Neben dem Funktions-Template Abs soll auch noch die Funktion Abs definiert sein:
int Abs(int x)
{
if (x<0) return -x;
else return x;
}
template<> double Abs<double>(double x)
{
if (x<0) return -x;
else return x;
}
Welche dieser Funktionen bzw. welche Spezialisierungen der Funktions-Templates werden dann durch die
Aufrufe von Abs in b) aufgerufen?
10.2 Generische Klassen: Klassen-Templates
10.2.1 Die Deklaration von Klassen-Templates mit Typ-Parametern
10.2.2 Spezialisierungen von Klassen-Templates
10.2.3 Templates mit Nicht-Typ-Parametern ⊕
10.2.4 Explizit instanziierte Klassen-Templates ⊕
10.2.5 Partielle und vollständige Spezialisierungen ⊕
10.2.6 Elemente und friend-Funktionen von Klassen-Templates ⊕
10.2.7 Ableitungen von Templates ⊕
10.2.8 Exportierte Templates
10.2.9 UML-Diagramme für parametrisierte Klassen ⊕
Aufgabe 10.2
1. Diese Aufgabe ist lediglich als Übung zu Templates gedacht. Die Lösung ist der Container-Klasse list aus der
Standardbibliothek in vielerlei Hinsicht unterlegen.
Überarbeiten Sie die Klasse MeineListe zu einem Template, so dass Werte beliebiger Datentypen in einer solchen
Liste gespeichert werden können:
class MeineListe {
typedef int T;
// T: der Datentyp der "Nutzdaten"
struct list_node {// Knoten der verketteten Liste
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
106
10 Templates und die STL
T data;
list_node* next;
};
list_node *first,*last;
public:
MeineListe():first(0),last(0) {};
~MeineListe()
{ // Destruktor gibt alles wieder frei
while (first!=0)
{
list_node* tmp = first;
first = first->next;
delete tmp;
}
};
void insert(T data) // Erzeugt einen neuen Knoten
{ // der Liste und fügt diesen nach last ein.
// last zeigt anschließend auf das letzte und
// first auf das erste Element der Liste.
list_node* tmp=new list_node;
tmp->data = data;
tmp->next = 0;
if (last == 0) first = tmp;
else last->next = tmp;
last = tmp;
}
void show()
{
for (list_node* tmp=first; tmp!=0; tmp=tmp->next)
Form1->Memo1->Lines->Add(tmp->data);
}
};
2. Definieren Sie ein Klassen-Template für ein Array, das im Wesentlichen wie ein gewöhnliches Array verwendet
werden kann. Beim Zugriff auf ein Element mit dem Indexoperator [] soll eine Exception ausgelöst werden, wenn
der Index nicht im Bereich der definierten Elemente liegt. Die Anzahl der Elemente eines solchen Arrays soll als
Template-Parameter des Datentyps int übergeben werden.
3. Bei einem Klassen-Template müssen die Datentypen der Template-Argumente immer explizit angegeben werden.
Deshalb ist es mit etwas viel Schreibaufwand verbunden, mit dem Klassen-Template pair der Standardbibliothek ein
Objekt zu definieren, das man z.B. in einem assoziativen Container ac ablegen will:
map<string,string> ac;
ac.insert(pair<string,string>("Daniel","13.11.79"));
Bei einem Funktions-Template werden die Datentypen der Template-Argumente dagegen aus den Datentypen der
Funktionsargumente abgeleitet. Schreiben Sie ein Funktions-Template make_pair, das ein pair aus den Funktionsargumenten erzeugt und das so verwendet werden kann:
ac.insert(make_pair("Daniel","13.11.79"));
4. Der Aufruf der Funktionen read und write der Stream-Klassen fstream usw. ist mit etwas viel Schreibaufwand
verbunden, da man die Daten nach char* konvertieren und außerdem die Anzahl der zu schreibenden Bytes
angeben muss:
f.write((char*)&x,sizeof(x));
f.read((char*)&x,sizeof(x));
Definieren Sie ein Klassen-Template binary_fstream, das wie in test_binary_fstream verwendet werden kann. Die
Elementfunktionen read und write sollen immer eine Variable des Datentyps des Template-Parameters lesen oder
schreiben. Außer diesen Funktionen sollen auch alle Funktionen aus einem fstream in einem binary_fstream
verfügbar sein. Der Konstruktor soll eine Datei immer als Binärdatei zum Lesen und Schreiben öffnen.
void test_binary_fstream()
{
binary_fstream<int> f("c:\\test\\bin");
for (int i=0; i<10; i++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
10.3 Funktionsobjekte in der STL
107
f.write(i);
}
5. Das in Abschnitt 5.10.10 beschriebene Klassen-Template auto_ptr der Standardbibliothek enthält einen Zeiger auf
ein Objekt. Der Destruktor einer aus diesem Klassen-Template erzeugten Klasse ruft für diesen Zeiger delete auf.
Dadurch wird beim Verlassen eines Blocks, in dem ein Objekt einer solchen Klasse definiert wurde, immer der
Destruktor für das Objekt aufgerufen, auf das dieser Zeiger zeigt. Deshalb wird in der Funktion g auch dann der mit
new int angelegte Speicherplatz wieder freigegeben, wenn beim Aufruf von f eine Exception auftritt:
void g()
{ // T ist ein Datentyp
auto_ptr<T> p(new T); // Mit T* p=new T Speicherleck,
f();
// falls Exception beim Aufruf f()
}
Wäre p hier kein auto_ptr, sondern ein gewöhnlicher Zeiger, hätte das ein Speicherleck zur Folge, wenn beim
Aufruf von f eine Exception auftritt.
a) Definieren Sie ein Klassen-Template MeinAutoPtr, das wie in der Funktion g verwendet werden kann. Dieses
Template soll einen Zeiger enthalten, der auf das Element zeigt, mit dem es initialisiert wird. Die Funktion get
soll diesen Zeiger als Funktionswert liefern. Im Destruktor soll der Speicherbereich wieder freigegeben werden,
auf den der Zeiger zeigt.
b) Bei der Zuweisung eines Objekts y an ein Objekt x der Klasse auto_ptr sowie bei der Initialisierung von x mit y
erhält x den in y enthaltenen Zeiger und der Zeiger in y den Wert Null. Definieren Sie für MeinAutoPtr einen
Zuweisungsoperator und einen Copy-Konstruktor, der diese Anforderungen erfüllt.
Das Klassen-Template MeinAutoPtr ist natürlich nur eine stark vereinfachte Version von auto_ptr und soll auto_ptr
nicht ersetzen.
10.3 Funktionsobjekte in der STL
10.3.1 Der Aufrufoperator ()
10.3.2 Prädikate und arithmetische Funktionsobjekte
10.3.3 Binder, Funktionsadapter und Negatoren
Aufgabe 10.3
1. Schreiben Sie ein Funktions-Template is_sorted, das den Funktionswert true zurückgibt, wenn die Elemente im
Bereich [first,last) aufsteigend sortiert sind. Verwenden Sie dazu den STL-Algorithmus adjacent_find.
2. Die Windows-API-Funktion
int lstrcmp(LPCTSTR lpString1, // address of first null-terminated string
LPCTSTR lpString2); // address of second null-terminated string
vergleicht die beiden als Argument übergebenen nullterminierten Strings gemäß dem Zeichensatz, der sich aus den
Ländereinstellungen in der Systemsteuerung von Windows ergibt. Bei der Ländereinstellung Deutsch werden so
auch Umlaute berücksichtigt. Der Funktionswert von lstrcmp ist kleiner als Null, falls das erste Argument vor dem
zweiten kommt, gleich Null, falls beide gleich sind, und andernfalls größer als Null.
a) Definieren Sie mit lstrcmp eine Funktion, die bei sort für Compare eingesetzt werden kann, um zwei Strings des
Datentyps string zu vergleichen.
b) Verwenden Sie die Funktion von a) zum Sortieren eines vector v1 mit Elementen des Datentyps string. Damit
der Unterschied zu c) deutlich wird, sollen diese Strings auch Umlaute enthalten.
c) Sortieren Sie einen Vektor v2 mit denselben Elementen wie v1 mit der Funktion sort, wobei für Compare kein
Argument übergeben wird.
d) Erzeugen Sie aus den Elementen von v1 und v2 mit transform die Elemente eines dritten Vektors v3, die mit
dem Funktionsobjekt plus<string> aus den Strings der Vektoren v1 und v2 zusammengesetzt werden. Geben Sie
die Elemente von v3 mit for_each und einer geeigneten Funktion print (wie auf Seite 981) aus.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
108
10 Templates und die STL
3. Definieren Sie eine Klassenhierarchie, in der eine virtuelle Funktion f einer Basisklasse C in einer abgeleiteten
Klasse D und in einer von D abgeleiteten Klasse E überschrieben wird. Die Funktion f soll keine Parameter haben.
a) Ein Container v soll Zeiger auf Objekte dieser Klassenhierarchie enthalten. Überzeugen Sie sich davon, dass mit
for_each(v.begin(),v.end(),mem_fun(&C::f));
die virtuelle Funktion f für jedes Element aus dem Container aufgerufen wird. Geben Sie dazu in jeder Funktion
den Namen der Klasse aus, zu der sie gehört.
b) Erweitern Sie diese Klassenhierarchie um eine virtuelle Funktion g mit einem Parameter des Datentyps int und
rufen Sie g mit for_each auf.
c) Ein Container soll Objekte dieser Klassenhierarchie enthalten. Rufen Sie die virtuelle Funktion f mit for_each
und mem_fun_ref für jedes Element des Containers auf.
4. Ein Container soll Datensätze des Typs CName enthalten:
struct CName {
string Vorname;
string Nachname;
CName(string v, string n):Vorname(v),Nachname(n) {}
};
Geben Sie mit dem Algorithmus find_if alle Elemente des Containers aus, die einen bestimmten Nachnamen haben.
a) Verwenden Sie für das Prädikat pred in find_if eine Funktion, die von einem ersten Parameter des Typs CName
den Nachnamen mit einem zweiten Parameter des Datentyps string vergleicht und den Wert true zurückgibt,
wenn beide gleich sind. Diese Funktion kann mit bind2nd und ptr_fun in find_if verwendet werden.
b) Verwenden Sie für das Prädikat pred in find_if ein von der Klasse unary_function<CName,bool> abgeleitetes
Funktionsobjekt.
10.4 Iteratoren und die STL-Algorithmen
10.4.1 Die verschiedenen Arten von Iteratoren
10.4.2 Umkehriteratoren
10.4.3 Einfügefunktionen und Einfügeiteratoren
10.4.4 Stream-Iteratoren
10.4.5 Container-Konstruktoren mit Iteratoren
10.4.6 STL-Algorithmen für alle Elemente eines Containers
Aufgabe 10.4
1. Verwenden Sie zur Definition der folgenden Funktions-Templates die STL-Algorithmen sort und copy sowie
geeignete Iteratoren. Alle Datensätze, die in eine Datei geschrieben werden, sollen in eine eigene Zeile geschrieben
werden.
a) Das Funktions-Template WriteToFile soll die Elemente aus dem Bereich [first, last) in eine Datei schreiben,
deren Name als Parameter übergeben wird.
int a[3]={1,2,3};
WriteToFile<int>(a,a+3,"c:\\test\\s.txt");
b) Das Funktions-Template CopyFile soll eine Datei in eine zweite kopieren. Die Namen der Dateien sollen als
Parameter übergeben werden.
CopyFile<int>("c:\\test\\s.txt","c:\\test\\sc.txt");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
10.5 Die Algorithmen der STL
109
c) Das Funktions-Template SortFile soll eine Datei sortieren. Dazu sollen die Elemente in einen vector eingelesen
und dieser dann sortiert werden. Der sortierte vector soll dann in die Zieldatei geschrieben werden.
SortFile<int>("c:\\test\\s.txt","c:\\test\\ss.txt");
d) Das Funktions-Template FileIsSorted soll den booleschen Wert true zurückgeben, wenn die Datei sortiert ist,
deren Name als Parameter übergeben wird, und andernfalls false. Verwenden Sie dazu die Funktion is_sorted
aus Aufgabe 3.2.
bool b1=FileIsSorted<int>("c:\\test\\s.txt");
e) Das Funktions-Template ShowFile soll die Elemente einer Datei, deren Namen als Parameter übergeben wird,
am Bildschirm ausgeben.
f) Testen Sie die Funktions-Templates von a) bis d) mit einer sortierten, einer unsortierten, einer leeren und einer
Datei mit einem Element des Datentyps int. Geben Sie die dabei erzeugten Dateien mit ShowFile aus.
g) Testen Sie WriteToFile mit einem selbstdefinierten Datentyp, für den der Ein- und Ausgabeoperator definiert ist
(wie z.B. CBruch von 6.9.3)
2. Konstruieren Sie einen vector<int> und einen vector<CBruch> mit den Werten aus einer Datei, die in Aufgabe 1
angelegt wurde.
10.5 Die Algorithmen der STL
10.5.1 Lineares Suchen
10.5.2 Zählen
10.5.3 Der Vergleich von Bereichen
10.5.4 Suche nach Teilfolgen
10.5.5 Minimum und Maximum
Aufgabe 10.5.5
Bei den Aufgaben 1. bis 3. können Sie sich an den Ausführungen in Abschnitt 10.4.6 orientieren. Die Lösungen sollen
Algorithmen der STL verwenden.
1. Schreiben Sie die folgenden Funktions-Templates:
a) Equal soll genau dann den Wert true zurückgeben, wenn zwei als Parameter übergebene Container der STL
dieselben Elemente enthalten. Diese Container sollen verschieden sein können, z.B. ein vector und ein set.
b) Count soll die Anzahl der Elemente als Funktionswert zurückgeben, die in einem als ersten Parameter
übergebenen Container der STL enthalten sind und einen als zweiten Parameter übergebenen Wert haben.
c) CountFileElements soll die Anzahl der Elemente als Funktionswert zurückgeben, die in einer Datei, deren Name
als Parameter übergeben wird, einen ebenfalls als Parameter übergebenen Wert hat.
2. Schreiben Sie jeweils ein Funktions-Template MinValue, das den minimalen Wert
a) aus einer Datei zurückgibt, deren Name als Parameter übergeben wurde.
c) eines Containers zurückgibt, der als Parameter übergeben wurde.
b) eines Arrays zurückgibt, das als Parameter übergeben wird. Dieser Funktion soll außerdem die Anzahl der
Elemente des Arrays übergeben werden.
3. Schreiben Sie in a) bis c) jeweils ein Funktions-Template for_each_if, das für alle Werte aus einem Container, für
die ein als Argument übergebenes Prädikat den Wert true hat, eine ebenfalls als Parameter übergebene Operation f
aufruft. Die Operation soll eine Funktion oder ein Funktionsobjekt sein können.
a) for_each_if soll mit einem Container der STL aufgerufen werden können.
b) for_each_if soll mit einer Datei aufgerufen werden können, deren Name als Parameter übergeben wird.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
110
10 Templates und die STL
c) for_each_if soll mit einem Array aufgerufen werden können, das ebenso wie die Anzahl seiner Elemente als
Parameter übergeben wird.
4. Bei einem Gruppenwechsel (siehe Abschnitt 4.6.9) betrachtet man alle aufeinanderfolgenden Elemente aus einer
Sequenz als Gruppe, für die ein vorgegebenes binäres Prädikat den Wert true hat. Am Anfang und am Ende einer
jeden solchen Gruppe führt man dann bestimmte Anweisungen aus.
Schreiben Sie ein Funktions-Template, dem ein Datentyp, ein Container und ein binäres Prädikat als TemplateParameter übergeben wird. In diesem Template soll mit adjacent_find und dem Prädikat der Anfang und das Ende
einer Gruppe im Container bestimmt werden. Am Anfang und Ende einer jeden Gruppe sollen dann eine globale
Funktion aufgerufen werden. Für jedes Element in einer Gruppe soll eine weitere globale Funktion aufgerufen
werden, die das Element ausgibt.
Testen Sie dieses Funktions-Template mit einem sequentiellen Container mit int-Werten. Dabei sollen alle
aufeinander folgenden Werte, die bei einer Division durch 10 gleich sind, als Gruppe betrachtet werden. Geben Sie
am Anfang und Ende einer Gruppe eine Meldung aus, die anzeigt, dass eine Gruppe erkannt wurde. Innerhalb einer
Gruppe sollen die Werte im Container ausgegeben werden.
5. Definieren Sie die folgenden Funktions-Templates der STL selbst. Sie können sich dazu an den Algorithmen
orientieren, deren Quelltext in diesem Abschnitt gezeigt wurde. Testen Sie diese mit verschiedenen
Containerklassen, z.B. vector, list, set, string sowie mit einem Array.
template<class InputIterator, class T>
iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last,
const T& value);
template<class InputIterator, class Predicate>
iterator_traits<InputIterator>::difference_type
count_if(InputIterator first, InputIterator last,
Predicate pred);
template<class ForwardIterator>
ForwardIterator min_element(ForwardIterator first,
ForwardIterator last);
template<class ForwardIterator>
ForwardIterator adjacent_find(ForwardIterator first,
ForwardIterator last);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
10.5 Die Algorithmen der STL
111
10.5.6 Elemente vertauschen
10.5.7 Kopieren von Bereichen
10.5.8 Elemente transformieren und ersetzen
10.5.9 Elementen in einem Bereich Werte zuweisen
10.5.10 Elemente entfernen
10.5.11 Die Reihenfolge von Elementen vertauschen
10.5.12 Permutationen
10.5.13 Partitionen
10.5.14 Bereiche sortieren
10.5.15 Binäres Suchen in sortierten Bereichen
10.5.16 Mischen von sortierten Bereichen
10.5.17 Mengenoperationen auf sortierten Bereichen
10.5.18 Heap-Operationen
10.5.19 Verallgemeinerte numerische Operationen
Aufgabe 10.5.19
Verwenden Sie bei den Lösungen dieser Aufgaben Algorithmen der STL.
1. In einer Folge mit einer ungeraden Anzahl von Werten ist der Median der Wert, der nach einer Sortierung der
Folge in der Mitte steht. Bei einer geraden Anzahl von Werten ist der Median der Mittelwert der beiden mittleren
Werte. Schreiben Sie ein Funktions-Template Median, das den Median der Werte aus einem Container der STL
zurückgibt.
2. Schreiben Sie in a) bis c) jeweils ein Funktions-Template MaxElements, das die n größten Werte einer Quelle in
einen Zielbereich kopiert. Der Zielbereich soll ein beliebiger sequentieller Container der STL sein, der ebenso wie n
und die Quelle als Parameter übergeben wird. In den verschiedenen Versionen soll die Quelle sein:
a) ein Container der STL,
b) eine Datei, deren Name als Parameter übergeben wird,
c) ein Array.
3. Schreiben Sie ein Funktions-Template Remove, das die Elemente mit einem bestimmten Wert aus einem Container
der STL entfernt. Sowohl der Container als auch der Wert sollen als Parameter übergeben werden.
4. Schreiben Sie ein Funktions-Template, das zwei verschiedene sortierte Dateien zu einer einzigen sortierten Datei
zusammenmischt, deren Namen als Parameter übergeben werden.
5. Eine mit 4 Parametern des Datentyps int definierte Funktion f soll zum Testen mit allen Permutationen der Werte 1,
2, 3 und 4 aufgerufen werden. Schreiben Sie eine Funktion, die diese Aufrufe als Datei erzeugt.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
11 Verschiedenes
11.1 3D-Grafik mit OpenGL
11.1.1 Initialisierungen
11.1.2 Grafische Grundelemente: Primitive
11.1.3 Modelltransformationen
11.1.4 Vordefinierte Körper
11.1.5 Lokale Transformationen
11.1.6 Beleuchtungseffekte
11.1.7 Texturen
Aufgaben 11.1
Schreiben Sie ein Programm, das in einer Menüleiste die Optionen Scene, Lights und Texture anbietet.
1. Unter Scene sollen die folgenden Optionen angeboten werden:
a) Nach der Auswahl „coordinate system“ soll ein Koordinatenkreuz gezeichnet werden. Verwenden Sie dazu eine
Funktion Arrow, die einen Pfeil der Länge 1 vom Ursprung auf der x-Achse zeichnet. Erzeugen Sie aus drei
solchen Pfeilen dasselbe Koordinatenkreuz wie mit der Funktion CoordinateSystem von Seite 1034.
b) Ein Modell eines Wassermoleküls soll aus einer Kugel für das Sauerstoffatom und zwei Kugeln für die beiden
Wasserstoffatome bestehen. Die Wasserstoffatome sollen mit den Sauerstoffatomen durch dünne Stäbchen
(Zylinder) verbunden werden, die einen Winkel von 120 Grad einschließen.
c) In einer Funktion RotatingCubeAndCylinder sollen ein Würfel und ein Kegel gezeichnet werden. Bei
wiederholten Aufrufen dieser Funktion sollen sich diese Figuren unabhängig voneinander drehen.
d) Die Stadt wie im Beispiel auf Seite 1035 soll nicht durch eine absolute Positionierung der Quader erzeugt
werden, sondern durch einen Quader im Ursprung, der mit glTranslate an die richtige Position gesetzt wird.
e) In einer Funktion Robot soll ein einfacher Roboter gezeichnet werden, der z.B. aus einer Kugel für den Kopf und
Zylindern für den Körper und die Arme besteht. Bei wiederholten Aufrufen soll sich der Winkel der Arme zum
Körper leicht ändern, so dass der Eindruck einer kontinuierlichen Bewegung entsteht.
f) Ergänzen Sie das Solarsystem von Seite 1040 so, dass sich die Erde um eine Achse durch den Nord- und Südpol
dreht. Außerdem soll sich ein zweiter Mond um die Erde drehen.
g) Eine Funktion AnimatedPaperplanes soll einige Papierflieger (siehe Seite 1036) zeichnen. Ihre Position soll sich
zwischen zwei aufeinander folgenden Aufrufen leicht verändern, so dass der Eindruck entsteht, dass sie (z.B. auf
einer spiralförmigen Bahn) durch den Raum segeln.
2. Die jeweils dargestellte Szene soll mit den Pfeiltasten gedreht und verschoben werden können. Verwenden Sie dazu
in der Funktion FormKeyDown geeignete Transformationen.
3. Unter der Menüoption Lights soll eine Lichtquelle aktiviert und deaktiviert werden können. Falls die Lichtquelle
aktiviert ist, sollen in einem weiteren Formular die Position und Intensität der RGB-Werte des ambienten und spekulären Lichts sowie die ambienten, spekulären und diffusen Reflektionswerte der Oberflächen gesetzt werden
können (z.B. mit einem ScrollBar). Dabei sollen die diffusen und spekulären Lichtwerte jeweils gleich sein, ebenso
die ambienten und diffusen Materialwerte.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
114
11 Verschiedenes
4. Unter der Option Textures sollen die folgenden Optionen angeboten werden:
a) Ein einfaches Bild wie das Schachbrett von Seite 1047 soll als Textur geladen werden.
b) Ein Windows-Bitmap soll mit einem OpenPictureDialog als Textur geladen werden.
c) Die unter a) oder b) geladene Textur soll bei einfachen Figuren wie einem Quadrat oder einer Kugel verwendet
werden.
d) Ergänzen Sie das Solarsystem so, dass die Erde mit der unter a) oder b) gewählten Textur gezeichnet wird.
e) Schreiben Sie eine Funktion TexturedCube, die einen Würfel mit der aktuell gewählten Textur auf den
Seitenflächen zeichnet. In einem genügend großen Würfel kann man sich dann wie in einem Raum bewegen.
11.2 Win32-Funktionen zur Dateibearbeitung
11.2.1 Elementare Funktionen
11.2.2 Der gleichzeitige Zugriff von mehreren Anwendern auf eine Datei
11.2.3 Record-Locking
11.2.4 VCL-Funktionen zur Dateibearbeitung und die Klasse TFileStream
Aufgaben 11.2
Verwenden Sie hier nur die Win32-Funktionen zur Dateibearbeitung.
1. Legen Sie eine einfache Datei (z.B. mit 10 aufeinander folgenden Werten des Datentyps int) an. Lesen Sie
anschließend die Daten aus dieser Datei. Prüfen Sie nach jedem Aufruf einer Win32-Funktion, ob ein Fehler
aufgetreten ist, und geben Sie dann mit einer Funktion wie ShowLastError eine Meldung aus.
2. Geben Sie einige dieser Fehlermeldungen aus, indem Sie mit CreateFile eine Datei mit einem unzulässigen
Dateinamen öffnen oder aus einer nicht geöffneten bzw. nur zum Schreiben geöffneten Datei lesen.
3. Die Datei von Aufgabe 1 soll so geöffnet werden, dass andere Anwender sie lesen oder beschreiben können. Bieten
Sie die Möglichkeit an, einzelne Datensätze der Datei zu sperren, und prüfen Sie vor dem Lesen oder Schreiben von
Datensätzen, ob sie gesperrt ist. Testen Sie das Programm, indem sie es mehrfach starten.
11.3 Datenübertragung über die serielle Schnittstelle
11.3.1 Grundbegriffe
11.3.2 Standards für die serielle Schnittstelle: RS-232C bzw. V.24
11.3.3 Win32-Funktionen zur seriellen Kommunikation
Aufgabe 11.3
Schreiben Sie ein Programm, das Daten über ein Kabel von der seriellen Schnittstelle COM1 an die serielle
Schnittstelle COM2 sendet.
Die übertragenen Daten sollen aus einem Edit-Fenster gelesen werden. Als Reaktion auf einen Timer soll geprüft
werden, ob Daten empfangen wurden. Die empfangenen Daten sollen in einem Memo angezeigt werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
11.4 Datenbank-Komponenten der VCL
11.4 Datenbank-Komponenten der VCL
11.4.1 Tabellen und die Komponente TTable
11.4.2 Die Anzeige von Tabellen mit der Komponente DBGrid
11.4.3 Indizierte Tabellen
11.4.4 Datenbanken mit mehreren Tabellen
11.4.5 SQL-Abfragen
11.4.6 Transaktionen und Cached Updates
11.4.7 Die BDE am Beispiel von ODBC und MS Access Datenbanken
11.4.8 Visuell gestaltete Datenbank-Ausdrucke mit QuickReport
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
115
12 Lösungen
Da die Zeilen in den Quelltexten der Lösungen manchmal länger sind als eine Druckseite breit ist, kommt es
gelegentlich zu unschönen Formatierungen.
Angaben wie
// Datei: d:\Loesungen\kap-2\2.2\HaushSanU.cpp
bezeichnen die Datei mit den Quelltexten auf der CD, die im Buch enthalten ist. Auf dieser CD findet man diese
Lösungen als Projekte für die Version 5 des Borland C++Builders.
12.1 Lösungen Kapitel 2
12.1.1 Aufgabe 2.2
// Datei: d:\Loesungen\kap-2\2.2\HaushSanU.cpp
#include "HaushSanU.h"
//-------------------------------------------------------------------void __fastcall TForm1::BloeschenClick(TObject *Sender)
{
EVorname->Clear();
ENachname->Clear();
EOrdnungswidrigkeit->Clear();
EBussgeld->Clear();
}
//-------------------------------------------------------------------void __fastcall TForm1::BEndeClick(TObject *Sender)
{
Close();
}
12.1.2 Aufgabe 2.3
// Datei: d:\Loesungen\kap-2\2.3\LabelsU.cpp
#include "LabelsU.h"
//-------------------------------------------------------------------void __fastcall TForm1::BLinksAusrichtenClick(TObject *Sender)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
118
{
Label1->Caption
= "Label links";
Label1->Alignment = taLeftJustify;
}
//-------------------------------------------------------------------void __fastcall TForm1::BRechtsAusrichtenClick(TObject *Sender)
{
Label1->Caption
= "Label rechts";
Label1->Alignment = taRightJustify;
}
//-------------------------------------------------------------------void __fastcall TForm1::BUnsichtbarClick(TObject *Sender)
{
Label1->Visible = false;
}
//-------------------------------------------------------------------void __fastcall TForm1::BSichtbarClick(TObject *Sender)
{
Label1->Visible = true;
}
//-------------------------------------------------------------------void __fastcall TForm1::BLinksClick(TObject *Sender)
{
Label1->Left = 0;
}
//-------------------------------------------------------------------void __fastcall TForm1::BRechtsClick(TObject *Sender)
{
Label1->Left = Form1->ClientWidth-Label1->Width;
}
12.1.3 Aufgabe 2.4
// Datei: d:\Loesungen\kap-2\2.4\Tr1U.cpp
#include "TR1U.h"
//----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::BRechneIntClick(TObject *Sender)
{
EErgebnis->Text=IntToStr(StrToInt(EOperand1->Text)+
StrToInt(EOperand2->Text));
}
void __fastcall TForm1::BLoeschenClick(TObject *Sender)
{
EOperand1->Clear();
EOperand2->Clear();
EErgebnis->Clear();
}
//----- Aufgabe 2 -----------------------------------------------void __fastcall TForm1::BRechneFloatClick(TObject *Sender)
{
EErgebnis->Text=FloatToStr(StrToFloat(EOperand1->Text)+
StrToFloat(EOperand2->Text));
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.1 Lösungen Kapitel 2
// Datei: d:\Loesungen\kap-2\2.4\Tr4U.cpp
#include "TR4U.h"
//----- Aufgabe 3 -----------------------------------------------void __fastcall TForm1::BAddiereClick(TObject *Sender)
{
LPlus->Caption="+";
EErgebnis->Text=FloatToStr(
StrToFloat(EOperand1->Text)+StrToFloat(EOperand2->Text));
}
//-------------------------------------------------------------------void __fastcall TForm1::BSubtrahiereClick(TObject *Sender)
{
LPlus->Caption="-";
EErgebnis->Text=FloatToStr(
StrToFloat(EOperand1->Text)-StrToFloat(EOperand2->Text));
}
//-------------------------------------------------------------------void __fastcall TForm1::BMultipliziereClick(TObject *Sender)
{
LPlus->Caption="*";
EErgebnis->Text=FloatToStr(
StrToFloat(EOperand1->Text)*StrToFloat(EOperand2->Text));
}
//-------------------------------------------------------------------void __fastcall TForm1::BDividiereClick(TObject *Sender)
{
LPlus->Caption="/";
EErgebnis->Text=FloatToStr(
StrToFloat(EOperand1->Text)/StrToFloat(EOperand2->Text));
}
//-------------------------------------------------------------------void __fastcall TForm1::BLoeschenClick(TObject *Sender)
{
EOperand1->Clear();
EOperand2->Clear();
EErgebnis->Clear();
}
12.1.4 Aufgabe 2.5
// Datei: d:\Loesungen\kap-2\2.5\StringsU.cpp
#include "StringsU.h"
//-------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Add(Edit1->Text);
ListBox1->Items->Add(Edit1->Text);
ComboBox1->Items->Add(Edit1->Text);
}
//-------------------------------------------------------------------void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
Label1->Caption=ListBox1->Items->Strings[ListBox1->ItemIndex];
}
//-------------------------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
119
120
12 Lösungen
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Label2->Caption=ComboBox1->Text;
}
12.1.5 Aufgabe 2.6
// Datei: d:\Loesungen\kap-2\2.6\EventsU.cpp
#include "EventsU.h"
//-------------------------------------------------------------------void __fastcall TForm1::BTestClick(TObject *Sender)
{
EOnClick->Text="OnClick";
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestEnter(TObject *Sender)
{
EOnEnter->Text="OnEnter";
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestExit(TObject *Sender)
{
EOnExit->Text="OnExit";
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
EOnKeyDown->Text=IntToStr(Key);
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestKeyPress(TObject *Sender, char &Key)
{
EOnKeyPress->Text=Key;
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestKeyUp(TObject *Sender, WORD &Key,
TShiftState Shift)
{
EOnKeyUp->Text=Key;
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestMouseDown(TObject *Sender,TMouseButton Button,
TShiftState Shift, int X, int Y)
{
EOnMouseDown->Text="MouseDown";
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
EOnMouseUp->Text="MouseUp";
}
//-------------------------------------------------------------------void __fastcall TForm1::BTestMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
EOnMouseMove->Text=IntToStr(X)+","+IntToStr(Y);
}
//-------------------------------------------------------------------void __fastcall TForm1::BClearClick(TObject *Sender)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.1 Lösungen Kapitel 2
{
EOnClick->Clear();
EOnEnter->Clear();
EOnExit->Clear();
EOnMouseDown->Clear();
EOnMouseMove->Clear();
EOnMouseUp->Clear();
EOnKeyPress->Clear();
EOnKeyUp->Clear();
EOnKeyDown->Clear();
}
12.1.6 Aufgabe 2.7
// Datei: d:\Loesungen\kap-2\2.7\IfBspU.cpp
#include "IfBspU.h"
//-------------------------------------------------------------------void __fastcall TForm1::TestClick(TObject *Sender)
{
if (CheckBox1->Checked) Button1->Enabled=false;
else Button1->Enabled=true;
// oder
Button1->Enabled=!CheckBox1->Checked;
if (CheckBox2->Checked) Button1->Caption="Halli";
else Button1->Caption="Hallo";
if (CheckBox3->Checked) Button1->Hide();
else Button1->Show();
if (RadioButton1->Checked) Label1->Caption="extra Info";
else Label1->Caption="";
if (RadioButton2->Checked) Label1->Hide();
else Label1->Show();
}
//--------------------------------------------------------------------
12.1.7 Aufgabe 2.9
// Datei: d:\Loesungen\kap-2\2.9\AnalogRU.cpp
#include "AnalogRU.h"
/*
Die Anweisungen in den Funktionen aChange, bChange und
dChange sind alle gleich. Sobald Funktionen behandelt sind,
liegt es nahe, diese in einer einzigen Funktion zusammenzufassen.
*/
//-------------------------------------------------------------------void __fastcall TForm1::aChange(TObject *Sender)
{
if (b->Position*b->Position != a->Position*d->Position)
x->Caption = FloatToStr((0.0+b->Position - d->Position)/
(b->Position*b->Position - a->Position*d->Position));
else x->Caption = "Division durch 0";
if (b->Position != d->Position)
y->Caption = FloatToStr((0.0+b->Position - a->Position)/
(b->Position*b->Position - a->Position*d->Position));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
121
122
else y->Caption = "Division durch 0";
Edita->Text = IntToStr(a->Position);
Editb->Text = IntToStr(b->Position);
Editc->Text = IntToStr(d->Position);
}
//-------------------------------------------------------------------void __fastcall TForm1::bChange(TObject *Sender)
{
if (b->Position*b->Position != a->Position*d->Position)
x->Caption = FloatToStr((0.0+b->Position - d->Position)/
(b->Position*b->Position - a->Position*d->Position));
else x->Caption = "Division durch 0";
if (b->Position != d->Position)
y->Caption = FloatToStr((0.0+b->Position - a->Position)/
(b->Position*b->Position - a->Position*d->Position));
else y->Caption = "Division durch 0";
Edita->Text = IntToStr(a->Position);
Editb->Text = IntToStr(b->Position);
Editc->Text = IntToStr(d->Position);
}
//-------------------------------------------------------------------void __fastcall TForm1::dChange(TObject *Sender)
{
if (b->Position*b->Position != a->Position*d->Position)
x->Caption = FloatToStr((0.0+b->Position - d->Position)/
(b->Position*b->Position - a->Position*d->Position));
else x->Caption = "Division durch 0";
if (b->Position != d->Position)
y->Caption = FloatToStr((0.0+b->Position - a->Position)/
(b->Position*b->Position - a->Position*d->Position));
else y->Caption = "Division durch 0";
Edita->Text = IntToStr(a->Position);
Editb->Text = IntToStr(b->Position);
Editc->Text = IntToStr(d->Position);
}
12.1.8 Aufgabe 2.11
// Datei: d:\Loesungen\kap-2\2.11\MenuU.cpp
#include "MenuU.h"
//-------------------------------------------------------------------void __fastcall TForm1::Farbe1Click(TObject *Sender)
{
if (ColorDialog1->Execute())
Memo1->Brush->Color = ColorDialog1->Color;
}
//-------------------------------------------------------------------void __fastcall TForm1::Schriftart1Click(TObject *Sender)
{
if (FontDialog1->Execute())
Memo1->Font->Assign(FontDialog1->Font);
}
//-------------------------------------------------------------------void __fastcall TForm1::Dateiffnen1Click(TObject *Sender)
{
OpenDialog1->Filter = "Textdateien|*.TXT";
if (OpenDialog1->Execute())
Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
//-------------------------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.1 Lösungen Kapitel 2
123
void __fastcall TForm1::Allesmarkieren1Click(TObject *Sender)
{
Memo1->SelectAll();
}
//-------------------------------------------------------------------void __fastcall TForm1::Ausschneiden1Click(TObject *Sender)
{
Memo1->CutToClipboard();
}
//-------------------------------------------------------------------void __fastcall TForm1::Kopieren1Click(TObject *Sender)
{
Memo1->CopyToClipboard();
}
//-------------------------------------------------------------------void __fastcall TForm1::Dateispeichern1Click(TObject *Sender)
{
if (SaveDialog1->Execute())
Memo1->Lines->SaveToFile(OpenDialog1->FileName);
}
//-------------------------------------------------------------------void __fastcall TForm1::Suchen1Click(TObject *Sender)
{
FindDialog1->Execute();
}
//-------------------------------------------------------------------void __fastcall TForm1::SuchenundErsetzen1Click(TObject *Sender)
{
ReplaceDialog1->Execute();
}
//-------------------------------------------------------------------void __fastcall TForm1::Drucken1Click(TObject *Sender)
{
if (PrintDialog1->Execute()) Form1->Print();
}
//-------------------------------------------------------------------void __fastcall TForm1::Druckereinrichten1Click(TObject *Sender)
{
PrinterSetupDialog1->Execute();
}
12.1.9 Aufgabe 2.12
// Datei: d:\Loesungen\kap-2\2.12\Form1U.cpp
#include "Form1U.h"
#include "Unit2.h" // Manuell einfügen oder mit 'Datei|Unit Header einschließen'
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form2->ShowModal();
}
// Datei: d:\Loesungen\kap-2\2.12\Form2U.cpp
#include "Form2U.h"
TForm2 *Form2;
__fastcall TForm2::TForm2(TComponent* Owner)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
124
12 Lösungen
: TForm(Owner)
{
}
// Die Dialoge werden mit Datei|Neu|Dialoge dem Projekt hinzugefügt.
// Manuell einfügen oder mit 'Datei|Unit Header einschließen':
#include "OKHelpDlg.h"
#include "PasswDlg.h"
#include "StdDlg.h"
void __fastcall TForm2::Button1Click(TObject *Sender)
{
OKHelpBottomDlg->ShowModal();
}
void __fastcall TForm2::Button2Click(TObject *Sender)
{
PasswordDlg->ShowModal();
}
void __fastcall TForm2::Button3Click(TObject *Sender)
{
OKBottomDlg1->ShowModal();
}
// Datei: d:\Loesungen\kap-2\2.12\OKHelpDlg.h
#ifndef OKHelpDlgH
#define OKHelpDlgH
#include <OKCANCL1.h>
#include <vcl\ExtCtrls.hpp>
#include <vcl\Buttons.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Controls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\Graphics.hpp>
#include <vcl\Classes.hpp>
#include <vcl\SysUtils.hpp>
#include <vcl\Windows.hpp>
#include <vcl\System.hpp>
class TOKHelpBottomDlg : public TOKBottomDlg
{
__published:
TButton *HelpBtn;
void __fastcall HelpBtnClick(TObject *Sender);
private:
public:
virtual __fastcall TOKHelpBottomDlg(TComponent* AOwner);
};
extern PACKAGE TOKHelpBottomDlg *OKHelpBottomDlg;
#endif
// Datei: d:\Loesungen\kap-2\2.12\StdDlg.h
#ifndef StdDlgH
#define StdDlgH
#include <vcl\System.hpp>
#include <vcl\Windows.hpp>
#include <vcl\SysUtils.hpp>
#include <vcl\Classes.hpp>
#include <vcl\Graphics.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\Controls.hpp>
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.1 Lösungen Kapitel 2
#include <vcl\Buttons.hpp>
#include <vcl\ExtCtrls.hpp>
class TOKBottomDlg1 : public TForm
{
__published:
TButton *OKBtn;
TButton *CancelBtn;
TBevel *Bevel1;
private:
public:
virtual __fastcall TOKBottomDlg1(TComponent* AOwner);
};
extern PACKAGE TOKBottomDlg1 *OKBottomDlg1;
#endif
// Datei: d:\Loesungen\kap-2\2.12\PasswDlg.h
#ifndef PasswDlgH
#define PasswDlgH
#include <vcl\Buttons.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Controls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\Graphics.hpp>
#include <vcl\Classes.hpp>
#include <vcl\SysUtils.hpp>
#include <vcl\Windows.hpp>
#include <vcl\System.hpp>
class TPasswordDlg : public TForm
{
__published:
TLabel *Label1;
TEdit *Password;
TButton *OKBtn;
TButton *CancelBtn;
private:
public:
virtual __fastcall TPasswordDlg(TComponent* AOwner);
};
extern PACKAGE TPasswordDlg *PasswordDlg;
#endif
12.1.10 Aufgabe 2.13
// Datei: d:\Loesungen\kap-2\2.13\DrawU.cpp
#include "DrawU.h"
//-------------------------------------------------------------------void __fastcall TForm1::Pen1Click(TObject *Sender)
{
if (ColorDialog1->Execute())
Image1->Canvas->Pen->Color=ColorDialog1->Color;
}
//-------------------------------------------------------------------void __fastcall TForm1::Brush1Click(TObject *Sender)
{
if (ColorDialog1->Execute())
Image1->Canvas->Brush->Color=ColorDialog1->Color;
}
//-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{ // Hier sind alle relevanten Einstellungen zusammengefaßt.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
125
126
12 Lösungen
// Man kann diese Werte ebenso im Objektinspektor setzen.
Panel1->Align =alTop;
SpeedButtonLine->GroupIndex=1;
SpeedButtonRect->GroupIndex=1;
SpeedButtonCircle->GroupIndex=1;
}
//-------------------------------------------------------------------void __fastcall TForm1::Image1MouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (SpeedButtonLine->Down)
Image1->Canvas->MoveTo(X,Y);
}
//-------------------------------------------------------------------void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (SpeedButtonLine->Down)
Image1->Canvas->LineTo(X,Y);
else if (SpeedButtonRect->Down)
Image1->Canvas->Rectangle(X,Y,X+10,Y+10);
else if (SpeedButtonCircle->Down)
Image1->Canvas->Ellipse(X,Y,X+10,Y+10);
}
//-------------------------------------------------------------------void __fastcall TForm1::ffnen1Click(TObject *Sender)
{
if (OpenDialog1->Execute())
Image1->Picture->LoadFromFile(OpenDialog1->FileName);
}
12.1.11 Aufgabe 2.14
// Datei: d:\Loesungen\kap-2\2.14\ABOUT.CPP
#include "About.h"
TAboutBox *AboutBox;
__fastcall TAboutBox::TAboutBox(TComponent *Owner)
: TForm(Owner)
{
}
// Datei: d:\Loesungen\kap-2\2.14\Logo.cpp
#include "About.h"
#include "Logo.h"
#include "LogoStrs.h"
#include "MAPI.hpp"
TLogoAppForm *LogoAppForm;
template class TOpenOptions;
__fastcall TLogoAppForm::TLogoAppForm(TComponent *Owner)
: TForm(Owner)
{
}
void __fastcall TLogoAppForm::FormCreate(TObject *Sender)
{
Application->OnHint = ShowHint;
}
void __fastcall TLogoAppForm::FileNew(TObject *Sender)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.1 Lösungen Kapitel 2
127
FFileName = LoadStr(sUntitled);
RichEdit1->Lines->Clear();
RichEdit1->Modified = false;
}
void __fastcall TLogoAppForm::FileOpen(TObject *Sender)
{
if (OpenDialog->Execute())
{
RichEdit1->Lines->LoadFromFile(OpenDialog->FileName);
FFileName = OpenDialog->FileName;
RichEdit1->SetFocus();
RichEdit1->Modified = false;
RichEdit1->ReadOnly = OpenDialog->Options.Contains(ofReadOnly);
}
}
void __fastcall TLogoAppForm::FileSave(TObject *Sender)
{
if (FFileName == LoadStr(sUntitled))
FileSaveAs(Sender);
else
{
RichEdit1->Lines->SaveToFile(FFileName);
RichEdit1->Modified = false;
}
}
void __fastcall TLogoAppForm::FileSaveAs(TObject *Sender)
{
String str;
TVarRec vrs[1];
if (SaveDialog->Execute())
{
if (FileExists(SaveDialog->FileName))
{
str = FmtLoadStr(sOverwrite, OPENARRAY(TVarRec, (SaveDialog->FileName)));
if (MessageDlg(str, mtConfirmation, TMsgDlgButtons() << mbYes << mbNo <<
mbCancel, 0) != IDYES)
return;
}
RichEdit1->Lines->SaveToFile(SaveDialog->FileName);
FFileName = SaveDialog->FileName;
RichEdit1->Modified = false;
}
}
void __fastcall TLogoAppForm::FileSend(TObject *Sender)
{
TMapiMessage MapiMessage;
Cardinal MError;
MapiMessage.ulReserved = 0;
MapiMessage.lpszSubject = NULL;
MapiMessage.lpszNoteText = RichEdit1->Lines->Text.c_str();
MapiMessage.lpszMessageType = NULL;
MapiMessage.lpszDateReceived = NULL;
MapiMessage.lpszConversationID = NULL;
MapiMessage.flFlags = 0;
MapiMessage.lpOriginator = NULL;
MapiMessage.nRecipCount = 0;
MapiMessage.lpRecips = NULL;
MapiMessage.nFileCount = 0;
MapiMessage.lpFiles = NULL;
MError = MapiSendMail(0, 0, MapiMessage, MAPI_DIALOG | MAPI_LOGON_UI |
MAPI_NEW_SESSION, 0);
if (MError)
MessageDlg(LoadStr(sSendError), mtError, TMsgDlgButtons() << mbOK, 0);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
128
12 Lösungen
void __fastcall TLogoAppForm::FileExit(TObject *Sender)
{
Close();
}
void __fastcall TLogoAppForm::About(TObject *Sender)
{
AboutBox->ShowModal();
}
void __fastcall TLogoAppForm::ShowHint(TObject *Sender)
{
StatusBar->SimpleText = Application->Hint;
}
void __fastcall TLogoAppForm::Schriftart1Click(TObject *Sender)
{
FontDialog1->Font->Assign(RichEdit1->SelAttributes);
if (FontDialog1->Execute())
RichEdit1->SelAttributes->Assign(FontDialog1->Font);
}
// Datei: d:\Loesungen\kap-2\2.14\logoapp.cpp
USEFORM("LogoMain.cpp", LogoAppForm);
USERC("LogoStrs.rc");
USERES("LogoApp.res");
USEFORM("about.cpp", AboutBox);
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
Application->Initialize();
Application->CreateForm(__classid(TLogoAppForm), &LogoAppForm);
Application->CreateForm(__classid(TAboutBox), &AboutBox);
Application->Run();
return 0;
}
// Datei: d:\Loesungen\kap-2\2.14\LogoMain.cpp
#include
#include
#include
#include
"About.h"
"LogoMain.h"
"LogoStrs.h"
"MAPI.hpp"
//----- Aufgabe 2 -----------------------------------------------TLogoAppForm *LogoAppForm;
__fastcall TLogoAppForm::TLogoAppForm(TComponent *Owner)
: TForm(Owner)
{
FileNew1->Execute(); /* sets the default file name and clears the RichEdit
Control */
}
void __fastcall TLogoAppForm::FileNew1Execute(TObject *Sender)
{
FFileName = LoadStr(sUntitled);
RichEdit1->Lines->Clear();
RichEdit1->Modified = false;
}
void __fastcall TLogoAppForm::FileOpen1Execute(TObject *Sender)
{
if (OpenDialog->Execute())
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.1 Lösungen Kapitel 2
129
RichEdit1->Lines->LoadFromFile(OpenDialog->FileName);
FFileName = OpenDialog->FileName;
RichEdit1->SetFocus();
RichEdit1->Modified = false;
RichEdit1->ReadOnly = OpenDialog->Options.Contains(ofReadOnly);
}
}
void __fastcall TLogoAppForm::FileSave1Execute(TObject *Sender)
{
if (FFileName == LoadStr(sUntitled))
FileSaveAs1Execute(Sender);
else
{
RichEdit1->Lines->SaveToFile(FFileName);
RichEdit1->Modified = false;
}
}
void __fastcall TLogoAppForm::FileSaveAs1Execute(TObject *Sender)
{
String str;
TVarRec vrs[1];
if (SaveDialog->Execute())
{
if (FileExists(SaveDialog->FileName))
{
str = FmtLoadStr(sOverwrite, OPENARRAY(TVarRec, (SaveDialog->FileName)));
if (MessageDlg(str, mtConfirmation, TMsgDlgButtons() << mbYes << mbNo <<
mbCancel, 0) != IDYES)
return;
}
RichEdit1->Lines->SaveToFile(SaveDialog->FileName);
FFileName = SaveDialog->FileName;
RichEdit1->Modified = false;
}
}
void __fastcall TLogoAppForm::FileSend1Execute(TObject *Sender)
{
TMapiMessage MapiMessage;
Cardinal MError;
MapiMessage.ulReserved = 0;
MapiMessage.lpszSubject = NULL;
MapiMessage.lpszNoteText = RichEdit1->Lines->Text.c_str();
MapiMessage.lpszMessageType = NULL;
MapiMessage.lpszDateReceived = NULL;
MapiMessage.lpszConversationID = NULL;
MapiMessage.flFlags = 0;
MapiMessage.lpOriginator = NULL;
MapiMessage.nRecipCount = 0;
MapiMessage.lpRecips = NULL;
MapiMessage.nFileCount = 0;
MapiMessage.lpFiles = NULL;
MError = MapiSendMail(0, reinterpret_cast<unsigned int>(Application->Handle),
MapiMessage, MAPI_DIALOG | MAPI_LOGON_UI | MAPI_NEW_SESSION, 0);
if (MError)
MessageDlg(LoadStr(sSendError), mtError, TMsgDlgButtons() << mbOK, 0);
}
void __fastcall TLogoAppForm::FileExit1Execute(TObject *Sender)
{
Close();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
130
12 Lösungen
void __fastcall TLogoAppForm::HelpAbout1Execute(TObject *Sender)
{
AboutBox->ShowModal();
}
void __fastcall TLogoAppForm::Schriftart1Click(TObject *Sender)
{
FontDialog1->Font->Assign(RichEdit1->SelAttributes);
if (FontDialog1->Execute())
RichEdit1->SelAttributes->Assign(FontDialog1->Font);
}
// Datei: d:\Loesungen\kap-2\2.14\PageCtlU.cpp
#include "PageCtlU.h"
//----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
RadioButton1->Caption="Icon";
RadioButton2->Caption="List";
RadioButton3->Caption="Small";
RadioButton4->Caption="Report";
GroupBox1->Caption="Ansicht";
StatusBar1->SimplePanel=true;
PageControl1->Align=alClient;
}
// a)
void __fastcall TForm1::TabSheet1MouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
StatusBar1->SimpleText="TabSheet1";
}
void __fastcall TForm1::Button1MouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
StatusBar1->SimpleText="Button1";
}
void __fastcall TForm1::Label1MouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
StatusBar1->SimpleText="Label1";
}
// b)
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
ProgressBar1->Max = TrackBar1->Max;
ProgressBar1->Min = TrackBar1->Min;
ProgressBar1->Position=TrackBar1->Position;
}
void __fastcall TForm1::UpDown1Click(TObject *Sender, TUDBtnType Button)
{
TrackBar1->LineSize=StrToInt(Edit2->Text);
}
// c)
void __fastcall TForm1::AddClick(TObject *Sender)
{
ListView1->Items->Add();
ListView1->Items->Item[ListView1->Items->Count-1]->Caption=EName->Text;
ListView1->Items->Item[ListView1->Items->Count-1]->SubItems->Add(EAdresse->Text);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.1 Lösungen Kapitel 2
131
ListView1->Items->Item[ListView1->Items->Count-1]->SubItems->Add(ETelefon->Text);
}
void __fastcall TForm1::RadioButton1Click(TObject *Sender)
{
ListView1->ViewStyle=vsIcon;
}
void __fastcall TForm1::RadioButton2Click(TObject *Sender)
{
ListView1->ViewStyle=vsList;
}
void __fastcall TForm1::RadioButton3Click(TObject *Sender)
{
ListView1->ViewStyle=vsSmallIcon;
}
void __fastcall TForm1::RadioButton4Click(TObject *Sender)
{
ListView1->ViewStyle=vsReport;
}
12.1.12 Aufgabe 2.15
// Datei: d:\Loesungen\kap-2\2.15\MediaPlU.cpp
#include "MediaPlU.h"
//----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (OpenDialog1->Execute())
{
MediaPlayer1->FileName = OpenDialog1->FileName;
MediaPlayer1->DeviceType = dtAutoSelect;
MediaPlayer1->Open();
}
}
// Datei: d:\Loesungen\kap-2\2.15\TimerU.cpp
#include "TimerU.h"
//----- Aufgabe 2 -----------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
Memo1->Lines->Add(FloatToStr(double(Now())*24*60*60));
Memo1->Lines->Add(TimeToStr(Now()));
}
void __fastcall TForm1::Edit1Enter(TObject *Sender)
{
Timer2->Enabled=true;
Timer2->Interval=100;
Edit1->Color=clYellow;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
132
12 Lösungen
void __fastcall TForm1::Edit1Exit(TObject *Sender)
{
Timer2->Enabled=false;
Edit1->Color=clWindow;
}
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
if (Edit1->Color==clYellow)
Edit1->Color=clWindow;
else
Edit1->Color=clYellow;
}
12.2 Lösungen Kapitel 3
12.2.1 Aufgabe 3.3
// Datei: d:\Loesungen\kap-3\3.3\VarDeklU.cpp
#include "VarDeklU.h"
void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*
int Preis_in_$,
// unzulässig wegen Sonderzeichen $
x kleiner y,
// unzulässig wegen der Leerzeichen " "
Zinssatz_in_%, // unzulässig wegen Sonderzeichen %
x/y,
// unzulässig wegen Sonderzeichen /
this,
// unzulässig, da Schlüsselwort
Einwohner_von_Tübingen; // unzulässig wegen Umlaut "ü"
*/
}
12.2.2 Aufgaben 3.4
// Datei: d:\Loesungen\kap-3\3.4\IntU.cpp
#include "IntU.h"
//---- Aufgaben 3.4.2
-----------------------------------------------
void __fastcall TForm1::Aufg342Click(TObject *Sender)
{
// 1. a)
// 37 im Binärsystem:
// 37dez = 1*2^5 + 0*2^4 + 0*2^3 + 1*2^2 + 0*2^1 + 1*2^0 = 0010 0101bin
//
b)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.2 Lösungen Kapitel 3
133
// -37 im Zweierkomplement: Einerkomplement(37) +1
// 1101 1010 + 1 = 1101 1011bin = -37dez
//
c)
// 37dez = 2*16^1 + 5*16^0 = 25hex,
// Alternativ - Die Blöcke aus der Binärdarstellung sind die hex-Ziffern:
//
0010bin = 2hex, 0101bin = 5hex
//
//
37dez = 0010 0101bin = 25 hex
-37dez = 1101 1011bin = db hex
Memo1->Lines->Add("Aufgabe 1:");
int i1=37;
Memo1->Lines->Add(IntToStr(i1)+ "dez: "+IntToHex(i1,2)+ "hex");
int j1=-37;
Memo1->Lines->Add(IntToStr(j1)+ "dez: "+IntToHex(j1,2)+ "hex");
// 2. a)
// Zweierkomplement von ab=1010 1011: 0101 0101 bin = 55 hex = 85 dez,
// also -85
Memo1->Lines->Add("Aufgabe 2:");
signed char i2=0xab;
Memo1->Lines->Add(IntToStr(i2)+ "dez: "+IntToHex(i2,2)+ "hex");
//
b)
// ab = 10*16 + 11 = 171
unsigned char j2=0xab;
Memo1->Lines->Add(IntToStr(j2)+ "dez: "+IntToHex(j2,2)+ "hex");
// 3.
Memo1->Lines->Add("Aufgabe 3:");
Memo1->Lines->Add(IntToStr(030)); // Vorwahl Berlin // 24,
// da wegen der führenden 0 ein Oktalliteral
Memo1->Lines->Add(IntToStr(017+15)); // 30
Memo1->Lines->Add(IntToStr(0x12+10)); // 28
}
//---- Aufgaben 3.4.4 -----------------------------------------------// 1.
void __fastcall TForm1::Aufg3441Click(TObject *Sender)
{
unsigned char b = 255;
unsigned char c = 0;
short i = 20000;
b = b + 1;
// 0
c = c - 1;
// 255
short j = i/8; // 2500
short k = i/-8; // -2500
short l = i%8; // 0
short m = i%-8; // 0
Memo1->Lines->Add(IntToStr(b));
Memo1->Lines->Add(IntToStr(c));
Memo1->Lines->Add(IntToStr(j));
Memo1->Lines->Add(IntToStr(k));
Memo1->Lines->Add(IntToStr(l));
Memo1->Lines->Add(IntToStr(m));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
134
12 Lösungen
}
// 2.
void __fastcall TForm1::FormResize(TObject *Sender)
{
int frei = (Form1->ClientWidth - Button1->Width - Button2->Width)/3;
Button1->Left = frei;
Button2->Left = 2*frei + Button1->Width;
}
// 3.
void __fastcall TForm1::Aufg3443Click(TObject *Sender)
{
AnsiString s=
"Falls die beiden Zahlen i und j verschieden sind, ist immer einer der "
"beiden Faktoren in (i/j)*(j/i) Null (Ganzzahldivision). Wenn i und j "
"dagegen gleich sind, ist das Produkt immer Eins.";
Memo1->Lines->Add(s);
}
//---- Aufgaben 3.4.6 -----------------------------------------------void __fastcall TForm1::Aufg346Click(TObject *Sender)
{
// 1. a)
char c;
bool Grossbuchstabe = (c>='A') && (c<='Z');
// 1. b)
bool Buchstabe = ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'));
// 1. c)
bool alphanumerisch = ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))||
((c>='0') && (c<='9'));
// 2.
int Jahr;
bool Schaltjahr =((Jahr%4 == 0) && (Jahr%100 != 0)) || (Jahr%400 == 0);
// 3.
int j1,j2,m1,m2,t1,t2;
bool vorher = (j1<j2) || ((j1==j2) && ((m1<m2) || ((m1==m2) && (t1< t2))));
}
//-------------------------------------------------------------------void __fastcall TForm1::Aufg3464Click(TObject *Sender)
{
ffnen1->Enabled = !ffnen1->Enabled;
// zum Unterpunkt Öffnen des Menüs
GroupBox1->Visible = !GroupBox1->Visible;
// zum Unterpunkt Groupbox
}
12.2.3 Aufgaben 3.5
// Datei: d:\Loesungen\kap-3\3.5\FktU.cpp
#include "FktU.h"
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.2 Lösungen Kapitel 3
135
void __fastcall TForm1::FormCreate(TObject *Sender)
{ // Damit es keinen Konversionsfehler gibt, wenn man vergißt, einen
// ganzzahligen Wert in das Eingabefenster einzugeben.
Edit1->Text="50";
}
//---- Aufgabe 1
----------------------------------------------------
void __fastcall TForm1::unsign_forClick(TObject *Sender)
{
int n=StrToInt(Edit1->Text);
for (unsigned int u=0; u<=n-1; u++) // Warnung: Vergleich von signed- und
Memo1->Lines->Add(IntToStr(u));
//
unsigned-Werten
// a), b)
// Da u den Datentyp unsigned int hat, wird der Datentyp von n-1 im
// Ausdrucku<=n-1 ebenfalls in unsigned int konvertiert. Für n=0 führt
// das zu einer Endlosschleife. Für Werte n>=1 erhält man das erwartete
// Ergebnis.
// c) Mit der Bedingung u<n gibt es auch mit n=0 keine Endlosschleife.
for (unsigned int u=0; u<n; u++) // Warnung: Vergleich von signed- und
Memo1->Lines->Add(IntToStr(u));//
unsigned-Werten
}
//
Aufgabe 2 --------------------------------------------------
int Quersumme(int n)
{
if (n<0) n=-n; // falls n < 0
int s=0;
while (n>0)
{
s = s+n%10;
n = n/10;
}
return s;
}
void Zeige_QuerSummen(int a, int b)
{
Form1->Memo1->Lines->Add("Quersummen");
for (int i=a; i<=b; i++)
Form1->Memo1->Lines->Add(IntToStr(i)+": "+
IntToStr(Quersumme(i)));
}
void __fastcall TForm1::QuersummeClick(TObject *Sender)
{
Zeige_QuerSummen(1,StrToInt(Edit1->Text));
}
//----- Aufgabe 3 -------------------------------------------------int Fibonacci(int n)
{
int fib=0,f0=0,f1=1;
for (int i=0; i<n; i++)
{
fib = f0 + f1;
f1 = f0;
f0 = fib;
}
return fib;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
136
void zeige_Fibonacci(int n)
{
Form1->Memo1->Lines->Add("Fibonacci-Zahlen:");
for (int i=0; i<n; i++)
Form1->Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(Fibonacci(i)));
}
void __fastcall TForm1::FibonacciClick(TObject *Sender)
{
zeige_Fibonacci(StrToInt(Edit1->Text));
}
//----- Aufgabe 4 -------------------------------------------------bool prim(int n)
{
if (n<2) return false;
else if (n==2) return true;
else
{
int i=2;
while (i*i<=n)
{
if (n%i==0) return false;
i++;
}
return true;
}
}
void zeige_Primzahlen(int n)
{
Form1->Memo1->Lines->Add("Primzahlen: ");
for (int i=1;i<n; i++)
if (prim(i))
Form1->Memo1->Lines->Add(IntToStr(i));
}
void __fastcall TForm1::PrimzahlenClick(TObject *Sender)
{
zeige_Primzahlen(StrToInt(Edit1->Text));
}
int Goldbach(int n)
{
if ((n<6) || (n%2==1)) return -1;
else
{
int k=0;
for (int i=2; i<n; i++)
if (prim(i) && prim(n-i)) k++;
return k;
}
}
void zeige_Goldbach(int n)
{
Form1->Memo1->Lines->Add("Goldbach'sche Vermutung:");
for (int i=6; i<100; i=i+2)
Form1->Memo1->Lines->Add(IntToStr(i)+": g="+IntToStr(Goldbach(i)));
}
void __fastcall TForm1::GoldbachClick(TObject *Sender)
{
zeige_Goldbach(StrToInt(Edit1->Text));
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.2 Lösungen Kapitel 3
//----- Aufgabe 5 -------------------------------------------------void zeige_PythagZahlentripel(int n)
{
for (int a=1; a<n; a++)
for (int b=a; b<n; b++)
for (int c=1; c*c<=a*a+b*b; c++)
if (a*a+b*b==c*c)
Form1->Memo1->Lines->Add("a="+IntToStr(a)+" b="+
IntToStr(b)+" c= "+ IntToStr(c));
}
void __fastcall TForm1::PythagTripClick(TObject *Sender)
{
zeige_PythagZahlentripel(StrToInt(Edit1->Text));
}
//----- Aufgabe 6 -------------------------------------------------int f3nplus1(int n, bool show)
{
if (n<=1) return 0;
int m=0;
while (n!=1)
{
m++;
if (n%2==1) n=3*n+1;
else n=n/2;
if (show) Form1->Memo1->Lines->Add(" n="+IntToStr(n));
}
return m;
}
void zeige_3nplus1(int n)
{
for (int i=0; i<n; i++)
{
int k=f3nplus1(i,false);
Form1->Memo1->Lines->Add("i="+IntToStr(i)+" k="+IntToStr(k));
}
}
void __fastcall TForm1::f3nplus1Click(TObject *Sender)
{
zeige_3nplus1(StrToInt(Edit1->Text));
}
//----- Aufgabe 7 -------------------------------------------------void Lottozahlen()
{
Form1->Memo1->Lines->Add("Lottozahlen: ");
for (int i=0; i<7; i++)
Form1->Memo1->Lines->Add(IntToStr(1+rand()%49));
}
void __fastcall TForm1::LottoClick(TObject *Sender)
{
Lottozahlen();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
137
138
12.2.4 Aufgabe 3.6
// Datei: d:\Loesungen\kap-3\3.6\OsternU.cpp
#include "OsternU.h"
AnsiString Osterdatum(int J)
{
int A,B,C,D,E,M,N,OTag,PTag;
AnsiString s;
if (J < 1583) s = " Formel gilt nicht ";
else if (J < 1700) { M = 22; N = 2; }
else if (J < 1800) { M = 23; N = 3; }
else if (J < 1900) { M = 23; N = 4; }
else if (J < 2100) { M = 24; N = 5; }
else if (J < 2200) { M = 24; N = 6; }
else if (J < 2300) { M = 25; N = 0; }
else s = " Formel gilt nicht ";
A = J % 19;
B = J % 4;
C = J % 7;
D = (19*A + M) % 30;
E = (2*B + 4*C + 6*D + N) % 7;
if ((22 + D + E >= 1) && (22 + D + E <= 31)) // in [1..31])
{
OTag =22 + D + E;
PTag = OTag+49;
s = s+IntToStr(J)+" Ostern: "+IntToStr(OTag)+ ". März Pfingsten: ";
if (PTag>31+30+31) // Tage im März + April + Mai
s = s+IntToStr(PTag-92) + ". Juni";
else if (PTag > 30+31) // Tage im April + Mai
s = s+IntToStr(PTag-61) + ". Mai";
else s = s + " unmöglich"; // Wegen OTag >= 22 ist PTag >= 71
}
else
{
OTag = D + E - 9;
PTag = OTag+49;
if (OTag == 26) OTag = 19;
else if ((OTag==25) && (D==28) && (E==6) && (A>10)) OTag = 18;
s= s+IntToStr(J)+" Ostern: "+IntToStr(OTag)+ ". April Pfingsten: ";
if (PTag > 30+31) // Tage im April + Mai
s = s+IntToStr(PTag-61) + ". Juni";
else if (PTag > 30) // Tage im April
s = s+IntToStr(PTag-30) + ". Mai";
else s = s + " unmöglich"; // wegen OTag >= -9 ist PTag >= 40
}
return s;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int von=StrToInt(Edit1->Text);
int bis=StrToInt(Edit2->Text);
for (int J=von; J<=bis; J++)
Memo1->Lines->Add(Osterdatum(J));
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Edit1->Text="1970";
Edit2->Text="1980";
Memo1->Lines->Clear();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.2 Lösungen Kapitel 3
139
12.2.5 Aufgaben 3.7
// Datei: d:\Loesungen\kap-3\3.7\GleitKU.cpp
#include "GleitKU.h"
//----------- Aufgabe 1. --------------------------------------------void __fastcall TForm1::Aufg371Click(TObject *Sender)
{
int j=10,k=6;
double x=j,y=3.14,z=10;
double d1 = j/k;
double d2 = x/k;
int i1 = j/k;
int i2 = x/k;
// int i3 = 3(j+k);
int i4 = j/k/k;
int i5 = j/(z/y);
int i6 = j/(y-j/k);
//
//
//
//
1
1.666..
1
1
// i3 = 3*(j+k) würde gehen
// 0
// 3
// 4
Memo1->Lines->Add(FloatToStr(d1));
Memo1->Lines->Add(FloatToStr(d2));
Memo1->Lines->Add(FloatToStr(i1));
Memo1->Lines->Add(FloatToStr(i2));
Memo1->Lines->Add(FloatToStr(i4));
Memo1->Lines->Add(FloatToStr(i5));
Memo1->Lines->Add(FloatToStr(i6));
}
//---------- Aufgabe 2 ----------------------------------------------// Diese Lösung ist im Projekt Daniel enthalten.
//---------- Aufgabe 3 ----------------------------------------------// #include <stdlib.h> // bei manchen Versionen des C++Builders notwendig für
rand
double RegentropfenPi(int n)
{
int Treffer=0;
for (int i = 0; i<n; i++)
{
double x = (0.0+rand())/RAND_MAX;
double y = (0.0+rand())/RAND_MAX;
if (x*x+y*y < 1) Treffer++;
}
return 4.0*Treffer/n;
}
void __fastcall TForm1::RegentropfenClick(TObject *Sender)
{
Memo1->Lines->Add(FloatToStr(RegentropfenPi(10000)));
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
140
//--------- Aufgabe 4. ----------------------------------------------int Fakulaet(int n)
{
int f=1;
for (int i=2; i<=n; i++)
f = f*i;
return f;
}
double doubleFakulaet(int n)
{
double f=1;
for (int i=2; i<=n; i++)
f = f*i;
return f;
}
void __fastcall TForm1::FakultaetClick(TObject *Sender)
{
for (int i=1; i<30; i++)
Memo1->Lines->Add(IntToStr(i)+"! = "+IntToStr(Fakulaet(i)));
for (int i=1; i<30; i++)
{
double f = doubleFakulaet(i);
double Sekunden = f/1000000.0;
double Jahre = Sekunden/(60.0*60*24*365);
Memo1->Lines->Add(IntToStr(i)+"! = "+FloatToStr(f)+
" t="+FloatToStr(Sekunden)+
" j="+FloatToStr(Jahre));
}
}
//-------- Aufgabe 5. -----------------------------------------------#include <math.h> // für fabs
void __fastcall TForm1::ReihenfSummClick(TObject *Sender)
{
int n = 1000000;
float f,uf=0,of=0;
double d,ud=0,od=0;
long double e,ue=0,oe=0;
for (int i= 1; i<=n; i++)
{
e = (1.0/i)*(1.0/i); // 1/(i*i) ergibt für i=65536 Division by 0
f = e; // Da (1/i)*(1/i) den Datentyp Extended
d = e; // s = (1/i)*(1/i) usw.
uf = uf + f;
ud = ud + d;
ue = ue + e;
}
for (int i=n; i>=1; i--)
{
e = (1.0/i)*(1.0/i);
f = e;
d = e;
of = of + f;
od = od + d;
oe = oe + e;
}
Memo1->Lines->Add("n="+IntToStr(n));
Memo1->Lines->Add("of="+FloatToStr(of));
Memo1->Lines->Add("uf="+FloatToStr(uf)+
" df="+FloatToStr(fabs(of-uf)));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.2 Lösungen Kapitel 3
Memo1->Lines->Add("od="+FloatToStr(od));
Memo1->Lines->Add("ud="+FloatToStr(ud)+
" dd="+FloatToStr(fabs(od-ud)));
Memo1->Lines->Add("oe="+FloatToStr(oe));
Memo1->Lines->Add("ue="+FloatToStr(ue)+
" de="+FloatToStr(fabs(oe-ue)));
Memo1->Lines->Add("ex="+FloatToStr(M_PI*M_PI/6));
}
//------- Aufgabe 6. ------------------------------------------------long double Trapez(double a, double b, int n)
{
long double s,h;
s = (sqrtl(1-a*a) + sqrtl(1-b*b))/2;
h = (b-a)/n;
for (int i=1; i<n; i++)
s = s + sqrt(1-(a+i*h)*(a+i*h));
return s*h;
}
void __fastcall TForm1::NumIntegClick(TObject *Sender)
{
double t=Trapez(0,1,10000);
Memo1->Lines->Add("T="+FloatToStr(4*t));
}
//------ Aufgabe 7. -------------------------------------------------double Mises(int n)
{
double q=1;
for (int i=2; i<=n; i++)
q = q*(365-i+1)/365;
return 1-q; // 0 für n<=1
}
void __fastcall TForm1::GebProbMisesClick(TObject *Sender)
{
for (int i=1; i<50; i++)
//
if (i%5 == 0)
Memo1->Lines->Add("n="+IntToStr(i)+" p="+FloatToStr(Mises(i)));
}
//------- Aufgabe 8. ------------------------------------------------int fib(int n)
{
int fib=0,f0=0,f1=1;
for (int i=2; i<n; i++)
{
fib = f0 + f1;
f1 = f0;
f0 = fib;
}
return fib;
}
float fib_fl(int n)
{
float x0 = (1+sqrt(5))/2;
float x1 = (1-sqrt(5))/2;
return (pow(x0,n) - pow(x1,n))/sqrt(5);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
141
142
12 Lösungen
};
double
{
double
double
return
};
fib_db(int n)
x0 = (1+sqrt(5))/2;
x1 = (1-sqrt(5))/2;
(pow(x0,n) - pow(x1,n))/sqrt(5);
long double fib_ld(int n)
{
long double x0 = (1+sqrtl(5))/2;
long double x1 = (1-sqrtl(5))/2;
return (powl(x0,n) - powl(x1,n))/sqrtl(5);
};
void __fastcall TForm1::FibonacciClick(TObject *Sender)
{
for (int i=0; i<50; i++)
Memo1->Lines->Add(Format("fib(%d)=%d fl=%g db=%g ld=%g ",
OPENARRAY(TVarRec,(i,fib(i),fib_fl(i),fib_db(i),fib_ld(i)))));
}
//------- Aufgabe 9. ------------------------------------------------int round(double x)
{
return int(x+0.5);
}
void __fastcall TForm1::RoundClick(TObject *Sender)
{
for (double x=10; x<11; x=x+0.1)
Memo1->Lines->Add(FloatToStr(x)+": "+IntToStr(round(x)));
}
// Datei: d:\Loesungen\kap-3\3.7\DanielU.cpp
#include "DanielU.h"
//-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
// Dieses Programm hat noch viele Schwächen: So erhält man nur eine Fehler// meldung ohne konkrete Hinweise, wenn eine Datei nicht vorhanden ist,
// oder wenn die Werte im Memo keine Zahlen sind.
void __fastcall TForm1::DurchschnClick(TObject *Sender)
{
double Summe=0, k=0;
int n=Memo1->Lines->Count;
for (int i=0; i<n; i++)
if (Memo1->Lines->Strings[i]!="") // Leerzeilen (z.B. die letzte Zeile)
{
// nicht mitzählen
Summe = Summe+StrToFloat(Memo1->Lines->Strings[i]);
k++;
}
Edit1->Text = FloatToStr(Summe/k);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.2 Lösungen Kapitel 3
}
void __fastcall TForm1::RBDeutschClick(TObject *Sender)
{
Memo1->Lines->LoadFromFile("c:\\test\\noten.deu");
}
void __fastcall TForm1::RBMatheClick(TObject *Sender)
{
Memo1->Lines->LoadFromFile("c:\\test\\noten.mat");
}
void __fastcall TForm1::RBEnglischClick(TObject *Sender)
{
Memo1->Lines->LoadFromFile("c:\\test\\noten.eng");
}
void __fastcall TForm1::RBFranzClick(TObject *Sender)
{
Memo1->Lines->LoadFromFile("c:\\test\\noten.fra");
}
void __fastcall TForm1::RBChemieClick(TObject *Sender)
{
Memo1->Lines->LoadFromFile("c:\\test\\noten.che");
}
void __fastcall TForm1::SpeichernClick(TObject *Sender)
{
if (RBDeutsch->Checked)
Memo1->Lines->SaveToFile("c:\\test\\noten.deu");
if (RBMathe->Checked)
Memo1->Lines->SaveToFile("c:\\test\\noten.mat");
if (RBEnglisch->Checked)
Memo1->Lines->SaveToFile("c:\\test\\noten.eng");
if (RBFranz->Checked)
Memo1->Lines->SaveToFile("c:\\test\\note.fra");
if (RBChemie->Checked)
Memo1->Lines->SaveToFile("c:\\test\\noten.che");
}
12.2.6 Aufgaben 3.9
// Datei: d:\Loesungen\kap-3\3.9\ZeigerU.cpp
#include "ZeigerU.h"
//-------- Aufgabe 1 ------------------------------------------------void __fastcall TForm1::Aufg391Click(TObject *Sender)
{
int i=5;
int *pi, pj;
char *pc, pd;
// a)
//
pi=i;
// Fehler: Konvertierung von 'int' nach 'int*' nicht möglich
// b)
pi=&i;
// *pi=5
// c)
*pi=i;
// *pi=5
// d)
//
*pi=&i; // Fehler: Konvertierung von 'int*' nach 'int' nicht möglich
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
143
144
12 Lösungen
// e)
//
pi=pj;
// Fehler: Konvertierung von 'int' nach 'int*' nicht möglich
// f)
pc=&pd; // *pc=pd
// g)
//
pi=pc; // Fehler: Konvertierung von 'char*' nach 'int*' nicht möglich
// h)
pd=*pi;
// pd=5, wegen f) auch *pc=5
// h)
*pi=i**pc; // *pi=5*5
//f)
pi=0;
// *pi undefiniert
}
//-------- Aufgabe 2 ------------------------------------------------void __fastcall TForm1::Aufg392Click(TObject *Sender)
{
int i=10;
int* p=&i;
*p=17;
// Wegen p=&i: i=17
Memo1->Lines->Add(IntToStr(i));
// 17
Memo1->Lines->Add(IntToStr(*p));
// 17
}
//-------- Aufgabe 3 ------------------------------------------------// a)
Fibonacci-Zahlen
int Fibonacci(int n)
{
int fib=0,*f0=new int(0),*f1=new int(1);
/* Wenn der Wert der Variablen fib anschließend mit return
zurückgegeben werden soll, kann der von fib reservierte
Speicherplatz nicht mit delete freigegeben werden, da
die Funktion mit dem Aufruf von return verlassen wird.
Deswegen können nur die beiden lokalen Variablen f0 und
f1 dynamisch vereinbart werden.
*/
for (int i=0; i<n; i++)
{
fib = *f0 + *f1;
*f1 = *f0;
*f0 = fib;
}
delete f0; // nicht vergessen !!!
delete f1;
return fib;
}
void zeige_Fibonacci(int n)
{
Form1->Memo1->Lines->Add("Fibonacci-Zahlen:");
for (int i=0; i<n; i++)
Form1->Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(Fibonacci(i)));
}
// b)
Quersumme
int Quersumme(int n)
{
if (n<0) n=-n; // falls n < 0
int s=0;
/* Wenn der Wert der Variablen s anschließend mit return
zurückgegeben werden soll, kann der von s reservierte
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.2 Lösungen Kapitel 3
Speicherplatz nicht mit delete freigegeben werden, da
die Funktion mit dem Aufruf von return verlassen wird.
*/
while (n>0)
{
s = s+n%10;
n = n/10;
}
return s;
}
void Zeige_QuerSummen(int a, int b)
{
Form1->Memo1->Lines->Add("Quersummen");
for (int i=a; i<=b; i++)
Form1->Memo1->Lines->Add(IntToStr(i)+":"+IntToStr(Quersumme(i)) );
}
void __fastcall TForm1::Aufg383Click(TObject *Sender)
{
Zeige_QuerSummen(1,50);
zeige_Fibonacci(20);
}
//-------- Aufgabe 4 ------------------------------------------------int Leerzeichen(char* s)
{
int n=0;
while (*s)
{
if (*s==' ') n++;
s++;
}
return n;
}
void __fastcall TForm1::LeerzeichClick(TObject *Sender)
{
Form1->Memo1->Lines->Add(Leerzeichen(""));
Form1->Memo1->Lines->Add(Leerzeichen("1"));
Form1->Memo1->Lines->Add(Leerzeichen("1 "));
Form1->Memo1->Lines->Add(Leerzeichen(" 1"));
Form1->Memo1->Lines->Add(Leerzeichen(" 1 "));
}
//-------- Aufgabe 5 ------------------------------------------------void ShowTempPath()
{
LPTSTR lpBuffer=new char[MAX_PATH];
DWORD result=GetTempPath(MAX_PATH,lpBuffer);
if (result == 0)
Form1->Memo1->Lines->Add("Fehler");
else if (result > MAX_PATH)
Form1->Memo1->Lines->Add("Puffer zu klein");
Form1->Memo1->Lines->Add(lpBuffer);
delete[] lpBuffer;
}
void __fastcall TForm1::TempPathClick(TObject *Sender)
{
ShowTempPath();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
145
146
//-------- Aufgabe 6 ------------------------------------------------void ShowDriveStrings()
{
DWORD BufSize = 4*('Z'-'A'+1)+1;
LPTSTR lpBuffer=new char[BufSize];
DWORD result=GetLogicalDriveStrings(BufSize,lpBuffer);
if (result == 0)
Form1->Memo1->Lines->Add("Fehler");
else if (result > BufSize)
Form1->Memo1->Lines->Add("Puffer zu klein");
char* b['Z'-'A'+1];
// Die folgende for-Schleife bewirkt:
//
b[0]=lpBuffer;
//
b[1]=lpBuffer+4;
//
b[2]=lpBuffer+8;
// usw.
for (int i=0; i<('Z'-'A'+1); i++)
b[i]=lpBuffer+4*i;
int i=0;
while (*b[i])
{
Form1->Memo1->Lines->Add(b[i]);
i++;
}
delete[] lpBuffer;
}
void __fastcall TForm1::DriveStringsClick(TObject *Sender)
{
ShowDriveStrings();
}
12.2.7 Aufgabe 3.11
// Datei: d:\Loesungen\kap-3\3.11\TypedU.cpp
#include "TypedU.h"
#include <typeinfo>
using namespace std;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Add(typeid(PBOOL).name());
// int*
Memo1->Lines->Add(typeid(LPLONG).name()); // long*
Memo1->Lines->Add(typeid(LPDWORD).name()); // unsigned long*
Memo1->Lines->Add(typeid(LPVOID).name()); // void*
Memo1->Lines->Add(typeid(LPCVOID).name()); // const void*
}
12.2.8 Aufgabe 3.12
// Datei: d:\Loesungen\kap-3\3.12\EnumU.cpp
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.2 Lösungen Kapitel 3
#include "EnumU.h"
//------Aufgabe 1 ---------------------------------------------------void __fastcall TForm1::Aufg3131Click(TObject *Sender)
{
// a) Ohne den Umlaut in "März" möglich
enum Monate{Januar=1, Februar, /* März, */ April, Mai, Juni,
Juli, August, September, Oktober, November, Dezember};
//
//
//
//
b)
Nicht möglich, da die Enumeratoren Ganzzahlkonstanten sein
müssen.
enum Monate {Jan = "Januar", Februar = "Februar"};
// c)
// möglich
enum {max=100};
int a[max];
}
//------Aufgabe 2 a) ------------------------------------------------void __fastcall TForm1::DialogClick(TObject *Sender)
{
Form1->BorderStyle = bsDialog;
}
void __fastcall TForm1::SingleClick(TObject *Sender)
{
Form1->BorderStyle = bsSingle;
}
void __fastcall TForm1::SizeableClick(TObject *Sender)
{
Form1->BorderStyle = bsSizeable;
}
void __fastcall TForm1::NoneClick(TObject *Sender)
{
Form1->BorderStyle = bsNone;
}
void __fastcall TForm1::ToolWindowClick(TObject *Sender)
{
Form1->BorderStyle = bsToolWindow;
}
void __fastcall TForm1::SizeToolWinClick(TObject *Sender)
{
Form1->BorderStyle = bsSizeToolWin;
}
//------Aufgabe 2 b) ------------------------------------------------/* In der Online-Hilfe findet man:
typedef TFormBorderStyle TBorderStyle;
und
enum TFormBorderStyle { bsNone, bsSingle, bsSizeable, bsDialog,
bsToolWindow, bsSizeToolWin };
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
147
148
12 Lösungen
Damit ergibt sich die Lösung z. B. folgendermaßen:
*/
TFormBorderStyle bs=bsNone;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Aufg3131->Visible=false;
if (bs<bsSizeToolWin) bs=bs+1;
else bs=bsNone;
Form1->BorderStyle = bs;
}
12.2.9 Aufgabe 3.14
// Datei: d:\Loesungen\kap-3\3.14\PraeprU.cpp
#include "PraeprU.h"
//-------------------------------------------------------------------#include <vcl\graphics.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int clBlack=1;
// Falls clBlack ein Makro ist, hat diese Definition eine Fehlermeldung des
// Compilers zur Folge.
}
12.3 Lösungen Kapitel 4
12.3.1 Aufgaben 4.1
// Datei: d:\Loesungen\kap-4\4.1\StringU.cpp
#include "StringU.h"
#include <string>
using namespace std;
//------ Aufgabe 1 -------------------------------------------------// a)
void zerlegeDatum(AnsiString Datum)
{
AnsiString tstr = Datum.SubString(1,Datum.Pos('.')-1);
int Tag
= StrToInt(tstr);
Datum.Delete(1,Datum.Pos('.'));
int p = Datum.Pos('.');
AnsiString mstr = Datum.SubString(1,p-1);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
int Monat = StrToInt(mstr);
AnsiString jstr = Datum.SubString(p+1,Datum.Length()-p+1);
int Jahr = StrToInt(jstr);
Form1->Memo1->Lines->Add(Tag);
Form1->Memo1->Lines->Add(Monat);
Form1->Memo1->Lines->Add(Jahr);
}
// b)
void zerlegeDatum(string Datum)
{
int StartMonat = Datum.find(".");
string tstr = Datum.substr(0,StartMonat);
int StartJahr = Datum.find(".",StartMonat+1);
string mstr = Datum.substr(StartMonat+1,StartJahr-StartMonat-1);
string jstr = Datum.substr(StartJahr+1,Datum.length()-StartMonat);
Form1->Memo1->Lines->Add(tstr.c_str());
Form1->Memo1->Lines->Add(mstr.c_str());
Form1->Memo1->Lines->Add(jstr.c_str());
}
void Test_zerlegeDatum(AnsiString d)
{
Form1->Memo1->Lines->Add(d+":");
zerlegeDatum(d);
string s=d.c_str();
zerlegeDatum(s);
}
void __fastcall TForm1::Aufg411Click(TObject *Sender)
{
Test_zerlegeDatum("2.1.98");
Test_zerlegeDatum("11.12.2001");
}
//------ Aufgabe 2 -------------------------------------------------// Die Lösung der Aufgabe 2 ist das Projekt Telefon
//------ Aufgabe 3 -------------------------------------------------// a)
void parseString(string s)
{
Form1->Memo1->Lines->Add("string Test: "+AnsiString(s.c_str()));
string Separatoren=";.,- ";
string::size_type pos_tok=s.find_first_not_of(Separatoren);
while (pos_tok!=string::npos)
{
string::size_type pos_sep=s.find_first_of(Separatoren,pos_tok);
if (pos_sep==string::npos)
pos_sep=s.length();
string token=s.substr(pos_tok,pos_sep-pos_tok);
Form1->Memo1->Lines->Add("'"+AnsiString(token.c_str())+"'");
pos_tok=s.find_first_not_of(Separatoren,pos_sep+1);
}
}
// b)
// Am einfachsten mit den Funktionen FindFirstOf und FindFirstNotOf
// für AnsiStrings:
int FindFirstOf(const AnsiString s, const AnsiString Delimiter,int start)
{ // analog zu den Funktionen für die Klasse string für AnsiString
for (int i=start; i<=s.Length(); i++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
149
150
12 Lösungen
if (s.IsDelimiter(Delimiter,i)) return i;
return -1;
}
int FindFirstNotOf(const AnsiString s, const AnsiString Delimiter, int start)
{ // analog zu den Funktionen für die Klasse string für AnsiString
for (int i=start; i<=s.Length(); i++)
if (!s.IsDelimiter(Delimiter,i)) return i;
return -1;
}
// b)
void parseString(AnsiString s)
{
Form1->Memo1->Lines->Add("AnsiString Test: "+s);
AnsiString Separatoren=";.,- ";
int pos_tok=FindFirstNotOf(s,Separatoren,1);
while (pos_tok!=-1)
{
int pos_sep=FindFirstOf(s,Separatoren,pos_tok+1);
if (pos_sep==-1)
pos_sep=s.Length()+1;
AnsiString token=s.SubString(pos_tok,pos_sep-pos_tok);
Form1->Memo1->Lines->Add("'"+token+"'");
pos_tok=FindFirstNotOf(s,Separatoren,pos_sep+1);
}
}
// c)
void __fastcall TForm1::Aufg413Click(TObject *Sender)
{
parseString(AnsiString(""));
// leerer String , keine
parseString(
string(""));
// leerer String , keine
parseString(AnsiString(" "));
// nur Separator, keine
parseString(
string(" "));
// nur Separator, keine
parseString(AnsiString("123"));
parseString(
string("123"));
parseString(AnsiString(";123"));
"123"
parseString(
string(";123"));
"123"
parseString(AnsiString("123;"));
parseString(
string("123;"));
parseString(AnsiString(";123;"));
Ausgabe "123"
parseString(
string(";123;"));
Ausgabe "123"
Ausgabe
Ausgabe
Ausgabe
Ausgabe
// nur token, Ausgabe "123"
// nur token, Ausgabe "123"
// Separator am Anfang, ein token, Ausgabe
// Separator am Anfang, ein token, Ausgabe
// Separator am Ende, ein token, Ausgabe "123"
// Separator am Ende, ein token, Ausgabe "123"
// Separator am Anfang und Ende, ein token,
// Separator am Anfang und Ende, ein token,
parseString(AnsiString("456 ab.xy"));
parseString(
string("456 ab.xy"));
parseString(AnsiString(" 456 ab.xy"));
"789","ab","xy"
parseString(
string(" 456 ab.xy"));
"789","ab","xy"
parseString(AnsiString("456 ab.xy;"));
"789","ab","xy"
parseString(
string("456 ab.xy;"));
"789","ab","xy"
parseString(AnsiString(" 456 ab.xy;"));
Ausgabe "789","ab","xy"
parseString(
string(" 456 ab.xy;"));
Ausgabe "789","ab","xy"
}
//Ausgabe "456", "ab", "xy"
//Ausgabe "456", "ab", "xy"
// Separator am Anfang, Ausgabe
// Separator am Anfang, Ausgabe
// Separator am Ende, Ausgabe
// Separator am Ende, Ausgabe
// Separator am Anfang und am Ende,
// Separator am Anfang und am Ende,
//------ Aufgabe 4 -------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
void __fastcall TForm1::Aufg414Click(TObject *Sender)
{
Memo1->Font->Name="Courier";
double K=100;
// Currency K=StrToCurr(Form1->Edit1->Text); // bzw. K=100
for (int p=5; p<=15; p++)
{
// Currency z = K*p/100;
double z = K*p/100;
AnsiString s=Format("K=%8.2f p=%2d%% z=%7.2f",OPENARRAY(TVarRec,(K,p,z)));
Memo1->Lines->Add(s);
};
}
//------ Aufgabe 5 -------------------------------------------------AnsiString BinaerDarst(int n)
{
AnsiString s;
for (int i=1; i<=8*sizeof(n); i++)
{
if (n%2==1) s = "1"+s;
else s = "0"+s;
n = n/2;
}
return s;
}
void __fastcall TForm1::Aufg415Click(TObject *Sender)
{
for (int i=0; i<=8; i++)
Memo1->Lines->Add(BinaerDarst(i));
}
//------ Aufgabe 6 -------------------------------------------------int Max(int a, int b)
{
if (a>b) return a;
else return b;
}
AnsiString BigIntAdd1(AnsiString a, AnsiString b)
{ // Mit AnsiStrings
AnsiString s;
int m=Max(a.Length(),b.Length());
int ue=0;
for (int i=1; i<=m; i++)
{
int ai,bi;
if (i<=a.Length()) ai=a[a.Length()-i+1]-'0';
else ai=0;
if (i<=b.Length()) bi=b[b.Length()-i+1]-'0';
else bi=0;
int r=(ai+bi+ue)%10;
ue=(ai+bi+ue)/10;
AnsiString ziffer[10]={"0","1","2","3","4","5","6","7","8","9"};
s=ziffer[r]+s;
}
if (ue>0) s="1"+s;
return s;
}
#include <string>
using namespace std;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
151
152
string BigIntAdd2(string a, string b)
{ // mit den Strings der Standardbibliothek
string s;
string::size_type m=Max(a.length(),b.length());
int ue=0;
for (string::size_type i=0; i<m; i++)
{
string::size_type ai,bi;
if (i<a.length()) ai=a[a.length()-i-1]-'0';
else ai=0;
if (i<b.length()) bi=b[b.length()-i-1]-'0';
else bi=0;
int r=(ai+bi+ue)%10;
ue=(ai+bi+ue)/10;
string ziffer[10]={"0","1","2","3","4","5","6","7","8","9"};
s=ziffer[r]+s;
}
if (ue>0) s='1'+s;
return s;
}
void __fastcall TForm1::Aufg416Click(TObject *Sender)
{
AnsiString b="1";
for (int i=1; i<=100; i++)
{
b=BigIntAdd1(b,b);
Memo1->Lines->Add(AnsiString("2^")+IntToStr(i)+"="+b);
}
string c="1";
for (int i=1; i<=100; i++)
{
c=BigIntAdd2(c,c);
Memo1->Lines->Add(AnsiString("2^")+IntToStr(i)+"="+c.c_str());
}
}
//------ Aufgabe 7 -------------------------------------------------AnsiString SoundexCode(AnsiString s)
{
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
AnsiString code= "01230120022455012623010202";
// In Grossbuchstaben umwandeln:
s = s.UpperCase();
// Umlaute wirken sich nur an der ersten Stelle aus:
if (s.Length() >0)
if (s[1]=='Ä') s[1]='A';
else if (s[1]=='Ö') s[1]='O';
else if (s[1]=='Ü') s[1]='U';
// Entferne alle Zeichen, die keine Buchstaben sind
AnsiString t;
for (int i=1; i<=s.Length(); i++)
if (('A'<=s[i]) && (s[i]<= 'Z'))
t = t+s[i];
// Ersetze jedes Buchstabenpaar durch den Buchstaben
for (int i=1; i<=t.Length(); i++)
if ((i < t.Length()) && (t[i]==t[i+1]))
t.Delete(i,1);
// Erzeuge den Soundex-Code snd
AnsiString snd;
if (s.Length()>0) snd = s[1];
for (int i=2; i<=t.Length();i++)
if (snd.Length() < 4)
{
int j = t[i]-'A'+1;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
if (code[j]!='0')
snd = snd + code[j];
};
for (int i=snd.Length(); i<=4-1;i++) snd = snd + "0";
return snd;
}
void __fastcall TForm1::Aufg417Click(TObject *Sender)
{
Memo1->Lines->Add(SoundexCode("Euler"));
Memo1->Lines->Add(SoundexCode("Ellery"));
Memo1->Lines->Add(SoundexCode("Gauss"));
Memo1->Lines->Add(SoundexCode("Ghosh"));
Memo1->Lines->Add(SoundexCode("Hilbert"));
Memo1->Lines->Add(SoundexCode("Heilbronn"));
}
// Datei: d:\Loesungen\kap-4\4.1\TelefonU.cpp
#include "TelefonU.h"
//----- Aufgabe 2 -----------------------------------------------char* Telefonliste = "c:\\test\\telefon.txt";
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
Memo2->Lines->Clear();
if (!FileExists(Telefonliste)) // erzeuge leere Datei
Memo2->Lines->SaveToFile(Telefonliste);
Form1->Caption="Telefonverzeichnis";
Memo2->Lines->LoadFromFile(Telefonliste);
Memo1->Lines->Clear();
}
// b)
void __fastcall TForm1::suchenClick(TObject *Sender)
{
for (int i=0; i<=Memo2->Lines->Count-1; i++)
if (Memo2->Lines->Strings[i].UpperCase().Pos(Edit1->Text.UpperCase()) > 0)
Memo1->Lines->Add(Memo2->Lines->Strings[i]);
}
// c)
void __fastcall TForm1::speichernClick(TObject *Sender)
{
Memo2->Lines->SaveToFile(Telefonliste);
}
// d)
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (Key==13) suchenClick(Sender);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
153
154
12 Lösungen
12.3.2 Aufgaben 4.2.1
// Datei: d:\Loesungen\kap-4\4.2\EindArU.cpp
#include "EindArU.h"
//----Aufgabe 1 ------------------------------------------------void __fastcall TForm1::Aufg4211Click(TObject *Sender)
{
// a)
int a[10];
for (int i=1; i<=10;i++) a[i] = 0;
// Syntaktisch korrekt. Mit i=10 wird allerdings
// auf das nicht definierte Arrayelement a[10]
// zugegriffen.
// b)
char *p[]={"Alle","meine","Entchen"};
for (int i=0; i<=3;i++)
Memo1->Lines->Add(p[i]);
// Syntaktisch korrekt. Mit i=3 wird allerdings
// auf das nicht definierte Arrayelement a[3]
// zugegriffen.
{
// c)
char* p="schwimmen in", *q="dem See";
if (p==q) Memo1->Lines->Add("gleich");
else Memo1->Lines->Add("verschieden");
// Syntaktisch korrekt. Durch p==q werden allerdings
// die Zeiger und nicht die Strings auf Gleichheit
// verglichen.
}
}
//----Aufgabe 2 ------------------------------------------------void __fastcall TForm1::Aufg4212Click(TObject *Sender)
{
/*
char *x;
//
//
x = "hello"; //
//
Diese Definition reserviert Speicher für die
Adresse einesZeigers auf char.
Hier wird Speicherplatz für das Literal "hello"
reserviert und dessen Adresse x zugewiesen.
a) Falsch: Es wird kein nicht reservierter Speicherbereich
überschrieben
b) Falsch: Diese Anweisung hat keine Zugriffsverletzung zur Folge.
c) Richtig: a) und b) sind gleichwertig, wenn man vom
unterschiedlichen Zeitpunkt der Zuweisung absieht.
d) Auch diese Deklaration ist möglich. Allerdings hat x hier den
Datentyp "Array mit Elementen des Datentyps char".
*/
}
//----Aufgabe 3 ------------------------------------------------void SiebEratosthenes()
{
const int n=1000;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
155
bool prim[n+1];
// p[i]==true, falls i eine Primzahl ist, sonst false
for (int i=0;i<n;i++) prim[i] = true;
for (int i=2; i<=n; i++)
if (prim[i])
for (int j=i; j<=n/i; j++)
prim[j*i] = false;
for (int i=2; i<=n; i++)
if (prim[i])
Form1->Memo1->Lines->Add(IntToStr(i));
};
void __fastcall TForm1::EratosthenesClick(TObject *Sender)
{
SiebEratosthenes();
}
//----Aufgabe 4 ------------------------------------------------double Geburtagsproblem(int Anzahl_Personen)
{
int g[365];
int Anzahl_Versuche=1000,
Anzahl_Treffer=0;
bool gefunden;
for (int i=1; i<=Anzahl_Versuche; i++)
{
for (int j=0; j<365; j++) g[j] = 0;
for (int j=1; j<=Anzahl_Personen; j++)
g[rand()%365+1]++;
gefunden = false;
for (int j=0; j<365; j++)
if (g[j] > 1) gefunden = true;
if (gefunden) Anzahl_Treffer++;
};
Form1->Memo1->Lines->Add("n="+IntToStr(Anzahl_Personen)+" w="
+FloatToStr(1.0*Anzahl_Treffer/Anzahl_Versuche));
return 1.0*Anzahl_Treffer/Anzahl_Versuche;
}
void __fastcall TForm1::MisesSimulationClick(TObject *Sender)
{
Geburtagsproblem(23);
Geburtagsproblem(23);
Geburtagsproblem(23);
Geburtagsproblem(23);
Geburtagsproblem(23);
Geburtagsproblem(10);
Geburtagsproblem(20);
Geburtagsproblem(30);
/*
Die Simulationsergebnisse mit 1000 Versuchen stimmen recht gut mit den exakten
Werten (in Klammern) überein:
n=23
n=23
n=23
n=23
n=23
w=0,499
w=0,508
w=0,5
w=0,535
w=0,512
n=10 w=0,116
n=20 w=0,414
n=30 w=0,69
// exakter Wert für n=23
p=0,5073
// exakter Wert für n=10
// exakter Wert für n=20
// exakter Wert für n=30
p=0,1169
p=0,4114
p=0,7063
*/
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
156
}
12.3.3 Aufgaben 4.2.2
// Datei: d:\Loesungen\kap-4\4.2\ArrContU.cpp
#include "ArrContU.h"
//----- Aufgabe 1 -----------------------------------------------const int Max=5; // Mit einem kleinen Wert von Max kann man
// auch die Prüfungen auf die Obergrenze
// einfach testen.
AnsiString A[Max];
int Satzzahl=0; // Anzahl der Arrayelemente
// belegt im Array: A[0] .. A[Satzzahl-1]
// a)
void __fastcall TForm1::EinfuegenClick(TObject *Sender)
{
if (Satzzahl<Max)
{
A[Satzzahl]=Edit1->Text;
Satzzahl++;
}
else ShowMessage("Alle Plätze belegt");
}
// b)
void __fastcall TForm1::AnzeigenClick(TObject *Sender)
{
for (int i=0; i<Satzzahl; i++)
Memo1->Lines->Add(A[i]);
}
// c)
void __fastcall TForm1::LinSuchenClick(TObject *Sender)
{
for (int i=0; i<Satzzahl; i++)
if (Edit1->Text==A[i])
Memo1->Lines->Add(A[i]);
}
// d)
int pos(AnsiString s)
{
int p=0;
bool gefunden=false;
while ((p<Satzzahl) && !gefunden)
{
if (A[p]==s) gefunden=true;
else p++;
}
if (gefunden) return p;
else return -1;
}
void __fastcall TForm1::LoeschenClick(TObject *Sender)
{
int p=pos(Edit1->Text);
if ((0<=p) && (p<Satzzahl))
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
{
A[p]=A[Satzzahl-1];
Satzzahl--;
}
else ShowMessage(Edit1->Text+" existiert nicht");
}
// e)
void Asort()
{
for (int i=0; i<Satzzahl-1; i++)
{
int min = i;
for (int j=i+1; j<Satzzahl; j++)
if (A[j]<A[min]) min = j;
AnsiString x = A[i];
A[i] = A[min];
A[min] = x;
}
}
void __fastcall TForm1::SortierenClick(TObject *Sender)
{
Asort();
}
// f)
int BinaerSuchen(AnsiString s)
{
int M;
int L = 0;
int R = Satzzahl-1;
bool gefunden = false;
while ((L<=R) && (!gefunden))
{
// A sortiert ==> A[L] <= A[R]
M = (L + R)/2;
// A[L] <= A[M] <= A[R]
if (A[M]<s) L = M+1;
else if (s<A[M]) R = M-1;
else gefunden = true;
}
if (gefunden) return M;
else return -1;
}
void __fastcall TForm1::BinSuchenClick(TObject *Sender)
{
int pos=BinaerSuchen(Edit1->Text);
if (pos>=0)
Memo1->Lines->Add(A[pos]);
else
Memo1->Lines->Add("nicht gefunden");
}
// g)
void __fastcall TForm1::SortEinfClick(TObject *Sender)
{
AnsiString X=Edit1->Text;
if (Satzzahl<Max)
{
// Die Position p für das neue Element bestimmen
int p=0;
while ((p<Satzzahl) && (A[p]<X)) p++; // Reihenfolge der Bedingungen ist
relevant (Short-Circuit-Evaluation)
// Alle Elemente ab Position p um eine Position nach hinten verschieben
for (int i=Satzzahl-1; i>=p; i--)
A[i+1]=A[i];
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
157
158
12 Lösungen
// Das neue Element in Position p eintragen
A[p]=X;
// Anzahl der Elemente aktualisieren
Satzzahl++;
}
else ShowMessage("Alle Plätze belegt");
}
// h)
void __fastcall TForm1::SortLoeschenClick(TObject *Sender)
{
int p=pos(Edit1->Text);
if ((0<=p) && (p<Satzzahl))
{ // Alle Elemente ab Position p um eine Position nach vorne
for (int i=p; i<Satzzahl-1; i++)
A[i]=A[i+1];
// Anzahl der Elemente aktualisieren
Satzzahl--;
}
else ShowMessage(Edit1->Text+" existiert nicht");
}
//----- Aufgabe 2 -----------------------------------------------const int MaxS=5;
AnsiString Stack[MaxS];
int SPtr=-1;
void __fastcall TForm1::InitStackClick(TObject *Sender)
{
SPtr=-1;
pop->Enabled=false;
top->Enabled=false;
push->Enabled=true;
}
void __fastcall TForm1::pushClick(TObject *Sender)
{
if (SPtr<MaxS)
{
SPtr++;
Stack[SPtr]=Edit2->Text;
if (SPtr==MaxS-1)
push->Enabled=false;
pop->Enabled=true;
top->Enabled=true;
}
}
void __fastcall TForm1::topClick(TObject *Sender)
{
if ((0<=SPtr) && (SPtr<MaxS))
Memo1->Lines->Add(Stack[SPtr]);
}
void __fastcall TForm1::popClick(TObject *Sender)
{
if (SPtr>=0)
{
SPtr--;
if (SPtr==-1)
{
pop->Enabled=false;
top->Enabled=false;
}
push->Enabled=true;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
}
}
//----- Aufgabe 3 -----------------------------------------------const
AnsiString pi1000 = "pi=3" // die ersten 1000 Stellen von pi
"141592653589793238462643383279502884197169399375105820974944"
"592307816406286208998628034825342117067982148086513282306647"
"093844609550582231725359408128481117450284102701938521105559"
"644622948954930381964428810975665933446128475648233786783165"
"271201909145648566923460348610454326648213393607260249141273"
"724587006606315588174881520920962829254091715364367892590360"
"011330530548820466521384146951941511609433057270365759591953"
"092186117381932611793105118548074462379962749567351885752724"
"891227938183011949129833673362440656643086021394946395224737"
"190702179860943702770539217176293176752384674818467669405132"
"000568127145263560827785771342757789609173637178721468440901"
"224953430146549585371050792279689258923542019956112129021960"
"864034418159813629774771309960518707211349999998372978049951"
"059731732816096318595024459455346908302642522308253344685035"
"261931188171010003137838752886587533208381420617177669147303"
"598253490428755468731159562863882353787593751957781857780532"
"1712268066130019278766111959092164201989";
void __fastcall TForm1::TroepfelPiClick(TObject *Sender)
{
const int Stellen = 1000,
Max = 10*(Stellen/3);
int Q[Max+1],U[Max+1],A[Max+1];
AnsiString pistr = "pi=";
TDateTime Start = Time();
for (int i=0; i<=Max; i++) A[i] = 2;
int S;
int qi=0;
for (int n=1; n<=Stellen; n++)
{
U[Max] = 0;
for (int i=Max; i>=1; i--)
{
S = U[i]*i+10*A[i];
A[i]
= S % (2*i-1);
U[i-1] = S / (2*i-1);
}
S = U[0];
A[1] = S % 10;
S
= S / 10;
if (S== 9)
{
Q[qi] = S;
qi++;
}
else if (S==10)
{
for (int j=0; j<=qi-1;j++)
if (Q[j] < 9) Q[j]++;
else Q[j] = 0;
for (int j=0;j<=qi-1;j++)
pistr = pistr+IntToStr(Q[j]);
qi = 1;
Q[0] = 0;
}
else
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
159
160
12 Lösungen
for (int j=0;j<=qi-1;j++)
pistr = pistr+IntToStr(Q[j]);
qi = 1;
Q[0] = S;
}
}
TDateTime Ende = Time();
int i=0; // Vergleiche die beiden Strings zum Test
int j=1;
while ((j < pistr.Length()) && (j < pi1000.Length()))
{
if(pistr[j]==pi1000[j]) i++;
j++;
}
Memo1->Lines->Add(pistr);
Memo1->Lines->Add("n="+IntToStr(Stellen)+" gleich: "+IntToStr(i));
double Laufzeit=24*60*60*(Ende-Start);
Memo1->Lines->Add("Laufzeit="+TimeToStr(Ende-Start)+"
Laufzeit="+FloatToStr(Laufzeit));
}
12.3.4 Aufgaben 4.2.3
// Datei: d:\Loesungen\kap-4\4.2\MehrdimArU.cpp
#include "MehrdimArU.h"
//----- Aufgabe 1 ---------------------------------------------------void __fastcall TForm1::Aufg4231Click(TObject *Sender)
{
// a)
const int m=10, n=20;
int a2[m][n];
int min_i=0, min_j=0;
// Testwerte:
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
a2[i][j]=100-(i+j);
// Bestimme die gesuchten Positionen
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
if (a2[i][j]<a2[min_i][min_j])
{
min_i=i;
min_j=j;
}
Memo1->Lines->Add(IntToStr(min_i)+":"+IntToStr(min_j));
// b)
const int p=10;
int a3[m][n][p];
int min_k=0;
min_i=0, min_j=0;
// Testwerte:
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
for (int k=0; k<p; k++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
a3[i][j][k]=100-(i+j+k);
// Bestimme die gesuchten Positionen
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
for (int k=0; k<p; k++)
if (a3[i][j][k]<a3[min_i][min_j][min_k])
{
min_i=i;
min_j=j;
min_k=k;
}
Memo1->Lines->Add(IntToStr(min_i)+":"+IntToStr(min_j)+":"+IntToStr(min_k));
}
//----- Aufgabe 2 ---------------------------------------------------void __fastcall TForm1::EntfTabelleClick(TObject *Sender)
{
// a)
int d[][3]={{ 0, 10, 20},
{10, 0, 15},
{20, 15, 0}};
int r[] = {4, 2, 1, 2, 0};
int L=0;
for (int i=1; i<r[0]; i++)
L=L+d[r[i]][r[i+1]];
Memo1->Lines->Add("L="+IntToStr(L));
int s[][10]={{3,1,2,0},
// Route 1
{2,1,2},
// Route 2
{5,0,1,2,1,0} // Route 3
};
// b)
int MinL=0, MinInd=0;
for (int k=0; k<3; k++)
{
int L=0;
for (int i=1; i<s[k][0]; i++)
L=L+d[s[k][i]][s[k][i+1]];
if (k==0)
{
MinL=L;
MinInd=0;
}
else if (L<MinL)
{
L=MinL;
MinInd=k;
}
Memo1->Lines->Add("L="+IntToStr(L));
}
}
//----- Aufgabe 3 ---------------------------------------------------const int n=3;
int Zeile_mit_groesstem_Spaltenwert(int i, double a[][n])
{
int Max=i;
for (int j=i+1; j<n; j++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
161
162
12 Lösungen
if (labs(a[j][i])>labs(a[Max][i])) Max =j;
return Max;
}
void vertausche_Zeilen(int z1, int z2, double a[][n], double b[])
{
double h;
for (int i=z1; i<n; i++)
{
h=a[z1][i];
a[z1][i]=a[z2][i];
a[z2][i]=h;
}
h=b[z1];
b[z1]=b[z2];
b[z2]=h;
}
void GaussElimination(double a[][n], double b[], double x[] ,double p[], int n)
{// Koeffizienten: a, rechte Seite: b, Lösung: x, Probe: p
for (int i=0; i<n; i++)
for (int j=i+1; j<n; j++)
{
int z1=Zeile_mit_groesstem_Spaltenwert(i,a);
if (i!=z1) vertausche_Zeilen(i, z1, a, b);
if (a[i][i]==0)
{ // Abbruch
ShowMessage("Nicht lösbar");
return;
}
double f=-a[j][i]/a[i][i];
a[j][i]=0;
for (int k=i+1;k<n; k++)
a[j][k]=a[j][k]+f*a[i][k];
b[j]=b[j]+f*b[i];
}
// rechte Seite "rückwärts" auflösen:
for (int i=n-1; i>=0; i--)
{
x[i]=b[i];
for (int j=n-1; j>i; j--)
x[i]=x[i]-a[i][j]*x[j];
x[i]=x[i]/a[i][i];
}
// Probe:
for (int i=0; i<n; i++)
{
p[i]=-b[i];
for (int j=0; j<n; j++)
p[i]=p[i]+a[i][j]*x[j];
}
}
void Loesung_und_Probe_anzeigen(double x[] ,double p[], int n)
{
for (int i=0; i<n; i++)
{
AnsiString is=IntToStr(i);
Form1->Memo1->Lines->Add("p"+is+"="+FloatToStr(p[i]));
Form1->Memo1->Lines->Add("x"+is+"="+FloatToStr(x[i]));
}
}
void __fastcall TForm1::GaussElimClick(TObject *Sender)
{ // Eliminationsverfahren von Gauß
double a1[n][n]={{1,2,3}, // Koeffizienten
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
{1,4,6},
{2,3,7}};
double b1[n]={1,2,3};
163
// rechte Seite
double a2[n][n]={{2,3,-5}, // Koeffizienten
{4,8,-3},
{-6,1,4}};
double b2[n]={-10,-19,-11},
// rechte Seite
x[n], // Lösung
p[n]; // Probe
GaussElimination(a1, b1, x ,p, n);
Loesung_und_Probe_anzeigen(x, p, n);
GaussElimination(a2, b2, x ,p, n);
Loesung_und_Probe_anzeigen(x, p, n);
}
//-------------------------------------------------------------------void __fastcall TForm1::Aufg424Click(TObject *Sender)
{
#include "Aufg424.cpp"
}
12.3.5 Aufgaben 4.2.4
// Datei: d:\Loesungen\kap-4\4.2\Aufg424.cpp
int a[10]={1,3,5,7}, *p=a;
// *p
// = a[0] = 1
// *p+1
// = a[0]+1 = 2
// (*p)+1 // = a[0]+1 = 2
// *(p+1) // = a[1] = 3
// *(p+3**p) // = a[3] = 7
// *p**p
// = a[0]*a[1] = 1
*(p+*(p+1)); // = a[3] = 7
//int a[10]={1,3,5,7}, *p=a;
Memo1->Lines->Add(*p);
// =
Memo1->Lines->Add(*p+1);
// =
Memo1->Lines->Add((*p)+1); // =
Memo1->Lines->Add(*(p+1)); // =
Memo1->Lines->Add(*(p+3**p)); //
Memo1->Lines->Add(*p**p);
// =
a[0] = 1
a[0]+1 = 2
a[0]+1 = 2
a[1] = 3
= a[3] = 7
a[0]*a[0] = 1
12.3.6 Aufgaben 4.2.5
// Datei: d:\Loesungen\kap-4\4.2\ArrFktParU.cpp
#include "ArrFktParU.h"
//-------- Aufgabe 1 ------------------------------------------------// a)
void AuswSort(int A[], int n)
{
for (int i=0; i<n-1; i++)
{
int min = i;
for (int j=i+1; j<n; j++)
if (A[j]<A[min]) min = j;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
164
12 Lösungen
int x = A[i];
A[i] = A[min];
A[min] = x;
}
}
// b)
void AuswSortPtr(int A[], int n)
{
for (int i=0; i<n-1; i++)
{
int x,min = i;
for (int j=i+1; j<n; j++)
if (*(A+j)<*(A+min)) min = j;
x = *(A+i);
*(A+i) = *(A+min);
*(A+min) = x;
}
}
// c)
AnsiString TestSort(int A[], int n)
{
bool sortiert=true;
for (int i=0; i<n-1; i++)
if (A[i]>A[i+1]) sortiert=false;
if (sortiert) return "sortiert";
else return "nicht";
}
// d)
void Test(int A[], int n)
{
Form1->Memo1->Lines->Add("vorher: "+TestSort(A,n));
AuswSortPtr(A,n);
Form1->Memo1->Lines->Add("nachher: "+TestSort(A,n));
}
void __fastcall TForm1::Aufg31151Click(TObject *Sender)
{
int a1[1]={1};
Test(a1,1);
int a21[2]={1,2};
Test(a21,2);
int a22[2]={2,1};
Test(a22,2);
int a31[3]={0,1,2}; Test(a31,3);
int a32[3]={0,2,1}; Test(a32,3);
int a33[3]={1,0,2}; Test(a33,3);
int a34[3]={1,2,0}; Test(a34,3);
int a35[3]={2,0,1}; Test(a35,3);
int a36[3]={2,1,0}; Test(a36,3);
}
//-------- Aufgabe 2 ------------------------------------------------#include <math.h> // notwendig für poly
void Wertetabelle(double a, double b, double inc, double p[])
{
for (double x=a; x<=b; x=x+inc)
Form1->Memo1->Lines->Add(FloatToStr(x)+" p="+FloatToStr(poly(x,3,p)));
}
void __fastcall TForm1::Aufg31152Click(TObject *Sender)
{
double p[4]={3,-2,1,-1};
Wertetabelle(0, 5, 0.5, p);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
12.3.7 Aufgaben 4.2.6
// Datei: d:\Loesungen\kap-4\4.2\DynArrU.cpp
#include "DynArrU.h"
//-------------------------------------------------------------------int Zeile_mit_groesstem_Spaltenwert(int i, double** a, int n)
{
int Max=i;
for (int j=i+1; j<n; j++)
if (labs(a[j][i])>labs(a[Max][i])) Max =j;
return Max;
}
void vertausche_Zeilen(int z1, int z2, double** a, double* b, int n)
{
double h;
for (int i=z1; i<n; i++)
{
h=a[z1][i];
a[z1][i]=a[z2][i];
a[z2][i]=h;
}
h=b[z1];
b[z1]=b[z2];
b[z2]=h;
}
void GaussElimination(double ** a, double *b, double *x ,double* p, int n)
{ // dieselben Anweisungen wie in Aufgabe 4.2.3.3
for (int i=0; i<n; i++)
for (int j=i+1; j<n; j++)
{
int z1=Zeile_mit_groesstem_Spaltenwert(i,a,n);
if (i!=z1) vertausche_Zeilen(i, z1, a, b,n);
if (a[i][i]==0)
{ // Abbruch
ShowMessage("Nicht lösbar");
return;
}
double f=-a[j][i]/a[i][i];
a[j][i]=0;
for (int k=i+1;k<n; k++)
a[j][k]=a[j][k]+f*a[i][k];
b[j]=b[j]+f*b[i];
}
// rechte Seite "rückwärts" auflösen:
for (int i=n-1; i>=0; i--)
{
x[i]=b[i];
for (int j=n-1; j>i; j--)
x[i]=x[i]-a[i][j]*x[j];
x[i]=x[i]/a[i][i];
}
// Probe:
for (int i=0; i<n; i++)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
165
166
12 Lösungen
p[i]=-b[i];
for (int j=0; j<n; j++)
p[i]=p[i]+a[i][j]*x[j];
}
}
void Loesung_und_Probe_anzeigen(double x[] ,double p[], int n)
{
for (int i=0; i<n; i++)
{
AnsiString is=IntToStr(i);
Form1->Memo1->Lines->Add("p"+is+"="+FloatToStr(p[i]));
Form1->Memo1->Lines->Add("x"+is+"="+FloatToStr(x[i]));
}
}
double **Create2DArray(int Dim1, int Dim2)
{
double** p=new double*[Dim1]; // Dim1 Adressen mit den Adressen
for (int i=0; i<Dim1; i++)
// der Zeilen
p[i]=new double[Dim2];
return p;
}
void Free2DArray(double **A, int n)
{
for (int i=0; i<n; i++)
delete[] A[i];
delete[] A;
}
void test_Gauss()
{
const int n=3;
// nur const für b
double **a=Create2DArray(n,n);
/* a[n][n] und **a sind verschiedene Datentypen. Deswegen kann die Funktion
GaussElimination nicht mit dem Array
double a[n][n]={{1,2,3}, // das geht nicht
{1,4,6},
{2,3,7}};
aufgerufen werden.
*/
// Die Elemente müssen deshalb so initialisiert werden:
a[0][0]=1; a[0][1]=2; a[0][2]=3;
a[1][0]=1; a[1][1]=4; a[1][2]=6;
a[2][0]=2; a[2][1]=3; a[2][2]=7;
// Bei eindimensionalen Arrays sind die Datentypen *a und a[n]
// als Parameter gleichwertig. Deshalb können diese auf beide
// Arten definiert werden:
double b[n]={1,2,3};
// rechte Seite
double *x=new double[n], // Lösung
*p=new double[n]; // Probe
GaussElimination(a, b, x, p, n);
Loesung_und_Probe_anzeigen(x ,p, n);
Free2DArray(a,n);
}
int n; // Dimension der Koeffizientenmatrix
void __fastcall TForm1::FormCreate(TObject *Sender)
{
n=3;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
Edit1->Text=IntToStr(n);
StringGrid1->ColCount=n+1;
StringGrid1->RowCount=n;
StringGrid1->FixedCols=0;
// kein Rand
StringGrid1->FixedRows=0;
StringGrid1->ScrollBars=ssNone; // kein Scrollbars anzeigen
StringGrid1->Options<<goEditing;
}
void __fastcall TForm1::GaussElimClick(TObject *Sender)
{
double **a=Create2DArray(n,n); // Koeffizienten
double* b=new double [n];
// rechte Seite
double* x=new double [n];
// Lösung
double* p=new double [n];
// Probe
// Übernehme die Werte aus dem StringGrid:
// Beachte dabei, daß ein StringGrid [Spalte][Zeile] adressiert und
// nicht wie C++ [Zeile][Spalte]
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
{
if (StringGrid1->Cells[j][i].Trim()=="") StringGrid1->Cells[j][i]="0";
a[i][j]=StrToFloat(StringGrid1->Cells[j][i]);
}
for (int j=0; j<n; j++)
{
if (StringGrid1->Cells[n][j].Trim()=="") StringGrid1->Cells[n][j]="0";
b[j]=StrToFloat(StringGrid1->Cells[n][j]);
}
GaussElimination(a, b, x, p, n);
Loesung_und_Probe_anzeigen(x ,p, n);
Free2DArray(a,n);
}
void __fastcall TForm1::Edit1Change(TObject *Sender)
{ // Lese hier die Größe der Matrix ein (sehr einfach):
if (Edit1->Text.Trim()!="")
{
n=StrToInt(Edit1->Text);
StringGrid1->ColCount=n+1;
StringGrid1->RowCount=n;
// Passe die Größe des StringGrid an:
StringGrid1->Width=(n+1)*StringGrid1->DefaultColWidth+(n+2)*StringGrid1>GridLineWidth;
StringGrid1->Height=n*StringGrid1->DefaultRowHeight+(n+1)*StringGrid1>GridLineWidth;
}
}
12.3.8 Aufgaben 4.3
// Datei: d:\Loesungen\kap-4\4.3\VectorU.cpp
#include "VectorU.h"
//----- Aufgabe 1 -----------------------------------------------#include <algorithm>
using namespace std;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
167
168
12 Lösungen
void __fastcall TForm1::Aufg4321Click(TObject *Sender)
{
// a)
int a[10]={9,8,7,6,5,4,3,2,1,0};
sort(a,a+10);
for (int i=0;i<10; i++)
Memo1->Lines->Add(a[i]);
// b)
int b[10];
copy(a,a+10,b);
for (int i=0;i<10; i++)
Memo1->Lines->Add(b[i]);
// c)
if (equal (a,a+10,b))
Memo1->Lines->Add("gleich");
else
Memo1->Lines->Add("ungleich");
b[1]=17;
if (equal (a,a+10,b))
Memo1->Lines->Add("gleich");
else
Memo1->Lines->Add("ungleich");
}
//----- Aufgabe 2 -----------------------------------------------#include <vector>
#include <list>
// für Aufgabe 4.3.6
#include <deque> // für Aufgabe 4.3.6
using namespace std;
typedef vector<AnsiString> Container;
// typedef list<AnsiString> Container;
// typedef deque<AnsiString> Container;
// für Aufgabe 4.3.6
// für Aufgabe 4.3.6
typedef Container::iterator Iterator;
Container A;
// a)
void __fastcall TForm1::EinfuegenClick(TObject *Sender)
{
A.push_back(Edit1->Text);
}
// b)
void __fastcall TForm1::AnzeigenClick(TObject *Sender)
{
for (Iterator i=A.begin(); i!=A.end(); i++)
Memo1->Lines->Add(*i);
/* // Die folgende Variante ist ebenso möglich:
for (int i=0; i<A.size(); i++)
Memo1->Lines->Add(A[i]);
*/
}
// c)
void __fastcall TForm1::LinSuchenClick(TObject *Sender)
{
for (Iterator i=A.begin(); i!=A.end(); i++)
if (*i==Edit1->Text)
Memo1->Lines->Add(*i);
}
// d)
void __fastcall TForm1::LoeschenClick(TObject *Sender)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
Iterator i=find(A.begin(),A.end(),Edit1->Text);
if (i!=A.end()) A.erase(i);
else Memo1->Lines->Add("nicht gefunden");
}
// e)
void __fastcall TForm1::SortierenClick(TObject *Sender)
{
sort(A.begin(),A.end()); // Für vector und deque
// A.sort(); // Für list
}
// f)
void __fastcall TForm1::BinSuchenClick(TObject *Sender)
{
if (binary_search(A.begin(), A.end(), Edit1->Text))
Memo1->Lines->Add(Edit1->Text);
else
Memo1->Lines->Add("nicht gefunden");
}
// g)
void __fastcall TForm1::SortEinfClick(TObject *Sender)
{
Iterator i=lower_bound(A.begin(), A.end(),Edit1->Text);
A.insert(i,Edit1->Text);
}
// h)
void __fastcall TForm1::SortLoeschenClick(TObject *Sender)
{
Iterator i=lower_bound(A.begin(), A.end(),Edit1->Text);
if (i!=A.end())
A.erase(i);
// Auch der folgende Aufruf ist möglich:
// LoeschenClick(Sender);
// Er ist aber nicht so schnell, da linear und nicht binär gesucht wird.
}
//----- Aufgabe 3 -----------------------------------------------#include <string>
#include <vector>
using namespace std;
void parseString(const string& s, vector<string>& v, const string&
Separatoren=";.,- ")
{
// Form1->Memo1->Lines->Add("Test: "+AnsiString(s.c_str()));
string::size_type pos_tok=s.find_first_not_of(Separatoren);
while (pos_tok!=string::npos)
{
string::size_type pos_sep=s.find_first_of(Separatoren,pos_tok);
if (pos_sep==string::npos)
pos_sep=s.length();
string token=s.substr(pos_tok,pos_sep-pos_tok);
v.push_back(token);
// Form1->Memo1->Lines->Add("'"+AnsiString(token.c_str())+"'");
pos_tok=s.find_first_not_of(Separatoren,pos_sep+1);
}
// Form1->Memo1->Lines->Add("Test Ende"+AnsiString(s.c_str()) );
}
int FindFirstOf(const AnsiString s, const AnsiString Delimiter,int start)
{
for (int i=start; i<=s.Length(); i++)
if (s.IsDelimiter(Delimiter,i)) return i;
return -1;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
169
170
12 Lösungen
}
int FindFirstNotOf(const AnsiString s, const AnsiString Delimiter, int start)
{
for (int i=start; i<=s.Length(); i++)
if (!s.IsDelimiter(Delimiter,i)) return i;
return -1;
}
void parseString(const AnsiString& s, vector<AnsiString>& v, const AnsiString&
Separatoren=";.,- ")
{
// Form1->Memo1->Lines->Add("Test: "+s);
;
int pos_tok=FindFirstNotOf(s,Separatoren,1);
while (pos_tok!=-1)
{
int pos_sep=FindFirstOf(s,Separatoren,pos_tok+1);
if (pos_sep==-1)
pos_sep=s.Length()+1;
AnsiString token=s.SubString(pos_tok,pos_sep-pos_tok);
// Form1->Memo1->Lines->Add("'"+token+"'");
v.push_back(token);
pos_tok=FindFirstNotOf(s,Separatoren,pos_sep+1);
}
// Form1->Memo1->Lines->Add("Test Ende"+s);
}
void testParseString(AnsiString s)
{
vector<AnsiString> v;
Form1->Memo1->Lines->Add("Test: "+s);
parseString(s,v);
for (unsigned int i=0; i<v.size(); i++)
Form1->Memo1->Lines->Add("'"+v[i]+"'");
Form1->Memo1->Lines->Add("Test Ende"+s);
Form1->Memo1->Lines->Add("");
}
void testParseString(string s)
{
vector<string> v;
Form1->Memo1->Lines->Add("Test: "+AnsiString(s.c_str()));
parseString(s,v);
for (unsigned int i=0; i<v.size(); i++)
Form1->Memo1->Lines->Add("'"+AnsiString(v[i].c_str())+"'");
Form1->Memo1->Lines->Add("Test Ende"+AnsiString(s.c_str()));
Form1->Memo1->Lines->Add("");
}
void testParseStrings()
{
vector<string> vs;
testParseString(AnsiString("456 ab.xy"));//Ausgabe "456", "ab", "xy"
testParseString(
string("456 ab.xy"));//Ausgabe "456", "ab", "xy"
// return;
testParseString(AnsiString("123")); // nur token, Ausgabe "123"
testParseString(
string("123")); // nur token, Ausgabe "123"
testParseString(AnsiString(""));
// leerer String , keine Ausgabe
testParseString(
string(""));
// leerer String , keine Ausgabe
testParseString(AnsiString("789 ab.xy;"));// Sep am Anfang und am Ende
testParseString(
string("789 ab.xy;"));// Sep am Anfang und am Ende
// Ausgabe "789","ab","xy"
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
// #include "\Loesungen\CppUtils\StringUt.cpp"
void __fastcall TForm1::Aufg4323Click(TObject *Sender)
{
testParseStrings();
}
//----- Aufgabe 4.3.6 -----------------------------------------------// In der Lösung von Aufgabe 2 wird nur der Datentyp des
// Containers geändert.
12.3.9 Aufgabe 4.4.1 a)
// Datei: d:\Loesungen\CppUtils\KBDecl.cpp
#ifndef KBDeclH
#define KBDeclH
struct CDatum {
int Tag;
int Monat;
int Jahr;
};
const MaxLaengeNameInhaber=20;
struct CKontobewegung {
int KontoNr;
char NameInhaber[MaxLaengeNameInhaber]; // nur für einige Dateiaufgaben
// als Array - sonst ist string bzw. AnsiString besser
CDatum Datum;
char BewArt;
// Currency Betrag; // Eigentlich angemessener als double.
double Betrag;
// Aber mit double geht später auch <<
};
AnsiString KBToString(CKontobewegung K)
{
return IntToStr(K.KontoNr)+" "+AnsiString(K.NameInhaber)+" "+
AnsiString(K.Datum.Tag)+"."+AnsiString(K.Datum.Monat)+"."+
AnsiString(K.Datum.Jahr)+" "+
AnsiString(K.BewArt)+" "+FloatToStr(K.Betrag);
}
// ----- Aufgabe 4.6.1.2 c) -----------------------------------------// ----- Funktionen für Zufallsdaten ---// #include <stdlib.h> // für BCB 3 notwendig
bool Schaltjahr(int Jahr)
{
return (Jahr%4==0)||((Jahr%100==0)&&(Jahr%400==0));
}
int Zuf(int a, int b)
{ // int random(int n): Zufallszahl im Bereich 0..n-1
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
171
172
// return a + random(b-a+1);
return a + rand()%(b-a+1);
}
int eindeutige_Kontonummer=0;
CKontobewegung Zufalls_KB()
{
const int Max_n = 10; // Anzahl der Vor- bzw. Nachnamen
AnsiString Nachname[Max_n] =
{"Duestrip, ","Sander, ", "König, ", "Blond, ",
"Schluck, ", "Parker, ", "Kannt, ", "Pascal, ",
"Coldrain, ","Prince, "};
AnsiString Vorname[Max_n] =
{"Daniel",
"Alek",
"Q.",
"James",
"Donald",
"Karl",
"Imanuel", "Nikolausi",
"John",
"Charlie"};
CKontobewegung K;
memset(&K, 0, sizeof(K));
eindeutige_Kontonummer++;
K.KontoNr = eindeutige_Kontonummer;
// Für manche Aufgaben ist es sinnvoller, wenn man eindeutige Datensätze
// hat. Bei anderen sind zufällige Schlüsselbegriffe sinnvoller. Je nach
// Bedarf kann man die nächste Anweisung auskommentieren:
K.KontoNr = Zuf(1000,1099);
int z = K.KontoNr % 100;
AnsiString Name=Nachname[z/Max_n]+Vorname[z%Max_n];
strcpy(K.NameInhaber,Name.c_str());
K.Datum.Jahr = Zuf(2000,2001);
K.Datum.Monat = Zuf(1,12);
if ((K.Datum.Monat == 4) ||
(K.Datum.Monat == 6) ||
(K.Datum.Monat == 9) ||
(K.Datum.Monat ==11)) K.Datum.Tag = Zuf(1,30);
else if (K.Datum.Monat == 2)
{
if (Schaltjahr(K.Datum.Jahr))
K.Datum.Tag = Zuf(1,28);
else K.Datum.Tag = Zuf(1,29);
}
else K.Datum.Tag = Zuf(1,31);
if (Zuf(0,1)==0) K.BewArt = '+';
else K.BewArt = '-';
K.Betrag = Zuf(1,30000)/100.0; // ".0" für Gleitkomma-Wert
return K;
}
#endif
// Datei: d:\Loesungen\CppUtils\KBForms.cpp
#ifndef KBFormsH
#define KBFormsH
// Für diese Funktionen muß das Formular den Namen
// 'KBForm1' bekommen (im Objektinspektor setzen).
void KBToForm(CKontobewegung K,TKBForm1* Form1)
{ // überträgt die Daten von K in das Formular Form1
Form1->EKontoNr->Text=IntToStr(K.KontoNr);
Form1->EName->Text=K.NameInhaber;
// Entweder
Form1->EDatum->Text=IntToStr(K.Datum.Tag)+"."+
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
173
IntToStr(K.Datum.Monat)+"."+
IntToStr(K.Datum.Jahr);
// oder
unsigned short year= K.Datum.Jahr;
unsigned short month=K.Datum.Monat;
unsigned short day= K.Datum.Tag;
TDateTime d=TDateTime(year, month, day);
// Form1->EDatum->Text=d.DateString();
Form1->EBewart->Text=K.BewArt;
Form1->EBetrag->Text=FloatToStr(K.Betrag);
}
CKontobewegung FormToKB(TKBForm1* Form1)
{ // überträgt die Daten des Formulars Form1 in den Funktionswert
CKontobewegung K;
K.KontoNr = StrToInt(Form1->EKontoNr->Text);
strcpy(K.NameInhaber,Form1->EName->Text.c_str());
// Entweder wie in Aufgabe 4.1.1
AnsiString Datum = Form1->EDatum->Text;
AnsiString tstr = Datum.SubString(1,Datum.Pos('.')-1);
K.Datum.Tag = StrToInt(tstr);
Datum.Delete(1,Datum.Pos('.'));
int p = Datum.Pos('.');
AnsiString mstr = Datum.SubString(1,p-1);
K.Datum.Monat = StrToInt(mstr);
AnsiString jstr = Datum.SubString(p+1,Datum.Length()-p+1);
K.Datum.Jahr = StrToInt(jstr);
// oder
TDateTime d;
d = StrToDate(Form1->EDatum->Text);
unsigned short year, month, day;
d.DecodeDate(&year, &month, &day);
K.Datum.Tag=day;
K.Datum.Monat=month;
K.Datum.Jahr=year;
// Ende 'oder'
Form1->EBewart->Text.Trim();
if (Form1->EBewart->Text.Length() >= 1)
K.BewArt =Form1->EBewart->Text[1];
K.Betrag = StrToCurr(Form1->EBetrag->Text);
return K;
}
// Die Funktion ClearKBForm ist nützlich, um Eingabefelder
// auf Werte zu setzen, die beim Aufruf von FormToKB
// keine Fehler verursachen.
void ClearKBForm(TKBForm1* Form1)
{
Form1->EKontoNr->Text="0";
Form1->EName->Text="";
TDateTime d=d.CurrentDate();
Form1->EDatum->Text=d.DateString();
Form1->EBewart->Text="-";
Form1->EBetrag->Text="0,00";
}
#endif
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
174
12.3.10 Aufgaben 4.4.1
// Datei: d:\Loesungen\kap-4\4.4\StructU.cpp
#include "StructU.h"
TKBForm1 *KBForm1;
__fastcall TKBForm1::TKBForm1(TComponent* Owner)
: TForm(Owner)
{
}
//------ Aufgabe 1 a) -----------------------------------------------// Damit die Datenstruktur CKontobewegung auch noch in anderen
// Programmen verwendet werden können, in eine eigene Datei:
#include "\Loesungen\CppUtils\KBDecl.cpp"
// Damit die Funktionen FormToKB und KBToForm auch noch in anderen
// Programmen verwendet werden können, in eine eigene Datei:
#include "\Loesungen\CppUtils\KBForms.cpp"
// Für die Funktionen KBToForm und FormToKB muß das Formular
// den Namen "KBForm1" haben (im Objektinspektor setzen).
#include<vector>
#include<deque>
#include<list>
using namespace std;
typedef vector<CKontobewegung> Container;
// typedef list<CKontobewegung> Container; // geht nicht
// typedef deque<CKontobewegung> Container; // das geht auch
typedef Container::iterator Iterator;
Container c;
Iterator pos=c.begin();
//------ Aufgabe 1 g) -----------------------------------------------void EnableButtons()
{
KBForm1->first->Enabled =!c.empty();
KBForm1->last->Enabled =!c.empty();
KBForm1->prev->Enabled=(!c.empty()) && (pos>c.begin());
KBForm1->next->Enabled=(!c.empty()) && (pos<c.end()-1);
KBForm1->Loeschen->Enabled =(!c.empty()) ;//&& (pos<=c.end()-1);
// Speichern ist immer enabled und muss nie geändert werden
// i)
if (c.empty())
KBForm1->Label1->Caption="Container leer";
else
KBForm1->Label1->Caption="Satz Nr. "+IntToStr(pos-c.begin()+1)+
" von "+IntToStr(c.end()-c.begin());
}
void __fastcall TKBForm1::FormCreate(TObject *Sender)
{
ClearKBForm(KBForm1); // nur zum Testen, damit man nicht so viele Werte
// eingeben muss
EnableButtons();
}
//------ Aufgabe 1 b) ------------------------------------------------
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
void __fastcall TKBForm1::SpeichernClick(TObject *Sender)
{
c.push_back(FormToKB(KBForm1));
/*
/*
/*
/*
h)
h)
h)
h)
*/
*/
*/
*/
// Durch das Einfügen wird pos ungültig. Deshalb
// auf eine Position (z.B. die des angezeigten
// Datensatzes, also des letzten - wegen push_back) setzen.
pos=c.end()-1;
EnableButtons();
}
//------ Aufgabe 1 c) -----------------------------------------------void __fastcall TKBForm1::prevClick(TObject *Sender)
{ // Da dieser Button nur unter der Bedingung 'pos>c.begin()' enabled
// ist, braucht diese Bedingung hier nicht mit 'if' geprüft werden.
// Dazu muss die Funktion EnableButtons nach jeder Veränderung
// des Containers oder von pos aufgerufen werden.
if (pos>c.begin()) // überflüssig
{
pos--;
KBToForm(*pos,KBForm1);
EnableButtons();
}
}
//------ Aufgabe 1 d) -----------------------------------------------void __fastcall TKBForm1::nextClick(TObject *Sender)
{ // Siehe den Kommentar in 'prevClick'
if (pos<c.end()-1) // überflüssig
{
pos++;
KBToForm(*pos,KBForm1);
}
EnableButtons();
}
//------ Aufgabe 1 e) -----------------------------------------------void __fastcall TKBForm1::firstClick(TObject *Sender)
{ // Siehe den Kommentar in 'prevClick'
if (!c.empty())
{
pos=c.begin();
KBToForm(*pos,KBForm1);
}
else ClearKBForm(KBForm1); // Container leer
EnableButtons();
}
void __fastcall TKBForm1::lastClick(TObject *Sender)
{ // Siehe den Kommentar in 'prevClick'
if (!c.empty())
{
pos=c.end()-1;
KBToForm(*pos,KBForm1);
}
else ClearKBForm(KBForm1); // Container leer
EnableButtons();
}
//------ Aufgabe 1 f) -----------------------------------------------void __fastcall TKBForm1::LoeschenClick(TObject *Sender)
{
/* h) */ // Durch das Löschen wird pos ungültig. Deshalb 'pos' auf
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
175
176
/* h) */ pos=c.erase(pos); // eine Position setzen, damit der Iterator
// auch nach dem Löschen wieder gültig ist.
if (pos!=c.end())
KBToForm(*pos,KBForm1);
else // pos == c.end(), d.h. das letzte Element wurde gelöscht
{
if (!c.empty())
{
pos--;
KBToForm(*pos,KBForm1);
}
else // das einzige Element wurde gelöscht
ClearKBForm(KBForm1);
}
EnableButtons();
}
//------ Aufgabe 1 h) -----------------------------------------------// In den Lösungen von b) und f) enthalten
//------ Aufgabe 1 i) -----------------------------------------------// In EnableButtons() enthalten.
//------ Aufgabe 2 b) -----------------------------------------------const int StrLaenge=20; // am einfachsten alle gleichlang
struct GT {
struct {
char Anrede[StrLaenge];
struct {
char Vorname[StrLaenge],
Nachname[StrLaenge];
} Name;
struct {
char PLZ[StrLaenge], // wegen Ausland als String
Ort[StrLaenge],
Strasse[StrLaenge],
Hausnummer[StrLaenge];// wegen 7b
} Anschrift;
char Ausland[StrLaenge];
struct {
char Vorwahl[StrLaenge]; // wegen führender Nullen
char Telnr[StrLaenge];
} Telefon;
}Adresse;
int Kontonr;
Currency Kontostand,
Kreditlimit;
};
void test_G()
{
//------ Aufgabe 2 c) -----------------------------------------------GT G;
G.Kontonr=1000;
strcpy(G.Adresse.Name.Vorname, "Richard");
strcpy(G.Adresse.Anschrift.PLZ, "72070");
//------ Aufgabe 2 d) -----------------------------------------------GT* GP=new GT;
GP->Kontonr=1000;
strcpy(GP->Adresse.Name.Vorname, "Richard");
strcpy(GP->Adresse.Anschrift.PLZ, "72070");
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
//------ Aufgabe 3 --------------------------------------------------struct TWertepaar {
int Erzielt,
Eingefangen;
};
struct {
int Position;
char Mannschaftsname[40];
TWertepaar Punkte,
Tore,
Heimtore,
Auswaertstore;
} B;
void test_B()
{
B.Position=17;
B.Punkte.Erzielt=18;
B.Punkte.Eingefangen=19;
TWertepaar W={1,2};
B.Punkte=W;
}
12.3.11 Aufgabe 4.4.2
// Datei: d:\Loesungen\kap-4\4.4\ListU.cpp
#include "ListU.h"
typedef AnsiString DataType ; // Datentyp der Listendaten
// DataType muss vor dem include deklariert werden.
// #include "\Loesungen\CppUtils\List.cpp"
struct list_node {
DataType data; // Datenwerte
list_node* next;
};
void insert_list_node(list_node*& first, list_node*& last,DataType data)
{ /* Erzeugt einen neuen Listen-Knoten und fügt diesen
nach last ein. Last zeigt anschließend auf das
letzte Element. */
list_node* tmp=new list_node;
tmp->data = data;
tmp->next = 0;
if (last == 0) first = tmp;
else last->next = tmp;
last = tmp;
}
void erase(list_node*& position)
{ // list.h
if (position==0) return; // oder Fehlermeldung
list_node* tmp = position;
position = position->next;
delete tmp;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
177
178
12 Lösungen
}
void clear(list_node*& first, list_node*& last)
{ // list.h
while (first!=0) erase(first);
last=0;
}
list_node *first=0, *last=0;
void __fastcall TForm1::ListInsertClick(TObject *Sender)
{
insert_list_node(first, last, Edit1->Text);
}
void __fastcall TForm1::ShowListClick(TObject *Sender)
{
for (list_node* i=first; i!=0; i=i->next)
Memo1->Lines->Add(i->data);
}
void __fastcall TForm1::ClearMemClick(TObject *Sender)
{
clear(first,last);
}
//----- Aufgabe 4.4.3 -----------------------------------------------// Aktivieren Sie CodeGuard unter Projekt-Optionen CodeGuard und beenden
// Sie dieses Programm ohne einen Aufruf von ClearMemClick. Dann erhalten
// Sie etwa das folgende Protokoll list.cgl:
/*
Fehler 00029. 0x300010 (Thread 0xFFFC0121):
Ressourcen-Leck: Objekt (0xFD5030) wurde nie gel÷scht
Objekt (0x00FD5030) [Gr÷_e: 8 Byte] war erzeugt mit new
Aufrufhierarchie:
0x004017C2(=LIST.EXE:0x01:0007C2) \CppUtils\List.cpp#10
0x00401B41(=LIST.EXE:0x01:000B41) d:\Loesungen\kap-4\4.4\ListU.cpp#28
0x00428EA8(=LIST.EXE:0x01:027EA8)
0x00408E99(=LIST.EXE:0x01:007E99)
0x0042B8C4(=LIST.EXE:0x01:02A8C4)
0x0041C2B7(=LIST.EXE:0x01:01B2B7)
-----------------------------------------Fehler 00030. 0x300010 (Thread 0xFFFC0121):
Ressourcen-Leck: Objekt (0xFD5010) wurde nie gelöscht
Objekt (0x00FD5010) [Gr÷_e: 8 Byte] war erzeugt mit new
Aufrufhierarchie:
0x004017C2(=LIST.EXE:0x01:0007C2) \CppUtils\List.cpp#10
0x00401B41(=LIST.EXE:0x01:000B41) d:\Loesungen\kap-4\4.4\ListU.cpp#28
0x00428EA8(=LIST.EXE:0x01:027EA8)
0x00408E99(=LIST.EXE:0x01:007E99)
0x0042B8C4(=LIST.EXE:0x01:02A8C4)
0x0041C2B7(=LIST.EXE:0x01:01B2B7)
-----------------------------------------Fehler 00031. 0x300010 (Thread 0xFFFC0121):
Ressourcen-Leck: Objekt (0xFD5050) wurde nie gel÷scht
Objekt (0x00FD5050) [Gr÷_e: 8 Byte] war erzeugt mit new
Aufrufhierarchie:
0x004017C2(=LIST.EXE:0x01:0007C2) \CppUtils\List.cpp#10
0x00401B41(=LIST.EXE:0x01:000B41) d:\Loesungen\kap-4\4.4\ListU.cpp#28
0x00428EA8(=LIST.EXE:0x01:027EA8)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
0x00408E99(=LIST.EXE:0x01:007E99)
0x0042B8C4(=LIST.EXE:0x01:02A8C4)
0x0041C2B7(=LIST.EXE:0x01:01B2B7)
*/
// Mit einem Aufruf von ClearMemClick. Dann erhalten Sie dagegen nur etwa
// das folgende Protokoll list.cgl:
/*
Aufgerufene Funktionen:
fflush (4 Mal)
delete (11 Mal)
free (7 Mal)
SysReallocMem (5 Mal)
new (11 Mal)
SysFreeMem (414 Mal)
SysGetMem (414 Mal)
lstrlenA (1 Mal)
lstrcpyA (4 Mal)
calloc (1 Mal)
strlen (21 Mal)
realloc (1 Mal)
strdup (1 Mal)
malloc (4 Mal)
memcpy (2 Mal)
Verwendete Ressourcen-Arten:
Objekt (11 Allocs, 11 max)
Speicherblock (421 Allocs, 260 max)
Verwendete Module:
00400000 10/11/2000 23:17:18 d:\Loesungen\kap-4\4.4\ListU.EXE
==========================================
*/
12.3.12 Aufgabe 4.4.7
// Datei: d:\Loesungen\kap-4\4.4\BitFeldU.cpp
#include "BitFeldU.h"
//----- Aufgabe 4.4.8 -----------------------------------------------#include <bitset>
#include <string>
using namespace std;
AnsiString BinaerDarst(unsigned int n, int max)
{
AnsiString s;
for (int i=1; i<=max; i++)
{
if (n%2==1) s = "1"+s;
else s = "0"+s;
n = n/2;
}
return s;
}
AnsiString DoubleBitmuster(double d)
{
union Udouble {
double d;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
179
180
struct {
unsigned
unsigned
unsigned
unsigned
} s;
} u;
u.d=d;
12 Lösungen
int
int
int
int
mant2;
mant1:20;
exp:11;
sign:1;
AnsiString s=FloatToStr(d)+
"S:"+BinaerDarst(u.s.sign,1)+
"E:"+BinaerDarst(u.s.exp,11)+
"M:"+BinaerDarst(u.s.mant1,20)+
BinaerDarst(u.s.mant2,32)+" e="+IntToStr(u.s.exp)+" e="+IntToStr(u.s.exp1023);
return s;
}
void __fastcall TForm1::BitmusterClick(TObject *Sender)
{
Memo1->Lines->Add(DoubleBitmuster(0.1));
double x=0;
for (int i=0; i<10; i++) x=x+0.1;
Memo1->Lines->Add(DoubleBitmuster(x));
Memo1->Lines->Add(DoubleBitmuster(1));
Memo1->Lines->Add(DoubleBitmuster(2));
Memo1->Lines->Add(DoubleBitmuster(3));
Memo1->Lines->Add(DoubleBitmuster(4));
}
12.3.13 Aufgabe 4.5.1
// Datei: d:\Loesungen\kap-4\4.5\GrafikU.cpp
#include "GrafikU.h"
//------ Aufgaben 4.5.1 ---------------------------------------------//------ Aufgabe 1 --------------------------------------------------#include <vcl\printers.hpp>
void PrintMemo()
{
if (Form1->FontDialog1->Execute())
Printer()->Canvas->Font->Assign(Form1->FontDialog1->Font);
Printer()->BeginDoc(); // Initialisiert Druckauftrag
int y=0;
// y-Position der Zeile
int ZA=10;
// Zeilenabstand
int LR=10;
// linker Rand
int Seite=0; // aktuelle Seitenzahl
for (int i=0; i<Form1->Memo1->Lines->Count;i++)
{
AnsiString z=Form1->Memo1->Lines->Strings[i];
if ((y+Printer()->Canvas->TextHeight(z) >
Printer()->PageHeight) || (Seite==0))
{
if (Seite>0) Printer()->NewPage();
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
181
Seite++;
AnsiString Kopfzeile="Kopfzeile
Seite: "+IntToStr(Seite);
Printer()->Canvas->TextOut(LR,0,Kopfzeile);
y=100; // Abstand zum Text
}
Printer()->Canvas->TextOut(LR,y,z);
y=y+ZA+Printer()->Canvas->TextHeight(z);
}
Printer()->EndDoc(); // Beendet Druckauftrag
}
void __fastcall TForm1::DruckeMemoClick(TObject *Sender)
{
PrintMemo();
}
//------ Aufgabe 2 --------------------------------------------------// Die Lösung der Aufgaben a) bis c) ist in der Datei GraphUtils.cpp
// enthalten:
#include "\Loesungen\CppUtils\GraphUtils.cpp";
//------ Aufgabe 2 d) -----------------------------------------------int Funktion=0;
void __fastcall TForm1::Function1Click(TObject *Sender)
{
Funktion++;
if (Funktion >5) Funktion=1;
double x0, y0, x1, y1, dx, dy;
if
(Funktion==1) { x0=-4; y0=-1; x1=4; y1=1;
dx=1;
else if (Funktion==2) { x0=-1; y0=0; x1=6; y1=100; dx=1;
else if (Funktion==3) { x0=-2; y0=-2; x1=2; y1=4;
dx=1;
else if (Funktion==4) { x0= 0; y0=0; x1=4; y1=1;
dx=1;
else if (Funktion==5) { x0=-2; y0=-6; x1=8; y1=10; dx=1;
dy=0.2; }
dy=10; }
dy=1; }
dy=0.2; }
dy=1; }
Image1->Canvas->Pen->Color = clRed;
for (int px=0; px<=Form1->Image1->Width-/*–*/1; px++)
{
// transformiere px in Welt-Koordinaten
double x=x_Welt(px, x0, x1, Image1->Width);
double y;
if
(Funktion==1) y= sin(x*x);
else if (Funktion==2) y= exp(x);
else if (Funktion==3) y= x*x;
else if (Funktion==4) y= 1/(1+x*x);
else if (Funktion==5) y= x*sin(x);
// transformiere y in Bildkoordinaten
int py=y_Bildschirm(y, y0, y1, Image1->Height);
if (0) Image1->Canvas->Pixels[px][py]=clRed;
else
if (px == 0) Form1->Image1->Canvas->MoveTo(px,py);
else
Form1->Image1->Canvas->LineTo(px,py);
}
Gitternetz(Image1, x0,x1,dx, Image1->ClientWidth,
y0,y1,dy, Image1->ClientHeight);
}
void __fastcall TForm1::ClearImagClick(TObject *Sender)
{
ClearImage(Image1);
TColor PenAlt = Image1->Canvas->Pen->Color;
Image1->Canvas->Brush->Color=clWhite;
Image1->Canvas->Pen->Color=clWhite;
Image1->Canvas->Rectangle(0,0,Image1->ClientWidth,Image1->ClientHeight);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
182
12 Lösungen
Image1->Canvas->Pen->Color=PenAlt;
}
//----- Aufgabe 3 -----------------------------------------------/*
Wie die Aufgabe mit der Fakultät schon gezeigt hat, wachsen die Fakultäten
sehr schnell an und liegen bereits ab n=13 außerhalb des mit 32-bit
darstellbaren Ganzzahlbereichs. Deshalb ist die folgende Funktion nur
für relativ kleine n einsetzbar:
*/
long double fak(int n)
{
long double result=1;
for (int i=1; i<n; i++) result=result*i;
return result;
}
int binom__(int n, int k)
{ // funktioniert ebenfalls für große Werte
return fak(n)/(fak(k)*fak(n-k));
//bin(n,k) = n!/(k!*(n-k)!)
};
/*
Eine genauere Betrachtung der Formel für bin(n,k) zeigt, dass sich zwei
Faktoren im Zähler und im Nenner immer wegkürzen. Damit erhält man mit
*/
int binom_(int n, int k)
{
int result = 1;
for (int i=1; i<k; i++)
result = result * (n-i+1) / i;
return result;
};
/*
auch für größere Werte von n und k korrekte Ergebnisse. Allerdings erhält
man auch mit dieser Version ab n=30 negative Werte. Verwendet man hier
Gleitkommazahlen, erhält man auch für große Werte von n und k richtige
Ergebnisse:
*/
long double binom(int n, int k)
{
long double result = 1;
for (int i=1; i<k; i++)
result = result * (n-i+1) / i;
return result;
};
/*
Da die Summe binom(n,0)+binom(n,1)+ ... binom(n,n) immer den Wert 2^n
ergeben muss, kann man mit der Funktion binom_test
prüfen, ob die Ergebnisse dieser Funktion plausibl sind. Ein Test zeigt,
dass man auch für n=10000 richtige Werte erhält.
*/
long double binom_test(int n)
{ // Summe muß "2 hoch n" sein
long double result=0;
for (int i=0; i<=n; i++)
result=result+binom(n,i);
return result;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
183
void __fastcall TForm1::BinomKoeffClick(TObject *Sender)
{
// Zum Testen:
// for (int i=5000; i<5020; i++)
// Memo1->Lines->Add(FloatToStr(binom_test(i))+" "+FloatToStr(powl(2.0,i))+"
"+FloatToStr(Potenz(2.0,i)));
AnsiString s;
for (int n=200; n<210; n++)
{
s = "";
for (int k=0; k<n; k++)
{
s=s+FloatToStr(binom(n,k))+" ";
Memo1->Lines->Add(s);
};
}
}
int round(double x)
{
return x+0.5;
}
void __fastcall TForm1::HistogrammClick(TObject *Sender)
{
int n=1000; // 100
Image1->Canvas->Pen->Color = clBlack;
int w = Image1->Width;
int h = Image1->Height;
double max = binom(n,n/2);
for (int i=0; i<n; i++)
Image1->Canvas->Rectangle(i*w /(n+1),h-0,
(i+1)*w /(n+1),round(h-h*(binom(n,i)/max)));
};
//------ Aufgabe 4 --------------------------------------------------double t=0;
// Inkrement für die Figuren
const int epKreis1=0, epKreis2=1, epKreis3=2, epZufall=3;
int Endpunkte=epKreis1; // Art der Endpunkte
const int fGerade=0, fEllipse=1, fRechteck=2;
int Figur=fGerade; // Geraden=0, Kreise=1, Rechteck=2
// für b):
int color=0;
// Farbe, wird zyklisch durchlaufen
// für c):
const int MaxF=50; // maximale Anzahl der Figuren
const int Endpunkte_pro_Figur=2;
int p[MaxF][2*Endpunkte_pro_Figur]; // Koordinaten der Endpunkte
int ind=0;
// Index der Punkte im Array p
int Max=MaxF;
// Anzahl der Figuren, die stehenbleiben
void init_p()
{ // z. B. beim Ereignis OnCreate des Formulars aufrufen
Form1->Timer1->Interval=100; // 10 Mal pro Sekunde
for (int i=0; i<MaxF; i++)
for (int j=0; j<2*Endpunkte_pro_Figur; j++)
p[i][j]=-1;
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
init_p();
}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
184
12 Lösungen
// Mittelpunkt und Radius so, daß es ins Bild paßt
int x=Image1->ClientWidth/2;
int y=Image1->ClientHeight/2;
int r=(x+y)/4;
// c) Lösche die alten Figuren wieder
if (p[ind]>0)
{
Form1->Image1->Canvas->Pen->Color=clWhite;
if (Figur==fGerade)
{ // lösche die alte Gerade
Form1->Image1->Canvas->MoveTo(p[ind][0],p[ind][1]);
Form1->Image1->Canvas->LineTo(p[ind][2],p[ind][3]);
}
else if (Figur==fEllipse) // lösche den alten Kreis
Form1->Image1->Canvas->Ellipse(p[ind][0],p[ind][1],p[ind][2],p[ind][3]);
else if (Figur==fRechteck) // lösche das alte Rechteck
Form1->Image1->Canvas->Rectangle(p[ind][0],p[ind][1],p[ind][2],p[ind][3]);
}
// b) Die Farben zyklisch durchlaufen:
color=color+1;
if (color >= 20) color=0;
Image1->Canvas->Pen->Color=PALETTEINDEX(color);
// Anstelle von PALETTEINDEX(color) kann auch ein Array oder eine Funktion
// verwendet werden, die mehr bzw. "schönere" Farben enthält.
if (Endpunkte==epKreis1)
{ // Endpunkte auf einem Kreis
t = t+0.05;
p[ind][0]=x+r*sin(t);
p[ind][1]=y+r*cos(t);
p[ind][2]=x+r*sin(2*t);
p[ind][3]=y+r*cos(2+t);
}
else if (Endpunkte==epKreis2)
{ // ähnlich wie Animation==0
t = t+0.1;
p[ind][0]=x+r*sin(-t);
p[ind][1]=y+r*cos(-t);
p[ind][2]=x+r*sin(2*t);
p[ind][3]=y+r*cos(2+t);
}
else if (Endpunkte==epKreis3)
{ // ähnlich wie Animation==0
t = t+0.1;
p[ind][0]=x+r*sin(t);
p[ind][1]=y+1.5*r*cos(t);
p[ind][2]=x+r*sin(2+t);
p[ind][3]=y+1.5*r*cos(2+t);
}
else if (Endpunkte==epZufall)
{ // Zufallsgeraden
t = t+0.1;
p[ind][0]=rand()%Image1->ClientWidth;
p[ind][1]=rand()%Image1->ClientHeight;
p[ind][2]=rand()%Image1->ClientWidth;
p[ind][3]=rand()%Image1->ClientHeight;
};
if (Figur==fGerade)
{ // Zeichne Gerade
Form1->Image1->Canvas->MoveTo(p[ind][0],p[ind][1]);
Form1->Image1->Canvas->LineTo(p[ind][2],p[ind][3]);
}
else if (Figur==fEllipse)
{ // zeichne Ellipse
Form1->Image1->Canvas->Brush->Style=bsClear;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
Form1->Image1->Canvas->Ellipse(p[ind][0],p[ind][1],
p[ind][2],p[ind][3]);
}
else if (Figur==fRechteck)
{ // zeichne Rechteck
Form1->Image1->Canvas->Brush->Style=bsClear;
Form1->Image1->Canvas->Rectangle(p[ind][0],p[ind][1],
p[ind][2],p[ind][3]);
}
// die Arraypositionen werden zyklisch durchlaufen:
if (ind<Max-1) ind++;
else ind=0;
}
void __fastcall TForm1::StartStopAnimClick(TObject *Sender)
{
Timer1->Enabled = !Timer1->Enabled;
}
//-------------------------------------------------------------------void __fastcall TForm1::RBKreis1Click(TObject *Sender)
{
Endpunkte=epKreis1;
}
//-------------------------------------------------------------------void __fastcall TForm1::RBKreis2Click(TObject *Sender)
{
Endpunkte=epKreis2;
}
//-------------------------------------------------------------------void __fastcall TForm1::RBKreis3Click(TObject *Sender)
{
Endpunkte=epKreis3;
}
//-------------------------------------------------------------------void __fastcall TForm1::RBZufallClick(TObject *Sender)
{
Endpunkte=epZufall;
}
void __fastcall TForm1::LinienClick(TObject *Sender)
{
Figur=fGerade;
Max=MaxF;
}
void __fastcall TForm1::KreiseClick(TObject *Sender)
{
Figur=fEllipse;
Max=1;
}
void __fastcall TForm1::RechteckeClick(TObject *Sender)
{
Figur=fRechteck;
Max=5;
}
//------ Aufgabe 5 --------------------------------------------------void __fastcall TForm1::RaeuberBeuteClick(TObject *Sender)
{
const double zb = 0.05, fb = 0.001,
sr = 0.05, fr = 2*fb;
Form1->Caption = "Räuber-Beute-Modell";
// Form1->Image1->Width = 600;
// Form1->Image1->Height = 400;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
185
186
12 Lösungen
Form1->Image1->Canvas->Pen->Color = clRed;
// Startwerte:
double r_alt = 100;
double b_alt = 50;
// zur Skalierung der y-Werte
// x0 = 0; x1 = 1000;
double y0 = 300;
double y1 = -10;
int pr, pb;
for (int px=0; px<=Form1->Image1->Width-1; px++)
{
double r = r_alt - sr*r_alt + fr*r_alt*b_alt;
double b = b_alt + zb*b_alt - fb*r_alt*b_alt;
Memo1->Lines->Add(Format("r=%.4g b=%.4g",OPENARRAY(TVarRec,(r,b))));
// transformiere y in Bild-Koordinaten
int pr_alt = pr; // undefinierte Werte beim
int pb_alt = pb; // ersten Durchlauf irrelevant
pr = (r-y0)*Form1->ClientHeight/(y1-y0);
pb = (b-y0)*Form1->ClientHeight/(y1-y0);
Form1->Image1->Canvas->MoveTo(px-1,pr_alt);
Form1->Image1->Canvas->Pen->Color = clRed;
if (px==0) Form1->Image1->Canvas->MoveTo(px,pr);
else Form1->Image1->Canvas->LineTo(px,pr);
Form1->Image1->Canvas->MoveTo(px-1,pb_alt);
Form1->Image1->Canvas->Pen->Color = clBlue;
if (px==0) Form1->Image1->Canvas->MoveTo(px,pb);
else Form1->Image1->Canvas->LineTo(px,pb);
Form1->Image1->Canvas->MoveTo(pr_alt,pb_alt);
Form1->Image1->Canvas->Pen->Color = clGreen;
if (px==0) Form1->Image1->Canvas->MoveTo(pr,pb);
else Form1->Image1->Canvas->LineTo(pr,pb);
r_alt = r;
b_alt = b;
};
}
12.3.14 Aufgabe 4.5.3
// Datei: d:\Loesungen\kap-4\4.5\InternetU.cpp
#include "InternetU.h"
#pragma link "SHDocVw_OCX"
//-------------------------------------------------------------------AnsiString dir="c:\\test";
// Verwende in FtpDownload dieses Verzeichnis:
AnsiString LocalFilename=dir+"\\ftp-1.html";
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
// Erzeuge das Verzeichnis, falls es nicht existiert
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
void __fastcall TForm1::LoadPageClick(TObject *Sender)
{
WideString url="http://www.yahoo.com";
// url="file:///C|\\CBuilder\\Examples\\DDraw\\directdw.htm";
// C:\CBuilder\Examples\DDraw\directdw.htm
CppWebBrowser1->Navigate(url,0,0,0,0);
}
void __fastcall TForm1::goBackClick(TObject *Sender)
{
CppWebBrowser1->GoBack();
}
void __fastcall TForm1::goForwardClick(TObject *Sender)
{
CppWebBrowser1->GoForward();
}
void __fastcall TForm1::SavePageClick(TObject *Sender)
{
// Bei dieser Lösungsvariante wird der Savedialog des IE aufgerufen:
TVariant p1=(dir+"\\HtmlPage1.htm").c_str();
CppWebBrowser1->ExecWB(Shdocvw_tlb::OLECMDID_SAVEAS,
Shdocvw_tlb::OLECMDEXECOPT_DONTPROMPTUSER,
&p1); // TVariant *pvaIn=TNoParam()
// Bei dieser Lösungsvariante wird ohne Savedialog gespeichert:
if ( (CppWebBrowser1) && (CppWebBrowser1->Document) )
{
IPersistFile *IpersFile;
HRESULT hr=CppWebBrowser1->Document->QueryInterface(IID_IPersistFile,
(void**)(&IpersFile));
if (SUCCEEDED(hr)) {
IpersFile->Save(StringToOleStr(dir+"\\HtmlPage2.htm"),false);
IpersFile->Release();
}
}
}
//-------------------------------------------------------------------void __fastcall TForm1::PrintPageClick(TObject *Sender)
{
CppWebBrowser1->ExecWB( Shdocvw_tlb::OLECMDID_PRINT,
Shdocvw_tlb::OLECMDEXECOPT_DONTPROMPTUSER );
}
void __fastcall TForm1::FtpDownloadClick(TObject *Sender)
{
NMFTP1->Host="ftp.uni-stuttgart.de";
NMFTP1->Port=21; // siehe "services" im Windows-Verzeichnis
NMFTP1->UserID="ftp";
NMFTP1->Password="anonymous";
NMFTP1->Connect();
NMFTP1->Download("index.html",LocalFilename);
NMFTP1->Disconnect();
}
void __fastcall TForm1::LoadLocalPageClick(TObject *Sender)
{
AnsiString fn= LocalFilename;
int pos=fn.Pos(":");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
187
188
12 Lösungen
WideString url="file:///"+AnsiString(fn[1])+"|"+fn.SubString(pos+1,fn.Length());
AnsiString s=url; // Nur für Debugger: WideStrings werden nicht angezeigt
CppWebBrowser1->Navigate(url,0,0,0,0);
}
12.3.15 Aufgaben 4.5.6
// Datei: d:\Loesungen\kap-4\4.5\TimeU.cpp
#include "TimeU.h"
//------- Aufgabe 4.5.6.1 -------------------------------------------#include "\Loesungen\CppUtils\TimeUtils.cpp" // Nur ein "\" bei Pfadangaben!
//------- Aufgabe 4.5.6.2 -------------------------------------------int TickCount=0;
double StartTime=HRTimeInSec();
void __fastcall TForm1::TimerTestClick(TObject *Sender)
{
if (!Timer1->Enabled)
{
Timer1->Enabled=true;
TickCount=0;
StartTime=HRTimeInSec();
}
else Timer1->Enabled=false;
}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
double now=HRTimeInSec();
TickCount++;
Memo1->Lines->Add(IntToStr(TickCount)+" t1="+FloatToStr(now-StartTime));
}
//------- Aufgabe 4.5.6.3 -------------------------------------------enum TimeMode { tmUserTime, tmKernelTime};
double ProcessTime_NT(TimeMode tm)
{
// Enables using the process handle:
HANDLE h=OpenProcess(PROCESS_QUERY_INFORMATION, //
// access flag
true, // handle inheritance flag
GetCurrentProcessId() ); // process identifier
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
BOOL b=GetProcessTimes(h,
&CreationTime,
&ExitTime,
&KernelTime,
&UserTime);
//
//
//
//
//
//
//
//
//
DWORD dwDesiredAccess,
when the process was created
when the process exited
time the process has spent in kernel mode
time the process has spent in user mode
specifies the process of interest
when the process was created
when the process exited
time the process has spent in kernel mode
time the process has spent in user mode
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
FILETIME ft;
if (tm==tmUserTime) ft=UserTime;
else if (tm==tmKernelTime) ft=KernelTime;
else ShowMessage("Invalid TimeMode");
LARGE_INTEGER L;
L.u.LowPart=ft.dwLowDateTime;
L.u.HighPart=ft.dwHighDateTime;
if (b) return L.QuadPart;
else return -1;
}
void MesseNTZeiten()
{ // funktioniert nur unter Windows-NT
const n=10000000L;
float f=0;
double Start1=HRTimeInSec();
double Start2=ProcessTime_NT(tmUserTime);
double Start3=ProcessTime_NT(tmKernelTime);
for (int i=1; i<=n; i++)
f+=0.1;
double End1=HRTimeInSec();
double End2=ProcessTime_NT(tmUserTime);
double End3=ProcessTime_NT(tmKernelTime);
Form1->Memo1->Lines->Add("t float="+FloatToStr(End1-Start1)+
" sum="+FloatToStr(f));
Form1->Memo1->Lines->Add("tU float="+FloatToStr(End2-Start2)+
" sum="+FloatToStr(f));
Form1->Memo1->Lines->Add("tK float="+FloatToStr(End3-Start3)+
" sum="+FloatToStr(f));
}
12.3.16 Aufgabe 4.5.7
// Datei: d:\Loesungen\kap-4\4.5\SetU.cpp
#include "SetU.h"
void __fastcall TForm1::fettClick(TObject *Sender)
{
if (fett->Checked)
Label1->Font->Style=Label1->Font->Style<<fsBold;
else
Label1->Font->Style=Label1->Font->Style>>fsBold;
}
//-------------------------------------------------------------------void __fastcall TForm1::kursivClick(TObject *Sender)
{
if (kursiv->Checked)
Label1->Font->Style=Label1->Font->Style<<fsItalic;
else
Label1->Font->Style=Label1->Font->Style>>fsItalic;
}
//-------------------------------------------------------------------void __fastcall TForm1::unterstrichenClick(TObject *Sender)
{
if (unterstrichen->Checked)
Label1->Font->Style=Label1->Font->Style<<fsUnderline;
else
Label1->Font->Style=Label1->Font->Style>>fsUnderline;
}
//-------------------------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
189
190
void __fastcall TForm1::durchgestrichenClick(TObject *Sender)
{
if (durchgestrichen->Checked)
Label1->Font->Style=Label1->Font->Style<<fsStrikeOut;
else
Label1->Font->Style=Label1->Font->Style>>fsStrikeOut;
}
//-------------------------------------------------------------------void __fastcall TForm1::Edit1KeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
AnsiString s;
if (Shift.Contains(ssShift)) s=s+"Shift ";
if (Shift.Contains(ssAlt))
s=s+"Alt ";
if (Shift.Contains(ssCtrl)) s=s+"Ctrl ";
// Diese Ereignisse treten bei KeyDown nicht ein:
if (Shift.Contains(ssLeft)) s=s+"Left ";
if (Shift.Contains(ssRight)) s=s+"Right ";
if (Shift.Contains(ssMiddle))s=s+"Middle ";
if (Shift.Contains(ssDouble))s=s+"Double ";
Memo1->Lines->Add(s);
/*
ssShift Die Taste Umschalt wird gedrückt.
ssAlt
Die Taste Alt wird gedrückt.
ssCtrl
Die Taste Strg wird gedrückt.
ssLeft
Die linke Maustaste wird gedrückt.
ssRight Die rechte Maustaste wird gedrückt.
ssMiddle Die mittlere Maustaste wird gedrückt.
ssDouble Es wurde mit der Maus doppelgeklickt.
*/
}
//--------------------------------------------------------------------
12.3.17 Aufgaben 4.6.3
// Datei: d:\Loesungen\kap-4\4.6\DatUebU.cpp
#include "DatUebU.h"
//----- Aufgabe 4.6.3 -----------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
// Damit die Dateien für die folgenden Aufgaben vorhanden sind:
if (!FileExists((dir+"\\config.sys").c_str()))
{
BOOL b=CopyFile("c:\\config.sys",
(dir+"\\config.sys").c_str(), true);
if (b) ShowMessage("config.sys copied to "+dir);
}
if (!FileExists((dir+"\\command.com").c_str()))
{
BOOL b=CopyFile("c:\\command.com",
(AnsiString(dir)+"\\command.com").c_str(), true);
if (b) ShowMessage("command.com copied to "+dir);
}
// Diese Windows-API-Funktionen sind in der Online-Hilfe zum Win32-SDK
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
// beschrieben.
}
//----- Aufgabe 1 a) -----------------------------------------------#include <fstream>
using namespace std;
int CountBytesOfFile(char* fn)
{
char c;
int n=0;
ifstream f(fn,ios::binary);
// ifstream f(fn); // ohne binary ist das Ergebnis falsch
if (!f) ShowMessage("Fehler bei open");
while (f.read(&c,sizeof(c)))
n++;
if (!f.eof()) ShowMessage("Fehler bei read");
f.close();
return n;
}
void __fastcall TForm1::CountBytesClick(TObject *Sender)
{
AnsiString fn1="c:\\test\\config.sys";
int n=CountBytesOfFile(fn1.c_str());
Memo1->Lines->Add(fn1+": "+IntToStr(n));
AnsiString fn2="c:\\test\\command.com";
n=CountBytesOfFile(fn2.c_str());
Memo1->Lines->Add(fn2+": "+IntToStr(n));
}
//----- Aufgabe 1 b) -----------------------------------------------void CopyFile(char* src, char* dst)
{
char c;
ifstream in(src,ios::binary);
AnsiString msg="Fehler bei open ";
if (!in) ShowMessage(msg+src);
ofstream out(dst,ios::binary);
if (!out) ShowMessage(msg+dst);
while (in.read(&c,sizeof(c)))
{
out.write(&c,sizeof(c));
if (!out) ShowMessage("Fehler bei write");
}
if (!in.eof()) ShowMessage("Fehler bei read");
in.close();
out.close();
}
void __fastcall TForm1::CopyFilesClick(TObject *Sender)
{
CopyFile("c:\\test\\config.sys", "c:\\test\\configc.sys");
CopyFile("c:\\test\\command.com", "c:\\test\\commandc.com");
}
//----- Aufgabe 1 c) -----------------------------------------------bool CompareFiles(char* fn1, char* fn2)
{
AnsiString msg="Fehler bei open ";
ifstream f1(fn1,ios::binary);
if (!f1) ShowMessage(msg+fn1);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
191
192
12 Lösungen
ifstream f2(fn2,ios::binary);
if (!f2) ShowMessage(msg+fn2);
bool gleich=true; // zwei leere Dateien sind gleich
while (f1 && f2 && gleich)
{
char c1,c2;
f1.read(&c1,sizeof(c1));
f2.read(&c2,sizeof(c2));
gleich=(c1==c2);
}
if (!f1 && !f1.eof()) ShowMessage("Fehler bei read f1");
if (!f2 && !f2.eof()) ShowMessage("Fehler bei read f2");
if (gleich)
gleich=(f1.eof()==f2.eof()); // Falls die Dateien verschieden lang sind
f1.close();
f2.close();
return gleich;
/*
Die folgende Schleife funktioniert nicht, da wegen der short circuit
evaluation von booleschen Ausdrücken beim Ende von f1 in f2 nicht mehr
gelesen wird:
while (f1.read(&c1,sizeof(c1)) && f2.read(&c2,sizeof(c2)) && gleich)
gleich=(c1==c2);
*/
}
void __fastcall TForm1::ComparefilesClick(TObject *Sender)
{
bool b1=CompareFiles("c:\\test\\config.sys", "c:\\test\\configc.sys");
bool b2=CompareFiles("c:\\test\\command.com", "c:\\test\\commandc.com");
bool b3=CompareFiles("c:\\test\\command.com", "c:\\test\\configc.sys");
bool b4=CompareFiles("c:\\test\\command.com", "c:\\test\\configc.sys");
Memo1->Lines->Add(IntToStr(b1));
Memo1->Lines->Add(IntToStr(b2));
Memo1->Lines->Add(IntToStr(b3));
Memo1->Lines->Add(IntToStr(b4));
}
//----- Aufgabe 1 d) -----------------------------------------------void WriteNTString(ofstream& f,char* s)
{ // schreibt alle Zeichen des nullterminierten Strings s
// einschließlich des Nullterminators in den stream f
f.write(s,strlen(s)+1);
}
AnsiString ReadNTString(ifstream& f)
{
// Die globale Funktion getline aus der Standardbibliothek
// von C++ ist ähnlich aufgebaut und sollte dieser Funktion
// vorgezogen werden.
AnsiString s;
char c;
while (f.read(&c,1) && c!=0)
s=s+c;
return s;
}
void __fastcall TForm1::WritNTSClick(TObject *Sender)
{
char* fn="\\test\\nts.txt";
ofstream out(fn,ios::binary);
if (out)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
193
{
WriteNTString(out,"123");
WriteNTString(out,"der nächste String ist leer");
WriteNTString(out,"");
WriteNTString(out,"A");
}
out.close();
ifstream in(fn,ios::binary);
if (in)
{
AnsiString s=ReadNTString(in);
while (in)
{
Memo1->Lines->Add(s);
s=ReadNTString(in);
}
}
in.close();
}
//----- Aufgabe 1 e) -----------------------------------------------void TextFilter(char* src, char* dst)
{
AnsiString msg="Fehler bei open ";
ifstream in(src,ios::binary);
if (!in) ShowMessage(msg+src);
ofstream out(dst,ios::binary);
if (!out) ShowMessage(msg+dst);
char c;
while (in.read(&c,sizeof(c)))
if ((' '<=c && c<='z') || c=='\n') // bis auf diese Anweisung identisch
// mit CopyFile
if (!out.write(&c,sizeof(c)))
ShowMessage("Fehler bei write");
if (!in.eof()) ShowMessage("Fehler bei read");
in.close();
out.close();
}
void __fastcall TForm1::FileFilterClick(TObject *Sender)
{
TextFilter("c:\\test\\config.sys", "c:\\test\\configf.sys");
TextFilter("c:\\test\\command.com", "c:\\test\\commandf.com");
TextFilter("c:\\test\\AlleMeineEntchen.doc", "c:\\test\\AlleMeineEntchen.txt");
}
12.3.18 Aufgaben 4.6.3, 4.6.4.2, 4.6.8 und 4.6.9
// Datei: d:\Loesungen\kap-4\4.6\DatVerwU.cpp
#include "DatVerwU.h"
TKBForm1 *KBForm1;
__fastcall TKBForm1::TKBForm1(TComponent* Owner)
: TForm(Owner)
{
}
//----- Aufgabe 2 a) ------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
194
12 Lösungen
#include "\Loesungen\CppUtils\KBDecl.cpp"
#include "\Loesungen\CppUtils\KBForms.cpp"
#include <fstream>
using namespace std;
fstream f;
void __fastcall TKBForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
ClearKBForm(KBForm1);
// Menüoptionen:
Neu1->Enabled=true;
ffnen1->Enabled=true;
Schliessen1->Enabled=false;
// Buttons Aufgabe 4.6.3.2:
Speichern->Enabled=false;
next->Enabled=false;
LinSuchen->Enabled=false;
// Buttons Aufgabe 4.6.8:
KBForm1->first->Enabled=false;
KBForm1->last->Enabled =false;
KBForm1->next->Enabled =false;
KBForm1->prev->Enabled =false;
}
void __fastcall TKBForm1::Neu1Click(TObject *Sender)
{
OpenDialog1->Filter = "KB-Dateien (*.dat)|*.dat";
OpenDialog1->InitialDir="c:\\test"; // "c:\" ist Escape-Sequenz
OpenDialog1->FileName="kb.dat";
if (OpenDialog1->Execute())
{
// Für Aufgabe 4.6.3.2 reicht
// f.open(OpenDialog1->FileName.c_str(),ios::binary|ios::out);
// ----- Aufgabe 4.6.5.1 a) --------------------------// Für Aufgabe 4.6.5.1 ist aber dieser Modus notwendig:
f.open(OpenDialog1->FileName.c_str(),ios::binary|ios::out|ios::in);
if (!f)
{
ShowMessage("Fehler bei open "+OpenDialog1->FileName);
f.clear();
f.close();
}
else
{
// Menüoptionen:
Neu1->Enabled=false;
ffnen1->Enabled=false;
Schliessen1->Enabled=true;
// Buttons Aufgabe 4.6.3.2:
Speichern->Enabled=true;
next->Enabled=false;
LinSuchen->Enabled=false;
// Buttons Aufgabe 4.6.8:
KBForm1->first->Enabled=true;
KBForm1->last->Enabled =true;
KBForm1->next->Enabled =true;
KBForm1->prev->Enabled =true;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
}
}
void EnableButtons();
void __fastcall TKBForm1::SpeichernClick(TObject *Sender)
{
if (f)
{
// Für die Aufgabe 4.6.8 muss der Dateizeiger auf die
// Position am Dateiende gesetzt werden.
CKontobewegung K=FormToKB(KBForm1);
f.write((char*)&K,sizeof(K));
// Die folgenden 3 Anweisungen sind nicht notwendig.
// Sie sind aber nützlich, falls etwas schiefgeht.
if (!f) ShowMessage("Fehler bei write ");
f.flush();
if (!f) ShowMessage("Fehler bei flush ");
EnableButtons();
}
else ShowMessage("Dateifehler");
}
void __fastcall TKBForm1::Schliessen1Click(TObject *Sender)
{
f.close();
// Menüoptionen:
ffnen1->Enabled=true;
Schliessen1->Enabled=false;
Neu1->Enabled=true;
// Buttons Aufgabe 4.6.3.2:
Speichern->Enabled=false;
next->Enabled=false;
LinSuchen->Enabled=false;
// Buttons Aufgabe 4.6.8:
KBForm1->first->Enabled=false;
KBForm1->last->Enabled =false;
KBForm1->next->Enabled =false;
KBForm1->prev->Enabled =false;
}
//----- Aufgabe 2 b) ------------------------------------------------void __fastcall TKBForm1::ffnen1Click(TObject *Sender)
{
OpenDialog1->Filter = "KB-Dateien (*.dat)|*.dat";
OpenDialog1->InitialDir="c:\\test"; // "c:\" ist Escape-Sequenz
OpenDialog1->FileName="kb.dat";
if (OpenDialog1->Execute())
{
// Für Aufgabe 4.6.3.2 reicht
// f.open(OpenDialog1->FileName.c_str(),ios::binary|ios::in);
//----- Aufgabe 4.6.8.1 a) --------------------------// Für Aufgabe 4.6.8.1 ist aber dieser Modus notwendig:
f.open(OpenDialog1->FileName.c_str(),ios::binary|ios::out|ios::in);
if (!f)
{
ShowMessage("Fehler bei open "+OpenDialog1->FileName);
f.clear();
f.close();
}
else
{
// Menüoptionen:
Neu1->Enabled=false;
ffnen1->Enabled=false;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
195
196
12 Lösungen
Schliessen1->Enabled=true;
// Buttons Aufgabe 4.6.3.2:
Speichern->Enabled=true;
next->Enabled=true;
LinSuchen->Enabled=true;
// Buttons Aufgabe 4.6.8:
KBForm1->first->Enabled=true;
KBForm1->last->Enabled =true;
KBForm1->next->Enabled =true;
KBForm1->prev->Enabled =true;
nextClick(Sender); // zeigt den ersten Datensatz an
}
}
}
void ReadAndDisplayData()
{
CKontobewegung K;
f.read((char*)&K,sizeof(K));
if (f)
KBToForm(K,KBForm1);
else
{
if (f.eof())
{
ShowMessage("Keine weiteren Daten vorhanden");
f.clear(); // eof ist kein Fehler
// Dieses clear ist notwendig, damit 'seekg', 'tellg' usw.
// anschließend funktionieren. Diese geben sonst -1 zurück.
}
else
ShowMessage("Fehler bei read ");
KBForm1->next->Enabled=false;
// ClearKBForm(KBForm1);
}
}
/* **************************************************************
Die etwas mühsamen Abfragen des Dateizustands in jeder der
Funktionen ReadAndDisplayData, LinSuchen usw. kann man sich
mit Exception-Handling sparen.
************************************************************** */
void __fastcall TKBForm1::nextClick(TObject *Sender)
{
ReadAndDisplayData();
EnableButtons();
}
//----- Aufgabe 2 c) ------------------------------------------------// Zu dieser Aufgabe gehört auch die Funktion Zufalls_KB in
// "\Loesungen\CppUtils\KBDecl.cpp"
void __fastcall TKBForm1::Zufallsdatei1Click(TObject *Sender)
{
AnsiString s;
srand(1); // jedesmal dieselben Daten erzeugen
if (InputQuery("Testdaten erzeugen","Anzahl der zu erzeugenden Datensätze", s))
{
int n = StrToInt(s);
OpenDialog1->InitialDir="c:\\test";
OpenDialog1->FileName="kb.dat";
OpenDialog1->Filter = "KB-Dateien (*.dat)|*.dat";
if (OpenDialog1->Execute())
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
{
ofstream f(OpenDialog1->FileName.c_str(),ios::binary);
for (int i=0; i<n; i++)
{
CKontobewegung K=Zufalls_KB();
f.write((char*)&K,sizeof(K));
}
f.close();
}
}
}
//----- Aufgabe 2 d) ------------------------------------------------void __fastcall TKBForm1::LinSuchenClick(TObject *Sender)
{
if (f)
{
CKontobewegung K;
bool gefunden=false;
while ((f.read((char*)&K,sizeof(K))) && (!gefunden))
{
AnsiString s=K.NameInhaber;
if (s.Pos(Edit1->Text)>0)
{
KBToForm(K,KBForm1);
gefunden=true;
}
}
if (!gefunden)
ShowMessage("Kein Datensatz gefunden");
if (f.eof())
{
f.clear(); // Damit nach einem Zurücksetzen des Dateizeigers
// wie in 'firstClick' weitergesucht werden kann.
LinSuchen->Enabled=false;
}
}
else ShowMessage("Dateifehler");
}
//---- Aufgabe 4.6.4.2 - Listendruck --------------------------------//
//
//
//
Die Namensgebung und Aufteilung in die Funktionen wurde so gewählt,
dass die Funktionen in späteren Aufgaben wiederverwendet werden können.
Für diese Aufgaben sind auch die mit /***/ gekennzeichneten Zeilen
notwendig. Für die aktuelle Aufgabe sind sie nicht notwendig.
int Zeilenzahl;
/***/ CKontobewegung Drucksatz, Altsatz;
const int Zeilen_pro_Seite= 72;
const int Druckzeilen_pro_Seite= 60;
Currency Summe;
// wird vor jedem Start initialisiert (in VorlGS0)
int Seitenzahl;
// "
int AnzahlFehler; // "
ifstream in;
// Datei, die gelesen wird
ofstream out;
// Ausgabedatei
#include <iomanip>
void Blattkopf(char* fn)
{ // druckt Überschriftszeilen am Anfang einer Seite
Seitenzahl++;
out << "Datei: "<<fn<<"
Ein-/Auszahlungen "
<< "Seite "<<Seitenzahl<<endl;
out<<setw(4)<<"Kto."<<" "
<<left<<setw(20)<<"Kontoinhaber"
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
197
198
12 Lösungen
<<right<<setw(10)<<"Datum"
<<setw(10)<<"Betrag"<<endl<<endl;
Zeilenzahl = 3;
//***/ Drucksatz = K; // damit am Anfang einer neuen Seite der erste
// Datensatz vollstaendig ausgedruckt wird
}
void Blattvorschub()
{ // druckt Schlußzeile und formfeed
out << "Anzahl der fehlerhaften Datensätze: "<<AnzahlFehler
<< '\f'; // formfeed - Blattvorschub
}
AnsiString MyCurrToStr(Currency c)
{ // CurrToStr druckt c=1 als "1" bzw. c=0,1 als "0,1" und nicht als "0,10"
AnsiString s=CurrToStr(c);
if (s.Pos(",")==0) s=s+",0";
if (s.Pos(",")==s.Length()-1) s=s+"0";
return(s);
}
void Drucke_Zeile_0(CKontobewegung K)
{
out<<setw(4)<<K.KontoNr<<" "
<<left<<setw(20)<<K.NameInhaber
<<right<<setw(2)<<K.Datum.Tag<<"."
<<setw(2)<<K.Datum.Monat<<"."<<setw(2)<<K.Datum.Jahr
<<" "<<K.BewArt<<setw(8)<<setprecision(8)
<<MyCurrToStr(K.Betrag).c_str()<<endl;
Zeilenzahl++;
}
void VorlGS0(char* infn, char* outfn)
{ // Anweisungen, die vor der Verarbeitung der Datensätze erfolgen
in.open(infn,ios::binary);
out.open(outfn);
Summe = 0;
Seitenzahl = 0;
AnzahlFehler = 0;
Blattkopf(infn);
}
void Bearbeite_DS_Stufe_0(char* infn, CKontobewegung K)
{ // bearbeitet (hier: druckt) einen Datensatz
if (Zeilenzahl >= Druckzeilen_pro_Seite)
{
Blattvorschub();
Blattkopf(infn);
/***/
Drucksatz.KontoNr = K.KontoNr;
// der erste DS auf einer neuen Seite soll die Kontonr enthalten
};
if (K.BewArt == '+')
Summe = Summe + K.Betrag;
else if (K.BewArt == '-') Summe = Summe - K.Betrag;
else AnzahlFehler++;
Drucke_Zeile_0(K);
/***/ Altsatz = K; // Altsatz ist der zuletzt verarbeitete Satz der Gruppe
};
CKontobewegung Lese_naechsten_Satz()
{
CKontobewegung K;
in.read((char*)&K,sizeof(K));
/***/ Drucksatz = K;
/***/ Drucksatz.KontoNr = 0; // innerhalb einer Gruppe soll die Kontonr.
// nicht ausgedruckt werden
return K;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
void NachlGS0()
{ // Anweisungen, die am Ende ausgeführt werden
out<<setw(38)<<"Gesamtsumme: "<<MyCurrToStr(Summe).c_str()<<endl;
Zeilenzahl = Zeilenzahl+2;
Blattvorschub();
in.close();
out.close();
}
void Listendruck(char* infn, char* outfn)
{
CKontobewegung K;
VorlGS0(infn,outfn);
K=Lese_naechsten_Satz();
while (in)
{
Bearbeite_DS_Stufe_0(infn,K);
K=Lese_naechsten_Satz();
}
NachlGS0();
};
void __fastcall TKBForm1::Listendruck1Click(TObject *Sender)
{
OpenDialog1->InitialDir="c:\\test";
OpenDialog1->FileName="kb.sor";
OpenDialog1->Filter = "sortierte KB-Dateien (*.sor)|*.SOR";
if (OpenDialog1->Execute())
// Listendruck("c:\\test\\kb.dat","c:\\test\\kb.out");
Listendruck(OpenDialog1->FileName.c_str(),"c:\\test\\kb.out");
}
//----- Aufgabe 4.6.8 b) --------------------------------------------//
//
//
//
Wie bei den Lösungen der Aufgabe 4.6.3.2 wäre es auch bei den folgenden
Lösungen sinnvoll, nach jeder Dateioperation zu prüfen, ob diese
erfolgreich war: "if (!f) ...". Damit die Logik aber nicht durch
zusätzliche Anweisungen verdeckt wird, wurde darauf verzichtet.
// Die folgenden Funktionen beschreiben alle relevanten
// Bedingungen und Operation. Mit ihnen kann die Lösung
// leicht auf andere Container und Dateien übertragen werden.
void EnableButtons()
{
if (f)
{
int act_pos=f.tellg();
f.seekg(0,ios::end);
// f.clear(); überflüssig: tellg und seekg(0,...) erzeugen nie einen
//
Fehler
int end_pos=f.tellg();
f.seekg(act_pos);
KBForm1->first->Enabled =(end_pos>0);// Enabled, falls Datei nicht leer
KBForm1->last->Enabled =(end_pos>0);// Enabled, falls Datei nicht leer
KBForm1->next->Enabled =(act_pos<end_pos);
KBForm1->prev->Enabled =(act_pos>=2*sizeof(CKontobewegung));
}
else if (f.eof()) ShowMessage("f.eof() **** "); // dieser Fall dürfte nie
// eintreten, da nach dem Lesen in ReadAndDisplayData() bei eof clear
// aufgerufen wird.
else ShowMessage("Dateifehler: !f"); // Was ging schief?
}
void __fastcall TKBForm1::prevClick(TObject *Sender)
{
if (f)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
199
200
12 Lösungen
{
int pos=f.tellg();
if (pos >= 2*sizeof(CKontobewegung))
{
f.seekg(pos-2*sizeof(CKontobewegung));
ReadAndDisplayData();
}
else ClearKBForm(KBForm1);
EnableButtons();
}
else ShowMessage("Dateifehler: !f"); // Was ging schief?
}
//----- Aufgabe 4.6.8 c) --------------------------------------------void __fastcall TKBForm1::firstClick(TObject *Sender)
{
if (f)
{
f.seekg(0);
ReadAndDisplayData();
EnableButtons();
}
else ShowMessage("Dateifehler: !f"); // Was ging schief?
}
//----- Aufgabe 4.6.8 d) --------------------------------------------void __fastcall TKBForm1::lastClick(TObject *Sender)
{
if (f)
{
f.seekg(-1*sizeof(CKontobewegung),ios::end);
if (f.tellg() >= 0)
ReadAndDisplayData();
EnableButtons();
}
else ShowMessage("Dateifehler: !f"); // Was ging schief?
}
//----- Aufgabe 4.6.8 e) --------------------------------------------void __fastcall TKBForm1::KorrekturClick(TObject *Sender)
{
if (f)
{
CKontobewegung K;
int pos=f.tellg();
if (pos >= sizeof(K))
{
f.seekg(pos-sizeof(K));
K=FormToKB(KBForm1);
f.write((char*)&K,sizeof(K));
}
else ShowMessage("Keine Korrektur möglich: Datei leer");
}
else ShowMessage("Dateifehler: !f"); // Was ging schief?
}
//----- Aufgabe 4.6.9.1 ---------------------------------------------#include <vector>
using namespace std;
enum TSortierbegriff {sbKontoNr, sbName,sbDatum,sbKontonrUndDatum}
Sortierbegriff=sbKontoNr;
//
Sortierbegriff=sbKontonrUndDatum;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
bool operator<(const CKontobewegung& k1, const CKontobewegung& k2)
{
if (Sortierbegriff==sbKontoNr)
return k1.KontoNr < k2.KontoNr;
else if (Sortierbegriff==sbName)
return k1.NameInhaber < k2.NameInhaber;
else if (Sortierbegriff==sbDatum)
{
if (k1.Datum.Jahr!=k2.Datum.Jahr)
return k1.Datum.Jahr<k2.Datum.Jahr;
else if (k1.Datum.Monat!=k2.Datum.Monat)
return k1.Datum.Monat<k2.Datum.Monat;
else return k1.Datum.Tag<k2.Datum.Tag;
}
else if (Sortierbegriff==sbKontonrUndDatum)
{
if (k1.KontoNr!=k2.KontoNr)
return k1.KontoNr<k2.KontoNr;
if (k1.Datum.Jahr!=k2.Datum.Jahr)
return k1.Datum.Jahr<k2.Datum.Jahr;
else if (k1.Datum.Monat!=k2.Datum.Monat)
return k1.Datum.Monat<k2.Datum.Monat;
else return k1.Datum.Tag<k2.Datum.Tag;
}
else
{
ShowMessage("Unzulässiger Sortierbegriff");
return false;
}
}
void SortiereKBDatei(char* infn, char* outfn)
{
vector<CKontobewegung> v;
CKontobewegung K;
ifstream in(infn,ios::binary);
while (in.read((char*)&K,sizeof(K)))
v.push_back(K);
in.close();
sort(v.begin(),v.end());
ofstream out(outfn,ios::binary);
for (size_t i=0; i<v.size(); i++)
out.write((char*)&v[i],sizeof(v[i]));
out.close();
}
void __fastcall TKBForm1::SortierenClick(TObject *Sender)
{
OpenDialog1->Filter = "KB-Dateien (*.dat)|*.dat";
OpenDialog1->InitialDir="c:\\test"; // "c:\" ist Escape-Sequenz
OpenDialog1->FileName="kb.dat";
if (OpenDialog1->Execute())
{
AnsiString outfn=OpenDialog1->FileName+".sor";
SortiereKBDatei(OpenDialog1->FileName.c_str(), outfn.c_str());
}
}
const int n=2; // Anzahl der zu mischenden Dateien
ifstream D[n];
ofstream M;
CKontobewegung K[n];
int MinIndex(int n)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
201
202
12 Lösungen
{
int m=0;
for (int i=1; i<n; i++)
if (!D[m]) m=i; // ***
else if (D[i] || D[m])
if (K[i]<K[m]) m=i;
return m;
/* zu ***:
if D[0].eof() m=1
if D[1].eof() m=2
...
*/
}
//----- Aufgabe 4.6.9.2 ---------------------------------------------// zusätzliche Deklarationen für Folgeprüfung:
CKontobewegung LetzterSatz;
int AnzahlFolgefehler;
void Folgepruefung(int i)
{
if (K[i]<LetzterSatz)
AnzahlFolgefehler++;
};
// Ende zusätzliche Deklarationen für Folgeprüfung
void Mischen2mitFP(char* fn1, char* fn2, char* mfn)
{
// Änderung für Folgeprüfung:
bool ErsterSatz=true;
AnzahlFolgefehler=0;
// Ende Änderung für Folgeprüfung
D[0].open(fn1,ios::binary);
D[1].open(fn2,ios::binary);
M.open(mfn,ios::binary);
D[0].read((char*)&K[0],sizeof(K[0]));
D[1].read((char*)&K[1],sizeof(K[1]));
while (D[0] || D[1])
{
int i=MinIndex(2);
// Änderung für Folgeprüfung:
if (ErsterSatz) ErsterSatz=false;
else Folgepruefung(i);
LetzterSatz=K[i];
// Ende Änderung für Folgeprüfung
M.write((char*)&K[i],sizeof(K[i]));
D[i].read((char*)&K[i],sizeof(K[i]));
//
if (!D[i]) ShowMessage(IntToStr(i));
}
D[0].close();
D[1].close();
M.close();
if (AnzahlFolgefehler>0)
ShowMessage(IntToStr(AnzahlFolgefehler)+" Folgefehler");
}
void __fastcall TKBForm1::MischFPClick(TObject *Sender)
{
Mischen2mitFP("c:\\test\\KB0.sor","c:\\test\\KB1.sor","c:\\test\\M.sor");
}
//----- Aufgabe 4.6.9.3 ---------------------------------------------Currency GS1Summe;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
void VorlGS1(CKontobewegung K) // Anweisungen, die zu Beginn einer Gruppe
{
// der Stufe 1 ausgeführt werden
GS1Summe = 0;
Drucksatz.KontoNr = 0; // damit am Anfang einer Gruppe
// die Kontonummer nicht ausgedruckt wird
out <<setw(4)<<K.KontoNr<<" "<<K.NameInhaber<<endl;
Zeilenzahl++;
}
bool GW_Stufe_1(CKontobewegung K)
{ // Bedingung fnr eine Gruppenwechsel der Stufe 1
return Altsatz.KontoNr != K.KontoNr;
}
void Setze_Gruppenkennzeichen_fuer_Stufe_1(CKontobewegung K)
{ // damit die Schleife mindestens einmal ausgeführt wird
Altsatz.KontoNr = K.KontoNr;
};
void Drucke_Zeile_1()
{
if (Drucksatz.KontoNr == 0) out<<"
";
else out<<setw(4)<<Drucksatz.KontoNr<<" ";
if (Drucksatz.Datum.Jahr == 0) out<<"
"; // 6 Leerzeichen
else out<<setw(4)<<Drucksatz.Datum.Jahr<<" ";
out<<"
"<<setw(2)<<Drucksatz.Datum.Tag<<"."<<setw(2)<<Drucksatz.Datum.Monat
<<"
"<<Drucksatz.BewArt<<" "<<setw(8)
<<MyCurrToStr(Drucksatz.Betrag).c_str()<<endl;
Zeilenzahl++;
};
void Bearbeite_DS_Stufe_1(char* infn, CKontobewegung K)
{
if (Zeilenzahl >= Druckzeilen_pro_Seite)
{
Blattvorschub();
Blattkopf(infn);
Drucksatz.KontoNr = K.KontoNr;
// der erste DS auf einer neuen Seite soll die Kontonr enthalten
};
if (K.BewArt == '+')
GS1Summe = GS1Summe + K.Betrag;
else if (K.BewArt == '-')
GS1Summe = GS1Summe - K.Betrag;
else AnzahlFehler++;
Drucke_Zeile_1();
Altsatz = K; // Altsatz ist der zuletzt verarbeitete Satz der Gruppe
}
void NachlGS1() // Anweisungen, die am Ende einer Gruppe der
{
// Stufe 1 ausgeführt werden
Summe = Summe + GS1Summe;
out<<"
"<<"Summe Kontonummer "<<setw(4)<<Altsatz.KontoNr<<":"
<<"..........."<<setw(8)<<MyCurrToStr(GS1Summe).c_str()<<endl;
out<<endl;
Zeilenzahl = Zeilenzahl+2;
};
void GW1(char* infn, char* outfn)
{
CKontobewegung K;
VorlGS0(infn,outfn);
K=Lese_naechsten_Satz();
while (in)
{
VorlGS1(K);
Setze_Gruppenkennzeichen_fuer_Stufe_1(K);
// damit die Schleife mindestens einmal ausgeführt wird
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
203
204
12 Lösungen
while (!GW_Stufe_1(K)&& (in))
{
Bearbeite_DS_Stufe_1(infn,K);
K=Lese_naechsten_Satz();
}
NachlGS1();
}
NachlGS0();
}
void __fastcall TKBForm1::GrW1Click(TObject *Sender)
{
OpenDialog1->InitialDir="c:\\test";
OpenDialog1->FileName="kb.sor";
OpenDialog1->Filter = "sortierte KB-Dateien (*.sor)|*.SOR";
if (OpenDialog1->Execute())
GW1(OpenDialog1->FileName.c_str(),"c:\\test\\gw1.txt");
}
12.3.19 Aufgabe 4.6.4.1
// Datei: d:\Loesungen\kap-4\4.6\HTMLU.cpp
#include "HTMLU.h"
#pragma link "SHDocVw_OCX"
//-------------------------------------------------------------------#include <fstream>
#include <string>
using namespace std;
void PrintTextlines(ostream& o, char* infn)
{
ifstream in(infn);
string s;
while (getline(in,s))
{
for (string::size_type i=0; i<s.length(); i++)
if
(s[i]=='ä') s.replace(i,1,"&auml;");
else if (s[i]=='ö') s.replace(i,1,"&ouml;");
else if (s[i]=='ü') s.replace(i,1,"&uuml;");
else if (s[i]=='ß') s.replace(i,1,"&szlig;");
else if (s[i]=='Ä') s.replace(i,1,"&Auml;");
else if (s[i]=='Ö') s.replace(i,1,"&ouml;");
else if (s[i]=='Ü') s.replace(i,1,"&Uuml;");
o<<"<BR>"<<s<<endl;
}
if (!in.eof()) ShowMessage("Fehler bei getline");
}
void TextToHTML(char* ifn, char* htmlfn)
{
ofstream o(htmlfn);
o<<"<HTML>"<<endl
<<"<HEAD>"<<endl
<<"<TITLE>"
<<ifn
<<"</TITLE>"<<endl
<<"</HEAD>"<<endl
<<"<BODY>"<<endl;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
PrintTextlines(o, ifn);
o<<"</BODY>"<<endl;
o<<"</HTML>"<<endl;
o.close(); // ³berfl³ssig
}
void __fastcall TForm1::TextToHtmlClick(TObject *Sender)
{
TextToHTML("Faust.txt", "c:\\test\\Faust.html");
WideString url="file:///C|\\test\\Faust.html";
CppWebBrowser1->Navigate(url,0,0,0,0);
Form1->Caption="Goethe's Faust";
//url="file:///C|\\CBuilder\\Examples\\DDraw\\directdw.htm"
// C:\CBuilder\Examples\DDraw\directdw.htm
}
//-------------------------------------------------------------------#include "\Loesungen\cpputils\kbdecl.cpp"
void print_KB(ostream& o, CKontobewegung K)
{
o<<"<TR>" // Neue Tabellenzeile
<<"<TD>"<<K.KontoNr
<<"<TD>"<<K.NameInhaber
<<"<TD>"<<K.Datum.Tag<<"."<<K.Datum.Monat<<"."<<K.Datum.Jahr
<<"<TD>"<<K.BewArt
<<"<TD>"<<K.Betrag
<<endl;
}
void PrintTable(ifstream& in, ostream& o)
{
o<<"<TABLE BORDER>"<<endl
<<"<TH>KontoNr"
<<"<TH>Name"
<<"<TH>Datum"
<<"<TH>Bew-Art"
<<"<TH>Betrag"
<<endl;
CKontobewegung K;
while(in.read((char*)&K,sizeof(K)))
print_KB(o,K);
if (!in.eof())
ShowMessage("Fehler bei read");
o<<"</TABLE>"<<endl;
}
void KBToHTML(char* ifn, char* htmlfn)
{
ifstream in(ifn,ios::binary);
ofstream o(htmlfn);
o<<"<HTML>"<<endl
<<"<HEAD>"<<endl
<<"<TITLE>"
<<ifn
<<"</TITLE>"<<endl
<<"</HEAD>"<<endl
<<"<BODY>"<<endl;
PrintTable(in, o);
o<<"</BODY>"<<endl;
o<<"</HTML>"<<endl;
o.close(); // überflüssig
}
void __fastcall TForm1::KBToHtmlClick(TObject *Sender)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
205
206
12 Lösungen
OpenDialog1->InitialDir="c:\\test";
OpenDialog1->FileName="kb.dat";
OpenDialog1->Filter = "Daten (*.dat)|*.DAT";
if (OpenDialog1->Execute())
{
KBToHTML(OpenDialog1->FileName.c_str(),"c:\\test\\kb.html");
WideString url="file:///C|\\test\\kb.html";
CppWebBrowser1->Navigate(url,0,0,0,0);
Form1->Caption="Kontobewegungen";
}
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
12.3.20 Aufgabe 4.6.6
// Datei: d:\Loesungen\kap-4\4.6\KonsAppU.cpp
#include
#include
#include
#include
<vcl\condefs.h>
<stdio.h>
<stdlib.h>
<string.h>
USERES("ConsApp.res");
#include <iostream>
using namespace std;
//------ Aufgabe 1 --------------------------------------------------int Quersumme(int n)
{ // genau wie die Lösung 3.4.7.1
if (n<0) n=-n; // falls n < 0
int s=0;
while (n>0)
{
s = s+n%10;
n = n/10;
}
return s;
}
void Zeige_QuerSummen(int a, int b)
{
cout<<"Quersummen"<<endl;
for (int i=a; i<=b; i++)
cout<<i<<": "<<Quersumme(i)<<endl;
}
//------ Aufgabe 2 --------------------------------------------------int Fibonacci(int n)
{ // genau wie die Lösung 3.4.7.2
int fib=0,f0=0,f1=1;
for (int i=0; i<n; i++)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
fib = f0 + f1;
f1 = f0;
f0 = fib;
}
return fib;
}
void zeige_Fibonacci(int n)
{
cout<<"Fibonacci-Zahlen:"<<endl;
for (int i=0; i<n; i++)// 30
cout<<"F("<<i<<")="<<Fibonacci(i)<<endl;
}
//------ Aufgabe 3 --------------------------------------------------bool prim(int n)
{ // genau wie die Lösung 3.4.7.3
if (n<2) return false;
else if (n==2) return true;
else
{
int i=2;
while (i<=n/2)
{
if (n%i==0) return false;
i++;
}
return true;
}
}
void zeige_Primzahlen(int n)
{
cout<<"Primzahlen: "<<endl;
for (int i=1;i<n; i++)
if (prim(i))
cout<<i<<endl;
}
//------ Aufgabe 4 --------------------------------------------------void zeige_PythagZahlentripel(int n)
{
for (int a=1; a<n; a++)
for (int b=a; b<n; b++)
for (int c=1; c<=a*a+b*b;c++)
if (a*a+b*b==c*c)
cout<<"a="<<a<<" b="<<b<<" c="<<c<<endl;
}
//------ Aufgabe 5 --------------------------------------------------int f3nplus1(int n, bool show)
{
if (n<=1) return 0;
int m=0;
while (n!=1)
{
m++;
if (n%2==1) n=3*n+1;
else n=n/2;
if (show) cout<<" n="<<n<<endl;
}
return m;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
207
208
}
void zeige_3nplus1(int n)
{
for (int i=0; i<n; i++)
{
int k=f3nplus1(i,false);
cout<<"i="<<i+" k="<<k<<endl;
}
}
int main(int argc, char **argv)
{
char Menuewahl=' ';;
while (Menuewahl!='Q')
{
cout<<"1. Aufgabe 1 (Quersumme)"<<endl;
cout<<"2. Aufgabe 2 (Fibonacci-Zahlen)"<<endl;
cout<<"3. Aufgabe 3 (Primzahlen und Goldbach’sche Vermutung)"<<endl;
cout<<"4. Aufgabe 4 (Pythagoräische Zahlentripel)"<<endl;
cout<<"5. Aufgabe 5 (3n+1-Problem)"<<endl;
cout<<"Q. Programmende"<<endl;
cin>>Menuewahl;
int n;
if (Menuewahl=='1') Zeige_QuerSummen(1,20);
if (Menuewahl=='2') zeige_Fibonacci(50);
if (Menuewahl=='3')
{
cin>>n;
zeige_Primzahlen(n);
}
if (Menuewahl=='4')
{
cin>>n;
zeige_PythagZahlentripel(n);
}
if (Menuewahl=='5')
{
cin>>n;
zeige_3nplus1(n);
}
}
return 0;
}
12.3.21 Aufgabe 4.6.7
// Datei: d:\Loesungen\kap-4\4.6\StrStrU.cpp
#include "StrStrU.h"
//----- Aufgabe 4.6.7 -----------------------------------------------#include <string>
#include <sstream>
using namespace std;
double StrToFloat(string s)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
209
istringstream is(s);
is.exceptions(ios::failbit);
double d;
is>>d;
return d;
}
int StrToInt(string s)
{
istringstream is(s);
is.exceptions(ios::failbit);
int i;
is>>i;
return i;
}
void TestStrToFloat(string s)
{
ostringstream out;
out<<"'"<<s<<"': "<<StrToFloat(s);
Form1->Memo1->Lines->Add(out.str().c_str());
}
void TestStrToInt(string s)
{
ostringstream out;
out<<"'"<<s<<"': "<<StrToInt(s);
Form1->Memo1->Lines->Add(out.str().c_str());
}
void TestStrTo()
{
TestStrToInt("123");
TestStrToInt("123 ");
TestStrToInt("12 3");
TestStrToInt("12A3");
TestStrToInt(" 123A");
TestStrToFloat("1.23");
TestStrToFloat("123 ");
TestStrToFloat("1.2 3");
TestStrToFloat("1.2A3");
TestStrToFloat(" 1.23A");
}
//
//
//
//
//
//
//
//
//
//
123
123
12
12
123
1.23
123
1.2
1.2
1.23
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TestStrTo();
}
12.3.22 Aufgabe 4.7
// Datei: d:\Loesungen\kap-4\4.7\AssContU.cpp
#include "AssContU.h"
#include "\Loesungen\CppUtils\TimeUtils.cpp"
#include "\Loesungen\CppUtils\StringUt.cpp"
/*
In StringUt.cpp ist auch die folgende Funktion:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
210
12 Lösungen
string getline_BUG_workaround(string z)
{
// BCB5 Bug: When using the global function getline(istream, std::string)
// to fill a string, it adds an extra null terminator to the string. This
// nullterminator is separate from the null terminator for the underlying
// char*.
// Dieser Bug ist im Service-Pack zum C++Builder5 behoben.
int n=z.length();
char x=z[n-1];
if (x=='\0')
z.erase(n-1,1);
// nur zum Testen:
n=z.length();
x=z[n-1];
return z;
}
*/
//-------- Aufgabe 1 ---------------------------------------------#include <map>
using namespace std;
typedef
typedef
typedef
InfoSys
int ValueType;
int KeyType;
map<KeyType,ValueType> InfoSys;
m;
bool ValueToKey(KeyType Key,ValueType& Value)
{
InfoSys::iterator pos=m.find(Key);
bool found=(pos!=m.end());
if (found)
Value=pos->second;
return found;
}
void __fastcall TForm1::InfoSystClick(TObject *Sender)
{
CHRTimer t;
ValueType data;
t.Start();
bool found=ValueToKey(StrToInt(Edit1->Text), data);
t.End();
if (found)
Memo1->Lines->Add(data);
else Memo1->Lines->Add("nicht gefunden!");
Memo1->Lines->Add(t.TimeStr());
/*
Aufgrund des logarithmischen Komplexität von m.find ist wegen
log(1000) ~ log(2^10) = 10
log(1000000) = log(1000*1000) ~ log(2^20) = 20
eine Verdoppelung der Suchzeiten zu erwarten. Tatsächlich konnten
aber keine Unterschiede beobachtet werden:
Gemessene Suchzeiten bei 1000 Elementen:
7,71048793979115E-5 Sekunden
7,54286863675221E-5
7,79429759131062E-5
7,87810724283008E-5
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.3 Lösungen Kapitel 4
7,79429759131062E-5
Gemessene Suchzeiten bei 1000000 Elementen:
9,7219195762584E-5
7,54286863675221E-5
8,12953619738849E-5
7,79429759131062E-5
7,12382037915486E-5
*/
}
void __fastcall TForm1::CreateDataClick(TObject *Sender)
{
m.clear();
const int max=100000;
CHRTimer t;
t.Start();
for (int i=0;i<max; i++) m[i]=i;
t.End();
Memo1->Lines->Add(t.TimeStr());
/*
Gemessene Zeiten:
100.000 map-Elemente: 1,5 Sekunden
1.000.000 map-Elemente: 17 Sekunden
*/
}
//-------- Aufgabe 2 ---------------------------------------------#include <set>
using namespace std;
set <int> RndSet;
int NewRand(unsigned int Max)
{
if (RndSet.size()<Max)
{
unsigned int Anzahl_vorher=RndSet.size(), i;
while (RndSet.size()==Anzahl_vorher) // Endlosschleife, falls
{
// s.size()>=Max
i=rand()%Max;
RndSet.insert(i);
}
return i;
}
else return -1; // oder Exception auslösen
}
// Diese Lösungsalternative ist mit jedem Container möglich, der eine
// Funktion wie find hat:
int NewRand1(unsigned int Max)
{
if (RndSet.size()<Max)
{
unsigned int i;
do // Endlosschleife, falls RndSet.size()>=Max
i=rand()%Max;
while (RndSet.find(i)!=RndSet.end());
RndSet.insert(i);
return i;
}
else return -1; // oder Exception auslösen
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
211
212
void __fastcall TForm1::NewRandClick(TObject *Sender)
{
int n=10;
for (int i=0; i<n+5; i++)
Memo1->Lines->Add(::NewRand1(n));
}
//-------- Aufgabe 3 ---------------------------------------------#include <set>
#include <fstream>
set <string> Woerterbuch;
typedef vector<string> Container;
void Woerterbuch_lesen(char* ifn)
{
Container c;
ifstream in(ifn);
in.exceptions(ios::badbit);
if (in)
{
string z;
while (getline(in,z))
{
z=getline_BUG_workaround(z);
parseString(z,c);
for (Container::iterator i=c.begin();i!=c.end(); i++)
Woerterbuch.insert(*i);
c.clear();
}
}
in.close(); // überflüssig, aber kein Fehler
}
void Text_pruefen(char* ifn)
{
Container c;
ifstream in(ifn);
in.exceptions(ios::badbit);
if (in)
{
string z;
while (getline(in,z))
{
z=getline_BUG_workaround(z);
parseString(z,c);
for (Container::iterator i=c.begin();i!=c.end(); i++)
{
if (Woerterbuch.find(*i)==Woerterbuch.end())
{ // nicht im Wörterbuch enthaltenene Wörter ausgeben:
Form1->Memo1->Lines->Add(AnsiString(z.c_str())+
": "+AnsiString(i->c_str()));
}
}
c.clear();
}
}
in.close(); // überflüssig, aber kein Fehler
}
void RechtschrPruefung()
{
Woerterbuch_lesen("faust.txt");
cout<<Woerterbuch.size()<<endl;
Text_pruefen("faust1.txt"); // In Faust1.txt wurden gegenüber
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
213
// faust.txt einige Worte verändert
}
void __fastcall TForm1::RechtSchrPrClick(TObject *Sender)
{
RechtschrPruefung();
}
//-------- Aufgabe 4 ---------------------------------------------typedef multimap<string,int> MMType;
MMType mm;
void MakeXRef(vector<string> v) // Ein XRef-Generator in 13 Zeilen
{ // erzeugt eine Crossreference-Liste aus den Strings eines Vektors
Container c;
int line=1;
for (vector<string>::iterator i=v.begin();i!=v.end(); i++)
{
parseString(*i,c);
for (Container::iterator token=c.begin();token!=c.end(); token++)
mm.insert(make_pair(*token,line));
c.clear();
line++;
}
}
void MakeXRef(char* fn) // fn: Dateiname
{ // erzeugt eine Crossreference-Liste aus den Wörtern und ihren Zeilennummern
Container c;
ifstream in(fn);
if (in)
{
string z;
int line=1;
while (getline(in,z))
{
z=getline_BUG_workaround(z);
parseString(z,c);
for (Container::iterator token=c.begin();token!=c.end(); token++)
mm.insert(make_pair(*token,line));
c.clear();
line++;
}
}
}
#include <sstream>
void PrintXRef(char* fn=0)
{ // Schreibt eine XRef-Liste in ein Memo oder in die Datei mit dem Namen fn
ofstream of;
if (fn!=0) of.open(fn);
for (MMType::iterator i=mm.begin(); i!=mm.end(); )
{
ostringstream out;
MMType::iterator k, first=mm.lower_bound(i->first);
MMType::iterator j, last=mm.upper_bound(i->first);
k=first;
out<<i->first<<": ";
for (j=first; j!=last; j++)
{
out << " "<<j->second;
i++; // Mit jedem gefundenen Wert hochzõhlen
};
if (fn!=0) of<<out.str()<<endl;
else Form1->Memo1->Lines->Add(out.str().c_str());
}
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
214
void __fastcall TForm1::XRefClick(TObject *Sender)
{
vector<string> v;
v.push_back("Alle meine Entchen");
v.push_back("schwimmen auf dem See,");
v.push_back("schwimmen auf dem See,");
MakeXRef(v);
PrintXRef();
mm.clear();
MakeXRef("AssContU.cpp");
PrintXRef();
MakeXRef("faust.txt");
PrintXRef("faust.xref");
}
//-------- Aufgabe 5 ---------------------------------------------#include "\Loesungen\CppUtils\KBDecl.cpp"
fstream f;
using namespace std;
typedef multimap<int,int> TKBMap;
TKBMap km;
void MMReadKeys(char* fn)
{
CKontobewegung K;
f.open(fn,ios::in|ios::out|ios::binary);
int pos=f.tellg();
while (f.read((char*)&K,sizeof(K)))
{
km.insert(make_pair(K.KontoNr,pos));
pos=f.tellg();
}
f.clear(); // Damit spätere seek-Zugriffe funktionieren
}
void __fastcall TForm1::KeysLesenClick(TObject *Sender)
{
OpenDialog1->InitialDir="c:\\test";
OpenDialog1->FileName="kb.dat";
OpenDialog1->Filter = "KB-Dateien (*.dat)|*.dat";
if (OpenDialog1->Execute())
{
MMReadKeys(OpenDialog1->FileName.c_str());
KeySuchen->Enabled=true;
Sortieren->Enabled=true;
}
}
bool MMFind(int Key, CKontobewegung& K)
{
TKBMap::iterator i=km.find(Key);
if (i!=km.end())
{
f.seekg(i->second);
f.read((char*)&K,sizeof(K));
return true;
}
else return false;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
void __fastcall TForm1::KeySuchenClick(TObject *Sender)
{
int Key=StrToInt(Edit1->Text);
CKontobewegung K;
if (MMFind(Key, K)) // hier nur eine ganz "primitive" Ausgabe:
Memo1->Lines->Add(IntToStr(K.KontoNr)+": "+K.NameInhaber);
else
Memo1->Lines->Add(Edit1->Text+": nicht gefunden.");
}
void MMSort(char* outfn)
{
CKontobewegung K;
ofstream out(outfn,ios::binary);
TKBMap::iterator i;
for (i=km.begin(); i!=km.end(); i++)
{
f.seekg(i->first);
f.read((char*)&K,sizeof(K));
out.write((char*)&K,sizeof(K));
}
out.close();
}
void __fastcall TForm1::SortierenClick(TObject *Sender)
{
OpenDialog1->InitialDir="c:\\test";
OpenDialog1->FileName="kb.sor";
OpenDialog1->Filter = "sortierte KB-Dateien (*.sor)|*.sor";
if (OpenDialog1->Execute())
{
MMSort(OpenDialog1->FileName.c_str());
}
}
//-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
KeySuchen->Enabled=false;
Sortieren->Enabled=false;
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
12.3.23 Aufgabe 4.8
// Datei: d:\Loesungen\kap-4\4.8\NumKlassU.cpp
#include "NumKlassU.h"
#include "\Loesungen\CppUtils\TimeUtils.cpp" // Nur ein "\" bei Pfadangaben!!!
//------- Aufgaben 4.8.1 --------------------------------------------//------- Aufgabe 1 --------------------------------------------------
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
215
216
#include <complex>
using namespace std;
AnsiString ComplexToStr(complex<long double> c)
{
return FloatToStr(real(c))+"+"+FloatToStr(imag(c))+"i";
}
void __fastcall TForm1::QuadGlClick(TObject *Sender)
{
complex<double> a(2,3), b(4,-5), c(-7,8), x1, x2;
// complex<double> a(2,0), b(4,0), c(-7,0), x1, x2;
x1= (-b+sqrt(b*b - 4.0*a*c))/(2.0*a);
x2= (-b-sqrt(b*b - 4.0*a*c))/(2.0*a);
Form1->Memo1->Lines->Add(ComplexToStr(x1));
Form1->Memo1->Lines->Add(ComplexToStr(x2));
// Probe:
Form1->Memo1->Lines->Add(ComplexToStr(a*x1*x1+b*x1+c));
Form1->Memo1->Lines->Add(ComplexToStr(a*x2*x2+b*x2+c));
}
// Mit typedef lassen sich die Datentypen etwas einfacher schreiben:
typedef complex<double> dcomplex;
AnsiString DComplexToStr(dcomplex c)
{
return FloatToStr(real(c))+"+"+FloatToStr(imag(c))+"i";
}
//------- Aufgabe 2 -------------------------------------------------void __fastcall TForm1::EinheitswurzelnClick(TObject *Sender)
{
const int n=100;
complex<double> w(cos(2*M_PI/n),sin(2*M_PI/n));
for (int i=0; i<n; i++)
{
complex<double> z=pow(w,i);
Memo1->Lines->Add(ComplexToStr(pow(z,n)));
}
}
//------- Aufgabe 3 -------------------------------------------------#include "\Loesungen\cpputils\GraphUtils.cpp"
int Funktion=0;
void __fastcall TForm1::ScrollBar1Change(TObject *Sender)
{
Funktion=1;
if (Funktion >5) Funktion=1;
double x0, y0, x1, y1, dx, dy;
if
(Funktion==1) { x0=-2; y0=-0.2; x1=2; y1=5;
dx=0.5; dy=0.5;}
else if (Funktion==2) { x0=-1; y0=0; x1=6; y1=100; dx=1; dy=10;}
else if (Funktion==3) { x0=-2; y0=-2; x1=2; y1=4;
dx=1; dy=1;}
else if (Funktion==4) { x0=-2; y0=-6; x1=8; y1=10; dx=1; dy=1;}
Image1->Canvas->Brush->Color=clWhite; // lösche das alte Bild
Image1->Canvas->Rectangle(0,0,Image1->ClientWidth,
Image1->ClientHeight);
Gitternetz(Form1->Image1,x0,x1,dx, Image1->ClientWidth,
y0,y1,dy, Image1->ClientHeight);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.3 Lösungen Kapitel 4
Image1->Canvas->Pen->Color = clRed;
for (int px=0; px<=Image1->Width-/*–*/1; px++)
{
// transformiere px in Welt-Koordinaten
double x=x_Welt(px, x0, x1, Form1->Image1->Width);
double y;
dcomplex z(x,ScrollBar1->Position/10.0); //
if
(Funktion==1) y= abs(sin(z));
else if (Funktion==2) y= abs(exp(z));
else if (Funktion==3) y= abs(z*z);
else if (Funktion==4) y= abs(z*sin(z));
// transformiere y in Bildkoordinaten
int py=y_Bildschirm(y, y0, y1, Image1->Height);
if (px == 0) Image1->Canvas->MoveTo(px,py);
else
Image1->Canvas->LineTo(px,py);
}
}
//-------
Aufgaben 4.8.2 --------------------------------------------
void Kreuz(int x, int y, int w)
{
Form1->Image1->Canvas->MoveTo(x-w,y);
Form1->Image1->Canvas->LineTo(x+w,y);
Form1->Image1->Canvas->MoveTo(x,y+w);
Form1->Image1->Canvas->LineTo(x,y-w);
}
// Die folgenden Variablen nur deshalb global, da
// die Teilaufgaben in eigenen Funktionen behandelt
// werden:
double sxy, sx, sy, sxx, ym, xm;
const int n=100; // n=2; n=100; n=10000;
#include <valarray>
using namespace std;
valarray<double> x(n), y(n);
void Kleinste_Quadrate_mit_valarrays()
{ // Aufgabe a)
sxy = (x*y).sum();
sx = x.sum();
sy = y.sum();
sxx = (x*x).sum();
xm = sx/n;
ym = sy/n;
}
void Kleinste_Quadrate_mit_Arrays()
{ // Aufgabe b)
sxy=0;
for (int i=0; i<n; i++) sxy=sxy+x[i]*y[i];
sx=0;
for (int i=0; i<n; i++) sx=sx+x[i];
sy=0;
for (int i=0; i<n; i++) sy=sy+y[i];
sxx=0;
for (int i=0; i<n; i++) sxx=sxx+x[i]*x[i];
xm=sx/n;
ym=sy/n;
}
void __fastcall TForm1::kleinsteQuadrateClick(TObject *Sender)
{// Methode der kleinsten Quadrate
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
217
218
12 Lösungen
// Aufgabe 4.8.2.1
const double a_=2, b_=3;
// Aufgabe 4.8.2.2 a): rnd=0;
// Aufgabe 4.8.2.2 b): rnd>0;
double rnd=5; // mit positivem Wert ergibt sich eine Streuung
for (int i=0; i<n; i++)
{ // Testwerte erzeugen
x[i] = i;
y[i] = a_*x[i] + b_ + rnd*((10-1.0)/2-rand()%10);
}
// Kleinste_Quadrate_mit_valarrays();
Kleinste_Quadrate_mit_Arrays();
double a=(n*sxy - sx*sy)/(n*sxx - sx*sx);
double b=ym-a*xm;
Memo1->Lines->Add("Kleinste Quadrate: ");
Memo1->Lines->Add("a_="+FloatToStr(a_)+ " a="+FloatToStr(a));
Memo1->Lines->Add("b_="+FloatToStr(b_)+ " b="+FloatToStr(b));
// Aufgabe 4.8.2.3
// zeichen die Punkte als Rechtecke
// Weltkoordinaten
int x0=x.min();
int x1=x.max();
int y0=y.min();
int y1=y.max();
Image1->Canvas->Pen->Color = clRed; // Kreuze rot zeichnen
for (int i=0; i<n; i++)
{ // #include "\Loesungen\cpputils\GraphUtils.cpp" vorausgesetzt
// transformiere (x[i],y[i]) in Bildkoordinaten
int px=x_Bildschirm(x[i], x0, x1, Image1->Width);
int py=y_Bildschirm(y[i], y0, y1, Image1->Height);
Kreuz(px, py, 5);
}
Image1->Canvas->Pen->Color = clBlack; // Gerade schwarz
// Anfangspunkt der Geraden
int px=x_Bildschirm(0,
x0,x1,Image1->Width);
int py=y_Bildschirm(a*0+b, y0,y1,Image1->Height);
Image1->Canvas->MoveTo(px,py);
// Endpunkt der Geraden
px=x_Bildschirm(n-1,
x0,x1,Image1->Width);
py=y_Bildschirm(a*(n-1)+b, y0,y1,Image1->Height);
Image1->Canvas->LineTo(px,py);
}
12.4 Lösungen Kapitel 5
12.4.1 Aufgaben 5.2.19
// Datei: d:\Loesungen\kap-5\5.2\ExprU.cpp
#include "ExprU.h"
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
//----- Aufgabe 1. -----------------------------------------------#include <typeinfo>
using namespace std;
int i=0;
void Aufgabe1()
{
int i=2,j=i,k=-6, m=8, n=10000, x=0, h[5];
unsigned u=40000u,v=u;
// Aufgaben
int a= k/i/j+n/j;
// a = ((k/i)/j)) + (n/j);
//
= -3/2 + 5000 = 4999
Form1->Memo1->Lines->Add(a);
Form1->Memo1->Lines->Add(typeid(k/i/j+n/j).name()); // int
int b= k/i/j++ + m/j++;
// Das Ergebnis ist undefiniert, da es davon abhängt, ob j++ zuerst im
// ersten oder im zweiten Teilausdruck erhöht wird
Form1->Memo1->Lines->Add(b);
Form1->Memo1->Lines->Add(typeid(k/i/j++ + m/j++).name()); // int
int c= i*4%-k*3;
// c = ((i*4)%(-k))*3;
//
= (8%6)*3=6
// Alle Operatoren sind multiplikative Operatoren und werden linksassoziativ
ausgewertet:
Form1->Memo1->Lines->Add(c);
Form1->Memo1->Lines->Add(typeid(i*4%-k*3).name()); // int
int d= k/-m-2;
// d = (k/-m)-2;
//
= 0-2 = -2
Form1->Memo1->Lines->Add(d);
Form1->Memo1->Lines->Add(typeid(k/-m-2).name()); // int
double e= m*n/u/v;
// Obwohl die linke Seite den Datentyp double hat, wird der Ausdruck als
// Ganzzahlwert ausgewertet.
// e = ((m*n)/u)/v;
//
= 2/40000 = 0
Form1->Memo1->Lines->Add(e);
Form1->Memo1->Lines->Add(typeid(m*n/u/v).name()); // unsigned int
int f= ++::i%++i;
// f = ++::i%++i;
//
= 1%3 = 1
Form1->Memo1->Lines->Add(f);
Form1->Memo1->Lines->Add(typeid(++::i%++i).name()); // int
int g = sizeof 17-2;
// g = (sizeof 17)-2;
//
= 4-2 = 2
Form1->Memo1->Lines->Add(g);
Form1->Memo1->Lines->Add(typeid(sizeof 17-2).name()); // unsigned int
while(x<=5) h[x]=x++;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
219
220
12 Lösungen
// Das Ergebnis der Anweisungen in der Schleife ist
// nicht definiert, da nicht klar ist, ob a[x] vor oder nach der Erhöhung
// von x angesprochen wird.
// Außerdem wird das Arrayelement A[5] angesprochen, für das
// kein Speicherplatz reserviert ist.
Form1->Memo1->Lines->Add(typeid(x++).name()); // int
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
Aufgabe1();
}
//----- Aufgabe 2. -----------------------------------------------void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
// a)
int w=1;
for (int i=0; i<8*sizeof(w); i++)
{
Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(w));
// mit "+" werden die verschiedenen Strings zu
// einem einzigen "zusammengeklebt"
w = w<<1;
}
// b)
w=1;
for (int i=0; i<8*sizeof(w); i++)
Memo1->Lines->Add(IntToStr(i)+": "+IntToStr(w<<i));
}
//----- Aufgabe 3. -----------------------------------------------void Aufgabe3()
{
int a,b,c,d,i;
// a)
if (a<=b && c<=d);// ...
// Da der Operator <= eine höhere Priorität hat als &&, wird dieser
// Ausdruck wie (a<=b) && (c<=d) behandelt.
//
if
//
//
//
//
//
//
//
//
//
//
//
//
if
//
//
//
//
b)
(a<=b & c<=d);// ...
Da der Operator <= eine höhere Priorität hat als &, wird dieser
Ausdruck wie (a<=b) & (c<=d) behandelt.
Die beiden Operanden (a<=b und (c<=d) sind boolesche Werte, die
für & in Ganzzahlwerte konvertiert werden. Dabei wird false zu 0 und
true zu 1. Für die verschiedenen Kombinationen ergibt sich so
dasselbe Ergebnis wie unter a)
p
true
true
false
false
q
true
false
true
false
int(p)
1
1
0
0
int(q)
1
0
1
0
p&q
1
0
0
0
p&&q
true
false
false
false
c)
(a<=b == c<=d);// ...
Da der Operator == eine höhere Priorität hat als <=, wird dieser
Ausdruck wie a<=(b == c)<=d behandelt.
Der Ausdruck (a<=b == c<=d) wird auf den ersten Blick oft als
(a<=b) == (c<=d) interpretiert.
for (int a=0; a<10; a++)
for (int b=0; b<10; b++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
221
for (int c=0; c<10; c++)
for (int d=0; d<10; d++)
//
if ( (a<=b == c<=d)==((a<=b) == (c<=d)) )
if ( ((a<=(b == c))<=d)!=((a<=b) == (c<=d)) )
Form1->Memo1->Lines->Add(IntToStr(a)+" "+IntToStr(b)+" "+IntToStr(c)+" "
+IntToStr(d));
// d)
for (int i=0; i<5; i++)
if (i&1)
Form1->Memo1->Lines->Add(IntToStr(i));
//
//
//
//
Für ungerade Werte von i hat i&1 den Wert 1
und für gerade den Wert 0.
Da der Wert 1 in true und 0 in false konvertiert wird, ist die
Bedingung für ungerade Werte von i erfüllt.
// e)
for (int i=0; i<5; i++)
if (i&1==0)
Form1->Memo1->Lines->Add(IntToStr(i));
// Da == eine höhere Priorität hat als &, wird dieser Ausdruck wie
// i&(1==0) interpretiert und ist immer false. Viele Leser
// interpretieren den Ausdruck auf den ersten wie (i&1)==0
}
void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
Aufgabe3();
}
//----- Aufgabe 4. -----------------------------------------------#include <stdio.h> // notwendig für sprintf
void Aufgabe4()
{
double x = 3.14;
int a=1,b=2;
// a)
int s = a?b:x; // s=2
Form1->Memo1->Lines->Add(typeid(a?b:x).name()); // double
// b)
{
int s = (x > 0) ? 1 : (x < 0) ? -1 : 0;
s = (x > 0) ? 1 : ((x < 0) ? -1 : 0);
if (x>0) s=1;
else if (x<0) s=-1;
else s=0;
}
// c)
for (int n=0;n<4;n++)
{
char s[100];
sprintf(s,"%d file%c found",n,(n==1)?' ':'s');
Form1->Memo1->Lines->Add(s);
}
// d)
// Übersetzen Sie die Anweisung nach d) ins Deutsche.
for (int n=0;n<4;n++)
{
char s[100];
sprintf(s,"%d Datei%s gefunden",n,(n==1)?"":"en");
Form1->Memo1->Lines->Add(s);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
222
12 Lösungen
}
void __fastcall TForm1::Aufg4Click(TObject *Sender)
{
Aufgabe4();
}
//----- Aufgabe 5. -----------------------------------------------void Aufgabe5()
{
int i,j,k;
i=3;j=1;k=2; k=i+++j;
// k=(i++)+j=4, i=4
Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+"
i=3;j=1;k=2; k= ++i+j++; // k=(++i)+(j++)=5, i=4, j=2
Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+"
i=3;j=1;k=2; k= -i+++j; // k=(-(i++))+j=-2, i=4,
Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+"
i=3;j=1;k=2; k= +i+-j;
// k=(+i)+(-j)=2
Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+"
i=3;j=1;k=2; k= i--+--j; // k=(i--)+(--j)=3, i=2, j=0
Form1->Memo1->Lines->Add("i="+IntToStr(i)+" j="+IntToStr(j)+"
}
k="+IntToStr(k));
k="+IntToStr(k));
k="+IntToStr(k));
k="+IntToStr(k));
k="+IntToStr(k));
void __fastcall TForm1::Aufg5Click(TObject *Sender)
{
Aufgabe5();
}
//----- Aufgabe 6. -----------------------------------------------// a)
void SetROH(const char* FileName)
{
SetFileAttributes(FileName,FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN);
}
// b)
bool isSystemFile(const char* FileName)
{
DWORD attrib=GetFileAttributes(FileName);
if (attrib!= 0xFFFFFFFF)
return attrib&FILE_ATTRIBUTE_SYSTEM;
else return false; // z. B. falls die Datei nicht existiert
}
// c)
bool SetFileAttributeToHidden(const char* FileName)
{
DWORD attrib=GetFileAttributes(FileName);
if (attrib!= 0xFFFFFFFF)
return SetFileAttributes(FileName,attrib|FILE_ATTRIBUTE_HIDDEN);
else return false;
}
// d)
bool RemoveFileAttributeHidden(const char* FileName)
{
DWORD attrib=GetFileAttributes(FileName);
if (attrib!= 0xFFFFFFFF)
return SetFileAttributes(FileName,attrib&(~FILE_ATTRIBUTE_HIDDEN));
else return false;
}
#include <string.h>
void Aufgabe6()
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
223
{
// Vor dem Aufruf dieser Funktion müssen die folgenden Dateien zuerst
// angelegt werden:
LPCTSTR
LPCTSTR
LPCTSTR
LPCTSTR
FileName1="c:\\test\\test1.dat";
FileName2="c:\\test\\test2.dat";
FileName3="c:\\test\\test3.dat";
FileName4="c:\\test\\test4.dat";
SetROH(FileName1);
if (isSystemFile(FileName2))
Form1->Memo1->Lines->Add(AnsiString(FileName2)+" ist Systemdatei");
else
Form1->Memo1->Lines->Add(AnsiString(FileName2)+" ist keine Systemdatei");
if (SetFileAttributeToHidden(FileName3))
Form1->Memo1->Lines->Add(AnsiString(FileName3)+" Attribut Hidden gesetzt");
else
Form1->Memo1->Lines->Add(AnsiString(FileName3)+" Attribut Hidden nicht
gesetzt");
if (RemoveFileAttributeHidden(FileName4))
Form1->Memo1->Lines->Add(AnsiString(FileName4)+" Attribut Hidden gelöscht");
else
Form1->Memo1->Lines->Add(AnsiString(FileName4)+" Attribut Hidden nicht
gelöscht");
}
void __fastcall TForm1::Aufg6Click(TObject *Sender)
{
Aufgabe6();
}
12.4.2 Aufgaben 5.3.4
1. n ungerade, d. h. n/2 = (n–1)/2
x
x0
p = p*x
n = n/2
x = x*x
n
n0
p
p0
p0*x0
(n0-1)/2
x0*x0
p*xn = (p0*x0)*(x0*x0)(n0–1)/2 = p0*x0*x0no–1 = p0*x0n0 = uv
2. n gerade, d. h. n/2 = n/2:
x
x0
n
n0
n = n/2
x = x*x
p
p0
n0/2
x0*x0
p*xn = p0*(x0*x0)(n0/2) = p0*x0n0 = uv
3.
s
s0
i = i + 1
s = s + i
i
i0
i0 + 1
s0 + i0 + 1
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
224
12 Lösungen
Damit gilt anschließend
s = s0 + (i0 + 1) = 1 + 2 + ... + i0 + (i0 + 1)
= 1 + 2 + ... + (i–1) + i
4.
s
s0
i
i0
i = i + 1
i0 + 1
s = s + i*i
s0 + (i0+1)*(i0+1)
Damit gilt anschließend
s = 1*1 + 2*2 + ... + i*i
5.
s
s0
s = s + i*i
i = i + 1
i
i0
s0 + i0*i0
i0 + 1
Damit gilt anschließend s = 1*1 + 2*2 + ... + i0*i0 + i0*i0
= 1*1 + 2*2 + ... + 2*(i–1)*(i–1)
und nicht die gefragte Invarianz.
12.4.3 Aufgabe 5.5
// Datei: d:\Loesungen\kap-5\5.5\BlockU.cpp
#include "BlockU.h"
void verschachtelt()
{
int i,j,k=7;
AnsiString s,t;
{
char j;
{
float i=0,j=i;
{
AnsiString i,j,k;
i = "ckt"; j = "ra"; k = "vert"; // i, j, k: AnsiString
s = s+k+j+i+" ";
// i, j, k, s: AnsiString
}
i = 1; j = 2;
// i, j: int
t = FloatToStr(i)+" + "+FloatToStr(j)+" = "+FloatToStr(k);//i,j,k: int
{
AnsiString t,j,k,i=s;
{
char t,j,k;
t = 's'; j = 'i'; k = 't';
// t, j, k: char
s = AnsiString(' ')+j+t+k+ ' '; // s: AnsiString, t, j, k: char
}
{
char t,j,k;
t = 'a'; j = 'D'; k = 's';
// t, j, k: char
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
225
s = AnsiString(j) + t + k+s;
// s: AnsiString, t, j, k: char
}
t = "nz sch"; j = "ja ga"; k = "ön "; // t, j, k: AnsiString
s = s+j+t+k+i;
// s, t, j, k, i: AnsiString
}
}
}
Form1->Memo1->Lines->Add(t);
Form1->Memo1->Lines->Add(s);
}
// t: AnsiString
// s: AnsiString
void __fastcall TForm1::VerschachteltClick(TObject *Sender)
{
verschachtelt();
/* Erzeugt die Ausgabe:
1 + 2 = 7
Das ist ja ganz schön vertrackt
*/
}
12.4.4 Aufgabe 5.6
// Datei: d:\Loesungen\kap-5\5.6\SpeicherklU.cpp
#include "SpeicherklU.h"
int Fibonacci_(int n)
{
int fib=0,f0=0,f1=1;
for (int i=0; i<n; i++)
{
fib = f0 + f1;
f1 = f0;
f0 = fib;
}
return fib;
}
int Fibonacci(int n)
{
const int max=50;
static int result[max];
static bool firstTime=true;
if (firstTime)
{ // Wird nur beim ersten Aufruf der Funktion ausgeführt.
for (int i=0; i<max; i++)
result[i]=Fibonacci_(i);
firstTime=false;
}
if (0<=n && n<max) return result[n];
else return -1;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (int i=0; i<50; i++)
Memo1->Lines->Add(IntToStr(Fibonacci(i)));
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
226
12 Lösungen
12.4.5 Aufgabe 5.7
// Datei: d:\Loesungen\kap-5\5.7\ifu.cpp
#include "ifu.h"
//----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
float x=17.1,Punkte;
int i=0,j;
bool b;
// a)
if (x==17.1) Memo1->Lines->Add("Volltreffer");
// Prüfungen auf Gleichheit mit einer Gleitkommavariablen haben nicht
// immer das erwartete Ergebnis.
//
if
//
//
b)
(i>=1 && i<=10) Edit1->Text = "Volltreffer";
Syntaktisch und semantisch korrekt. Verknüpfte Bedingungen müssen nicht
geklammert werden.
// c)
// if b && (i=j*x) Edit1->Text = "Volltreffer";
// Syntaxfehler: Die Bedingung in einer if-Anweisung muss in einer Klammer
// stehen. "=" ist ein Zuweisung und keine Prüfung auf Gleichheit.
// d)
if (Punkte >= 0) Edit1->Text="Extremes Pech";
else if (Punkte >= 20) Edit1->Text="Ziemliches Pech";
else if (Punkte >= 40) Edit1->Text="Ein wenig Glück gehabt";
// Syntaktisch korrekt, aber „<" mit „>" verwechselt (der zweite oder
// dritte else-Zweig wird nie erreicht).
}
//----- Aufgabe 2 -----------------------------------------------AnsiString NoteToStr(int Note)
{
if (Note==1)
return "sehr gut!!!";
else if (Note==2) return "gut";
else if (Note==3) return "na ja";
else if (Note==4) return "schwach";
else if ((Note==5) || (Note==6)) return "durchgefallen";
else return "Undefinierte Note "+IntToStr(Note);
}
void __fastcall TForm1::NoteClick(TObject *Sender)
{
Memo1->Lines->Add(NoteToStr(1));
Memo1->Lines->Add(NoteToStr(2));
Memo1->Lines->Add(NoteToStr(3));
Memo1->Lines->Add(NoteToStr(4));
Memo1->Lines->Add(NoteToStr(5));
Memo1->Lines->Add(NoteToStr(6));
Memo1->Lines->Add(NoteToStr(7));
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
//----- Aufgabe 3 -----------------------------------------------double LA_Summe,LB_Summe,MA_Summe,MB_Summe,MC_Summe,Summe;
void LagMat(char Lagergruppe, char Materialgruppe)
{
if (Lagergruppe == 'A')
{
if (Materialgruppe == 'A')
{
LA_Summe = LA_Summe + Summe;
MA_Summe = MA_Summe + Summe;
}
else if (Materialgruppe == 'B')
{
LA_Summe = LA_Summe + Summe;
MB_Summe = MB_Summe + Summe;
}
else if (Materialgruppe == 'C')
{
LA_Summe = LA_Summe + Summe;
MC_Summe = MC_Summe + Summe;
}
else ShowMessage("Unzulässige Materialgruppe in Lager A");
}
else if (Lagergruppe == 'B')
{
if (Materialgruppe == 'A')
{
LB_Summe = LB_Summe + Summe;
MA_Summe = MA_Summe + Summe;
}
else if (Materialgruppe == 'B')
{
LB_Summe = LB_Summe + Summe;
MB_Summe = MB_Summe + Summe;
}
else ShowMessage("Unzulässige Materialgruppe in Lager B");
}
else ShowMessage("Unzulässige Lagergruppe in Lager B");
}
void __fastcall TForm1::MatLagClick(TObject *Sender)
{
LagMat('A','B');
}
//----- Aufgabe 4 -----------------------------------------------struct CDatum {
int Tag, Monat, Jahr;
};
bool vorher (CDatum d1, CDatum d2)
{
if (d1.Jahr!=d2.Jahr) return d1.Jahr<d2.Jahr;
else // d1.Jahr==d2.Jahr
if (d1.Monat != d2.Monat) return d1.Monat < d2.Monat;
else // (d1.Jahr==d2.Jahr) and (d1.Monat == d2.Monat)
return d1.Tag < d2.Tag;
//
//
//
//
Falls die boolesche Variable vorher_oder_gleich genau dann den
Wert true erhalten soll, wenn das erste Datum zeitlich vor dem
zweiten liegt oder gleich dem zweiten ist, muss lediglich die
letzte Zeile auf <= geändert werden zu:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
227
228
//
//
}
12 Lösungen
return d1.Tag < d2.Tag;
void testDatumsvergleich(CDatum d1, CDatum d2)
{
AnsiString s1=IntToStr(d1.Tag)+"."+IntToStr(d1.Monat)+"."+
IntToStr(d1.Jahr);
AnsiString s2=IntToStr(d2.Tag)+"."+IntToStr(d2.Monat)+"."+
IntToStr(d2.Jahr);
if (vorher(d1,d2))
Form1->Memo1->Lines->Add(s1+" liegt vor "+s2);
else
Form1->Memo1->Lines->Add(s1+" liegt nicht vor "+s2);
}
void __fastcall TForm1::DatumsverglClick(TObject *Sender)
{
CDatum d1={1,2,3};
CDatum d2={1,2,3};
testDatumsvergleich(d1, d2);
}
//----- Aufgabe 5 -----------------------------------------------// Hier sind die Datentypen double (Funktion dEst) und Currency
// (Funktion CEst) angemessen.
// typedef double Gleitkommatyp;
typedef float Gleitkommatyp;
// Mit diesem typedef kann der Gleitkommatyp für die Est-Berechnung leicht
// geändert werden, um die float- und die double-Version zu vergleichen.
// a)
int dEst(Gleitkommatyp x)
{ // Einkommensteuertarif nach § 32a (1)
Gleitkommatyp est;
x = (int(x)/54)*54; // Nur zulässig für Werte von x im Wertebereich
// von int, bis 2.147.483.647 DM
if (x <= 12365) est = 0;
// Die Bedingung (0 <= x) && (x <= 12365) würde ESt für negative Werte
// von x (negative Einkommen sind mit Abschreibungen oder Verlusten
// möglich) undefiniert lassen.
else if (x <= 58643)
{ // Die Bedingung (x > 12095) braucht hier nicht mehr geprüft zu
// werden, da sie sich bereits aus der Negation der letzten Bedingung
// ergibt.
Gleitkommatyp y = (x-12312)/10000; // letzter Absatz (1)
est = (91.19*y+2590)*y;
}
else if (x <= 120041)
{
Gleitkommatyp z = (x-58590)/10000; // letzter Absatz (1)
est = (151.96*z + 3434)*z +13938;
}
else est = 0.53*x - 22843;
return est; // Absatz 3, letzter Satz: runde den Steuerbetrag auf volle
// DM ab. Das funktioniert aber nur im Wertebereich von int
};
Currency CEst(Currency x)
{ // Einkommensteuertarif nach § 32a (1)
Currency est;
x=(x/540000)*540000; // speziell für Currency, schneidet auch
// Nachkommastellen ab
if (x <= 12365) est = 0;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
// Die Bedingung (0 <= x) && (x <= 12365) würde ESt für negative Werte
// von x (negative Einkommen sind mit Abschreibungen oder Verlusten
// möglich) undefiniert lassen.
else if (x <= 58643)
{
Currency y = (x-12312)/10000; // letzter Absatz (1)
est = (91.19*y+2590)*y;
}
else if (x <= 120041)
{
Currency z = (x-58590)/10000;// letzter Absatz (1)
est = (151.96*z + 3434)*z +13938;
}
else est = 0.53*x -22843;
return 10000*(est/10000); // so wird für Currency der Steuerbetrag auf
// volle DM abgerundet (Absatz 3, letzter Satz)
};
void EstVergl(int x,int Soll)
{
Form1->Memo1->Lines->Add(IntToStr(x)+": "+IntToStr(dEst(x))+", " +
CurrToStr(CEst(x))+
" Soll="+IntToStr(Soll));
}
void test_Est()
{
EstVergl(10800,0);
EstVergl(16200,1020);
EstVergl(21600,2484);
EstVergl(27000,4000);
EstVergl(32400,5570);
EstVergl(37800,7193);
EstVergl(43200,8870);
EstVergl(48600,10599);
EstVergl(54000,12381);
EstVergl(59400,14217);
EstVergl(64800,16129);
EstVergl(70200,18129);
EstVergl(75600,20218);
EstVergl(81000,22396);
EstVergl(86400,24663);
EstVergl(91800,27018);
EstVergl(97200,29461);
EstVergl(102600,31994);
EstVergl(108000,34615);
EstVergl(113400,37324);
EstVergl(118800,40123);
}
void test_compEst(int von, int bis, int delta)
{
// Diese Funktion zeigt an, bei welchen Werten sich das Ergebnis der float// bzw. double-Version dEst und das der Currency-Version cEst
// unterscheiden. Bei meinen Tests waren alle double-Ergebnisse, aber nicht
// alle float-Ergebnisse, gleich den Currency-Ergebnissen.
for (int x=von; x<=bis; x+=delta)
if (dEst(x)!=CEst(x))
Form1->Memo1->Lines->Add(FloatToStr(x)+": "+
FloatToStr(dEst(x))+" != "+FloatToStr(CEst(x)));
}
void __fastcall TForm1::EStClick(TObject *Sender)
{
test_Est();
test_compEst(0,1000000,1);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
229
230
12 Lösungen
}
// b)
int Splitting(double x)
{
return 2*dEst(x/2);
}
//----- Aufgabe 5.7.2 -------------------------------------------//----- Aufgabe 1 -----------------------------------------------void LagMatSwitch(char Lagergruppe, char Materialgruppe)
{
switch (Lagergruppe)
{
case 'A':
switch (Materialgruppe)
{
case 'A': LA_Summe = LA_Summe + Summe;
MA_Summe = MA_Summe + Summe;
break;
case 'B': LA_Summe = LA_Summe + Summe;
MB_Summe = MB_Summe + Summe;
break;
case 'C': LA_Summe = LA_Summe + Summe;
MC_Summe = MC_Summe + Summe;
break;
default: ShowMessage("Unzulässige Materialgruppe in Lager A");
}
case 'B':
switch (Materialgruppe)
{
case 'A': LB_Summe = LB_Summe + Summe;
MA_Summe = MA_Summe + Summe;
break;
case 'B': LB_Summe = LB_Summe + Summe;
MB_Summe = MB_Summe + Summe;
break;
default: ShowMessage("Unzulässige Materialgruppe in Lager B");
}
default: ShowMessage("Unzulässige Lagergruppe in Lager B");
}
}
void __fastcall TForm1::NoteSwClick(TObject *Sender)
{
LagMatSwitch('A','B');
}
//----- Aufgabe 2 -----------------------------------------------void __fastcall TForm1::DatumsverglSwClick(TObject *Sender)
{
AnsiString s=
"Da die Abfragen nicht durch eine Prüfung auf Gleichheit mit einer "
"Konstanten gebildet werden, ist eine Lösung mit switch nicht möglich.";
Memo1->Lines->Add(s);
}
//----- Aufgabe 3 -----------------------------------------------void __fastcall TForm1::SteuerClick(TObject *Sender)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
231
AnsiString s=
"Die Auswahl der Berechnungsvorschrift zur Berechnung der Einkommensteuer "
"kann nicht in einer switch-Anweisung erfolgen, da die Bedingungen nicht "
"durch eine Prüfung auf Gleichheit gebildet werden und der Datentyp der "
"zum Vergleich herangezogenen Ausdrücke ein Gleitkommadatentyp ist.";
Memo1->Lines->Add(s);
}
12.4.6 Aufgaben 5.7.3
1. Aus Platzgründen steht „t“ für „true“ und „f“ für „false“.
a)
p
q
r
q or r
t
t
t
t
f
f
f
f
t
t
f
f
t
t
f
f
t
f
t
f
t
f
t
f
t
t
t
f
t
t
t
f
p
q
r
q and r
t
t
t
t
f
f
f
f
t
t
f
f
t
t
f
f
t
f
t
f
t
f
t
f
t
f
f
f
t
f
f
f
p
q
r
p and q
t
t
t
t
f
f
f
f
t
t
f
f
t
t
f
f
t
f
t
f
t
f
t
f
t
t
f
f
f
f
f
f
p and
(q or r)
t
t
t
f
f
f
f
f
p and q
p and r
t
t
f
f
f
f
f
f
t
f
t
f
f
f
f
f
(p and q)
or (p and r)
t
t
t
f
f
f
f
f
b)
p or
(q and r)
t
t
t
t
t
f
f
f
p or q
p or r
(p or q) and
(p or r)
t
t
t
t
t
f
f
f
t
t
t
t
t
t
f
f
t
t
t
t
t
f
t
f
q or r
p and (q or r)
t
t
t
f
t
t
t
f
t
t
t
f
f
f
f
f
c)
(p and q)
or r
t
t
t
f
t
f
t
f
"(p and q) or r" und "p and (q or r)" sind also nicht gleichwertig.
2.
a) (x >= 0) and (x <= 100)
b) (x = 1) or (c <> 'j')
c) (i < 1) and (i > 10). Da diese Bedingung für kein i erfüllt sein kann, wird S2 nie ausgeführt.
3. S1: (1 <= x) and (x <= 10)
S2: (10 < x) and (x <= 15)
S3: (15 > x)
S4: (x < 1)
4. Im letzten else-Zweig gilt die Negation der beiden vorangehenden Bedingungen:
not((x >= y) and (x >= z)) and not ((y >= x) and (y >= z))
Diese sind wegen de Morgan gleichwertig mit:
((x < y) or (x < z)) and ((y < x) or (y < z))
Zweimal "ausmultiplizieren" mit den Distributivgesetzen ergibt:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
232
12 Lösungen
[(x<y) and (y<x)] or [(x<y) and (y<z)] or [(x<z) and (y<x)] or [(x<z) and (y<z)]
Hier wurden die 4 mit or verknüpften Ausdrücke extra mit eckigen Klammern zusammengefasst. Der erste dieser
eckig geklammerten Ausdrücke hat immer den Wert false, während alle 3 weiteren jeder für sich gerade bedeuten,
dass z das Maximum der drei Werte x, y und z ist.
5. In den Aufgaben 5.3.4.1 und 5.3.4.2 wurden bereits die folgenden beiden Invarianten nachgewiesen:
n gerade:
n
// p*x = u
n = n/2;
x = x*x;
n
v
// p*x = u
n ungerade:
// p*x
p = p*x;
n = n/2;
x = x*x;
n
v
// p*x = u
n
= u
v
v
Das sind aber gerade die Anweisungsfolgen, die durch
if (n&1)
n = n/2;
x = x*x;
p = p*x;
ausgeführt werden, wenn n gerade oder ungerade ist. Damit gilt die gefragte Invarianz
n
v
// p*x = u
if (n&1) p = p*x;
n = n/2;
x = x*x;
n
v
// p*x = u
6. Begründen oder widerlegen Sie, dass die Funktion median immer den mittleren der drei als
Parameter übergebenen Werte liefert:
T median (T a, T b, T c) // T irgendein Datentyp
{
if (a < b)
if (b < c)
return b; // a<b and b<c
else if (a < c)
return c; // a<b and c<=b and a<c
else return a; // a<b and c<=b and c<=a
else if (a < c)
return a; // b<=a and a<c
else if (b < c)
return c; // b<=a and c <= a and b<c
else return b; // b<=a and c <= a and b>=c
}
12.4.7 Aufgabe 5.8
// Datei: d:\Loesungen\kap-5\5.8\SchleifU.cpp
#include "SchleifU.h"
void __fastcall TForm1::FormCreate(TObject *Sender)
{
EKapital->Text="1000";
EZinssatzVerzinsung->Text="3";
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
ELaufzeit->Text="10";
EDarlehen->Text="100000";
EZinssatzHypo->Text="6";
ETilgung->Text="2";
}
//----- Aufgabe 5.8.3.1 ---------------------------------------------void Zinsen(Currency Kapital, Currency Zinssatz, int Laufzeit)
{
int Jahre = 0;
if (Jahre < Laufzeit)
do
{
Kapital = Kapital*(1 + Zinssatz/100);
Jahre++;
Form1->RichEdit1->Lines->Add(IntToStr(Jahre)+". Jahr:
"+FloatToStrF(Kapital,ffFixed,8,2));
}
while (Jahre < Laufzeit);
}
void __fastcall TForm1::BerechneZinsClick(TObject *Sender)
{
Currency Kapital=StrToCurr(EKapital->Text);
Currency Zinssatz=StrToCurr(EZinssatzVerzinsung->Text);
int Laufzeit=StrToCurr(ELaufzeit->Text);
Zinsen(Kapital, Zinssatz, Laufzeit);
}
//----- Aufgabe 5.8.3.2 ---------------------------------------------void Hypothek(Currency Betrag, Currency Zinssatz, Currency Tilgungsrate)
{
Currency Restschuld = Betrag;
Currency Annuitaet = Restschuld*(Zinssatz+Tilgungsrate)/100;
int Jahre = 0;
AnsiString s="Hypothek: "+CurrToStr(Restschuld)+": Annuitaet:
"+CurrToStr(Annuitaet);
Form1->RichEdit1->Lines->Add(s);
while (Restschuld > 0)
{
Currency Zinsen = Restschuld*Zinssatz/100;
Restschuld = Restschuld - (Annuitaet - Zinsen);
Jahre++;
s="Ende "+IntToStr(Jahre)+".Jahr: Restschuld="+CurrToStr(Restschuld)+
" Zinsen="+CurrToStr(Zinsen)+" Tilgung="+CurrToStr(Annuitaet-Zinsen);
Form1->RichEdit1->Lines->Add(s);
}
};
void __fastcall TForm1::BerechneHypoClick(TObject *Sender)
{
Currency Darlehen
= StrToCurr(EDarlehen->Text);
Currency Zinssatz
= StrToCurr(EZinssatzHypo->Text);
Currency Tilgungsrate = StrToCurr(ETilgung->Text);
Hypothek(Darlehen, Zinssatz, Tilgungsrate);
}
//----- Aufgabe 5.8.3.3 ---------------------------------------------void __fastcall TForm1::for_intClick(TObject *Sender)
{
// typedef double T;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
233
234
//
//
//
//
//
12 Lösungen
typedef float T;
for (T d=0;d<=1;d+=0.1)
RichEdit1->Lines->Add(FloatToStr(d));
return;
a) Diese for-Schleife gibt die 10 Werte 0, 0.1, 0.2, ..., 0.9, 1.0 aus:
// for (double d=0;d<=1;d+=0.1)
for (int i=0;i<=10;i++)
RichEdit1->Lines->Add(FloatToStr(i/10.0));
// b) Diese for-Schleife gibt die n Werte ug, ug+d, ug+2*d, ..., ug+(n-1)*d,
ug+n*d=og aus:
double ug=0;
double og=1;
double d=1.0/10;
int n=(og-ug)/d+0.5; // Die Addition von 0.5 rundet den Wert
for (int i=0;i<=n;i++)
RichEdit1->Lines->Add(FloatToStr(ug+i*d));
}
//----- Aufgabe 5.8.4.1-2 -------------------------------------------void __fastcall TForm1::Aufg1_2Click(TObject *Sender)
{
RichEdit1->Lines->LoadFromFile("Aufg584.rtf");
RichEdit1->Lines->Insert(0,"Wenn man die Datei Aufg584.rtf mit MS Word
anzeigt,");
RichEdit1->Lines->Insert(1,"werden die Tabellen übersichtlicher dargestellt als
");
RichEdit1->Lines->Insert(2,"mit dieser RichEdit-Komponente. ");
RichEdit1->Lines->Insert(3,"");
}
//----- Aufgabe 5.8.4.3 ---------------------------------------------// FraktalU.cpp wurde dem Projekt hinzugefügt
#include "FraktalU.h"
void __fastcall TForm1::FraktaleClick(TObject *Sender)
{
FraktalForm->ShowModal();
}
//----- Aufgabe 5.8.5, 1-4 ------------------------------------------void __fastcall TForm1::Aufg1_4Click(TObject *Sender)
{
RichEdit1->Lines->LoadFromFile("Aufg585.rtf");
RichEdit1->Lines->Insert(0,"Wenn man die Datei Aufg585.rtf mit MS Word
anzeigt,");
RichEdit1->Lines->Insert(1,"werden die Tabellen übersichtlicher dargestellt als
");
RichEdit1->Lines->Insert(2,"mit dieser RichEdit-Komponente. ");
RichEdit1->Lines->Insert(3,"");
}
//----- Aufgabe 5.8.5.5 ---------------------------------------------// Hier mit 'AnsiString' - mit 'string' geht die Lösung analog
AnsiString BigIntShiftLeft(AnsiString a)
{
return a+"0";
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
AnsiString BigIntShiftRight(AnsiString a)
{
return a.Delete(a.Length(),1);
}
bool BigIntNull(AnsiString a)
{ // setzt voraus, dass 0 nicht durch "00" o. ä. dargestellt wird
return (a=="0")||(a=="");
}
int LastDigit(AnsiString a)
{ // ergibt die letzte Ziffer eines Strings als Zahl
return a[a.Length()]-'0';
}
AnsiString ziffer[10]={"0","1","2","3","4","5","6","7","8","9"};
AnsiString BigIntMultByDigit(AnsiString a, int n)
{ // Multipliziert die String-Zahl a mit der int-Zahl n
AnsiString s;
int ue=0;
for (int i=1; i<=a.Length(); i++)
{
int ai=a[a.Length()-i+1]-'0';
int r=(ai*n + ue)%10;
ue=(ai*n + ue)/10;
s=ziffer[r]+s;
}
if (ue>0) s=ziffer[ue]+s;
return s;
}
int Max(int a, int b)
{
return (a>b)?a:b;
}
AnsiString BigIntAdd(AnsiString a, AnsiString b)
{
AnsiString s;
int m=Max(a.Length(),b.Length());
int ue=0;
for (int i=1; i<=m; i++)
{
int ai,bi;
if (i<=a.Length()) ai=a[a.Length()-i+1]-'0';
else ai=0;
if (i<=b.Length()) bi=b[b.Length()-i+1]-'0';
else bi=0;
int r=(ai+bi+ue)%10;
ue=(ai+bi+ue)/10;
s=ziffer[r]+s;
}
if (ue>0) s="1"+s;
return s;
}
AnsiString BigIntMult(AnsiString u, AnsiString v)
{
AnsiString z=""; // Null
AnsiString x=u,y=v;
// assert(z+x*y==u*v);
while (!BigIntNull(x)) // (x != 0)
{
// if (x&1) z = z + y;
z=BigIntAdd(z,BigIntMultByDigit(y,LastDigit(x)));
y=BigIntShiftLeft(y); // y = y*2;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
235
236
12 Lösungen
x=BigIntShiftRight(x); // x = x/2;
}
// assert(z+x*y==u*v && x==0)
return z;
}
AnsiString fakultaet(int n)
{
AnsiString result="1";
for (int i=2; i<=n; i++)
result=BigIntMult(result,IntToStr(i));
return result;
}
double double_fakultaet(int n)
{
double result=1;
for (int i=2; i<=n; i++)
result=result*i;
return result;
}
void __fastcall TForm1::StringzahlMultClick(TObject *Sender)
{
for (int i=1; i<=45; i++)
{
RichEdit1->Lines->Add(IntToStr(i)+"!="+fakultaet(i));
RichEdit1->Lines->Add(IntToStr(i)+"!="+double_fakultaet(i));
}
}
12.4.8 Aufgaben 5.8.4
1. a) n <= 0 und n gerade.
b) Fall I:
Fall II:
i <= 0. Der Schleifenkörper wird nie ausgeführt, also erhält man auch keine Endlosschleife.
i > 0. Da für i > 0 stets i/2 < i gilt, wird i bei jeder Ausführung des Schleifenkörpers verkleinert.
Deshalb erhält man für keinen Wert von i eine Endlosschleife. Dagegen würde die Schleifenbedingung (i >= 0)
anstelle von (i > 0) zu einer Endlosschleife führen, da 0/2 = 0.
c) Fall I:
Fall II:
i >= n: Der Schleifenkörper wird nie ausgeführt.
i < n: Für jeden Wert von i ist i + 1 > i.
Deshalb wird i bei jeder Ausführung vergrößert und erhält schließlich den Wert n. Auch diese Schleife wird also
nie zur Endlosschleife.
d) Bei einer Prüfung von Gleitkommawerten auf Gleichheit können Ungenauigkeiten in der Zahldarstellung dazu
führen, dass eine erwartete Gleichheit doch nicht eintritt. Deshalb sollten Schleifen nie durch eine Prüfung von
Gleitkommawerten auf Gleichheit kontrolliert werden.
Eine Kontrolle der Schleife durch die Bedingung d >= 10 wäre allerdings relativ unproblematisch. Jedoch bietet
auch diese Bedingung keine Sicherheit dafür, dass der Schleifenkörper so oft ausgeführt wird, wie man das gerne
hätte. Es kann durchaus vorkommen, dass er einmal zu oft oder einmal zu wenig ausgeführt wird.
e) Wird für i > 0 immer zur Endlosschleife, da die Kontrollvariable in der Schleife nicht verändert wird.
f) siehe b).
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
237
Wie die Lösungen der Aufgaben b) und c) nahe legen, ist die endliche Wiederholung einer Schleife
z.B. dann gesichert, wenn sie durch eine ganzzahlige Variable i kontrolliert wird, wobei entweder
1. die wiederholte Ausführung des Schleifenkörpers stets i >= n (bzw. i > n) voraussetzt und i bei jeder Wiederholung
verkleinert wird oder
2. die wiederholte Ausführung des Schleifenkörpers stets i <= n (bzw. i < n) voraussetzt und i bei jeder Wiederholung
vergrößert wird.
Insbesondere sollte eine Schleife nie durch eine Prüfung von zwei Gleitkommawerten auf Gleichheit kontrolliert
werden, da Ungenauigkeiten bei der Darstellung von Gleitkommawerten leicht zu Endlosschleifen führen.
2. 1. Mit den Regeln von de Morgan ergibt sich aus
!((n > 0) && (c != 'q'))
die Bedingung (n <= 0) || (c == 'q').
2. Die Bedingung (n <= 0) aus der Lösung von 1. lässt sich zu (n = 0) verschärfen.
12.4.9 Aufgabe 5.8.4.3
// Datei: d:\Loesungen\kap-5\5.8\FraktalU.cpp
#include "FraktalU.h"
TFraktalForm *FraktalForm;
__fastcall TFraktalForm::TFraktalForm(TComponent* Owner)
: TForm(Owner)
{
}
// Die folgenden beiden Variablen werden in FormCreate
// bei mehr als 256 Bildschirmfarben
bool moreThan256Colors=false; // auf true gesetzt
bool usePalette=true;
// auf false gesetzt
// a)
double
double
x0= -2;
y0= 1.25;
// links oben
double
double
x1 = 0.5; // rechts unten
y1 = -1.25;
// e)
enum {Mandelbrot, Julia} Art_des_Fraktals=Mandelbrot;
long double jx= -0.745, jy= 0.1125; // für die Julia-Mengen
// c)
bool aborted=false; // Damit die Schleife unterbrochen werden kann
// b)
TColor IndexColor(int i)
{
// return PALETTEINDEX (i%16);
if (moreThan256Colors)
{
i=i%256; // maximal 255 Farben
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
238
12 Lösungen
int r30=i%30;
// i->[0..29]
int t=255-5*r30; // [0..29]->[255..145]
if (i<30)
return RGB(255-8*i,255,255); // weiss..türkis
else if (i<60) return RGB(0,255-8*r30,255); // türkis..blau
else if (i<90) return RGB(0,0,t); // blau .. dunkelblau
else if (i<120) return RGB(0,t,0); // grün .. dunkelgrün
else if (i<150) return RGB(t,0,0); // rot .. dunkelrot
else if (i<180) return RGB(t,t,0);
else if (i<210) return RGB(t,0,t);
else if (i<240) return RGB(0,t,t);
else
return RGB(t,t,t); // Graustufen
}
else if (usePalette)
return PALETTEINDEX (i);
else
{
static TColor c[17]={clWhite, clLtGray, clGray, clDkGray, clGreen,
clLime, clNavy, clOlive, clAqua, clBlue, clPurple, clRed,
clFuchsia, clMaroon, clSilver, clTeal, clYellow};
return c[i%17];
}
}
// a)
#include "\Loesungen\CppUtils\GraphUtils.cpp"
void ZeichneFraktal(TImage* Image)
{
const int max_it = 250; // 50,100,200
aborted = false; // für b)
for (int px=0; (px <= Image->Width-1) && (!aborted); px++)
{
for (int py=0;(py<=Image->Height-1) && (!aborted); py++)
{
double x = x_Welt(px,x0,x1,Image->Width);
double y = y_Welt(py,y1,y0,Image->Height);
long double x_it_alt = x;
long double y_it_alt = y;
long double dist;
int i = 0;
do {
i++;
/*
Eigentlich müssen diese Berechnungen durchgeführt werden:
x_it = x_it_alt*x_it_alt - y_it_alt*y_it_alt + x;
y_it = 2*x_it_alt*y_it_alt + y;
dist = x_it*x_it+y_it*y_it;
Die folgenden Operationen sparen aber einige Multiplikationen:
*/
long double sqr_x = x_it_alt*x_it_alt;
long double sqr_y = y_it_alt*y_it_alt;
long double pr_xy = x_it_alt*y_it_alt;
long double x_it,y_it;
/* e) */
if (Art_des_Fraktals==Mandelbrot)
{
x_it = sqr_x - sqr_y + x;
y_it = pr_xy + pr_xy + y;
}
else if (Art_des_Fraktals==Julia)
{
x_it = sqr_x - sqr_y + jx;
y_it = pr_xy + pr_xy + jy;
}
dist = sqr_x + sqr_y;
x_it_alt = x_it;
y_it_alt = y_it;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
//
239
while ((i <= max_it) && (dist <= 4));
TColor color=IndexColor(i);
TColor color=IndexColor(py);// Zum Test der Farbverläufe
if (i > max_it) color = clBlack;
Image->Canvas->Pixels[px][py] = color;
}
// c)
Damit ein eventuelles Anklicken des Buttons "Abbruch"
// erkannt wird und die Pixel gezeichnet werden.
if (!usePalette && px%5==0) Application->ProcessMessages();
}
int i=0;
i++;
return;
}
void __fastcall TFraktalForm::ZeichneClick(TObject *Sender)
{
FraktalForm->Caption="Fraktal: x0="+FloatToStr(x0)+" y0="+FloatToStr(y0)+
" x1="+FloatToStr(x1)+" y1="+FloatToStr(y1);
// Form1->Width = 400;
// Form1->Height = 400;
// Form1->Image1->Align = alClient;
HPALETTE hpal;
if (usePalette) // nur bei 256 Farben
{
HPALETTE CreateMyPalette(); // Prototyp, Definition unten
HPALETTE hpal=CreateMyPalette();
SelectPalette(Image1->Canvas->Handle,hpal,true);
RealizePalette(Image1->Canvas->Handle);
}
ZeichneFraktal(Image1);
if (usePalette) DeleteObject(hpal);
FraktalForm->Image1->Picture->SaveToFile("c:\\Fraktal.bmp");
}
// c)
void __fastcall TFraktalForm::AbbruchClick(TObject *Sender)
{
// Da das Zeichnen eines Fraktals relativ lange dauern kann,
// kann man es durch ein Anklicken dieses Buttons abbrechen.
aborted=true;
}
// d)
double x_start, y_start; // Position bei MouseDown
void __fastcall TFraktalForm::Image1MouseDown(TObject *Sender, TMouseButton
Button,
TShiftState Shift, int X, int Y)
{
x_start = x_Welt(X,x0,x1,Image1->Width);
y_start = y_Welt(Y,y1,y0,Image1->Height);
}
void __fastcall TFraktalForm::Image1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
x1 = x_Welt(X,x0,x1,Image1->Width);
y1 = y_Welt(Y,y1,y0,Image1->Height);
x0 = x_start;
y0 = y_start;
}
// f)
struct TMandelbrotParam {
char* Name;
// AnsiString Name; // geht nicht
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
240
12 Lösungen
double x0; double y0;
double x1; double y1;
};
const int nMBAW=13;
TMandelbrotParam MBPar[nMBAW]=
{
//
{AnsiString("Apfelmännchen"),
-2,
1.25,
0.5,
{"Apfelmännchen",
-2,
1.25,
0.5, -1.25},
{"Seepferdchen",
-0.88, 0.18, -0.70, 0.0},
{"Seepferdchen",
-0.73206,0.1570,-0.73196,0.1569},
{"Apfelmännchen unten", -0.17, -1.02, -0.15, -1.04},
{"Apfelmännchen links",-1.80,
0.03, -1.74, -0.03},
{"Spirale",
-0.750, 0.105, -0.740, 0.095}
};
-1.25},
struct TJuliaParam {
char* Name;
// AnsiString Name; // geht nicht
double x0; double y0;
double x1; double y1;
double jx; double jy;
};
const int nJAW=9;
TJuliaParam JPar[nJAW]=
{ // Bezeichnungen teilweise nach Peitgen u. a. (1986)
{ "Peitgen Fig. 15", -1.6, -1.2, 1.6, 1.2, -0.74543, 0.11301},
{ "Peitgen Map 18",
-1, -1.2,
1, 1.2,
0.32, 0.043},
{ "Peitgen Map 20",
-2, -1.5,
2, 1.5, -0.12375, 0.56508},
{ "Siegel Disk", -2, -1.5,
2, 1.5, -0.39054, -0.58679},
{ "Fatou Staub", -2, -1.5,
2, 1.5, 0.11031, -0.67037},
{ "Zweig",
-2, -1.5,
2, 1.5, 0, 1},
{ "Zweig mit Perlen",
-2, -1.5,
2, 1.5, -0.15652, 1.03225},
{ "Julia 1",
-2, -1.5,
2, 1.5, -0.72, 0.53},
{ "Julia 2",
-2, -1.5,
2, 1.5, -1.776, 0.4},
};
void __fastcall TFraktalForm::ListBox1Click(TObject *Sender)
{
Art_des_Fraktals=Mandelbrot;
int i=ListBox1->ItemIndex;
if (i<0) i=0;
x0 = MBPar[i].x0;
y0 = MBPar[i].y0; // links oben
x1 = MBPar[i].x1;
y1 = MBPar[i].y1; // rechts unten
}
void __fastcall TFraktalForm::ListBox2Click(TObject *Sender)
{
Art_des_Fraktals=Julia;
int i=ListBox2->ItemIndex;
if (i<0) i=0;
x0 = JPar[i].x0;
y0 = JPar[i].y0;
x1 = JPar[i].x1;
y1 = JPar[i].y1;
jx = JPar[i].jx;
jy = JPar[i].jy;
}
void __fastcall TFraktalForm::FormCreate(TObject *Sender)
{
Memo1->Lines->Clear();
Memo1->Lines->Add("In der Zeichnung kann ein "
"rechteckiger Bereich mit der Maus "
"markiert werden. Die linke obere "
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
241
"Ecke des Rechtecks erhält man beim "
"Drücken und die rechte untere Ecke "
"beim Loslassen der Maustaste. Mit "
"dem Button 'Zeichne' wird dieser "
"Ausschnitt gezeichnet (Zoom).");
int n=GetDeviceCaps(Image1->Canvas->Handle,NUMCOLORS);
moreThan256Colors = (n==-1); // -1, falls Farbtiefe größer als 8 Bits (256
Farben)
if (moreThan256Colors) usePalette=false;
// f)
for (int i=0; i< nMBAW; i++)
ListBox1->Items->Add(MBPar[i].Name);
for (int i=0; i< nJAW; i++)
ListBox2->Items->Add(JPar[i].Name);
}
HPALETTE CreateMyPalette()
{ // Diese Funktion ist nur dann notwendig, wenn für den Bildschirm nur 256
// Farben eingestellt sind. Sie erzeugt eine eigene Farbpalette, damit
// auch mit dieser Einstellung mehr als die ca. 20 Systemfarben verfügbar
// sind.
const int nColMyPalette=6*6*6; // Anzahl der Farben in der eigenene Palette:
// Weniger als 256-20, damit bei einer Bildschirmeinstellung
// mit 256 Farben die 20 Systemfarben nicht beeinflußt werden.
PLOGPALETTE pPal = (PLOGPALETTE)LocalAlloc(LPTR, sizeof(LOGPALETTE) +
nColMyPalette * sizeof(PALETTEENTRY));
//
pPal = (COLORREF *)LocalAlloc(LPTR, sizeof(COLORREF) *(N+1));
if (!pPal) return(NULL);
pPal->palVersion = 0x300;
pPal->palNumEntries = nColMyPalette;
int i=0;
// Da schwarz eine spezielle Bedeutung hat,
// hier nicht mit schwarz=(0,0,0) anfangen
for (int r=1; r<=6; r++)
// wie in nColMyPalette
for (int g=1; g<=6; g++)
// wie in nColMyPalette
for (int b=1; b<=6; b++) // wie in nColMyPalette
if (i<nColMyPalette)
{
pPal->palPalEntry[i].peRed = r*nColMyPalette/6;
pPal->palPalEntry[i].peGreen = g*nColMyPalette/6;
pPal->palPalEntry[i].peBlue = b*nColMyPalette/6;
pPal->palPalEntry[i].peFlags = 0;
i++;
}
HPALETTE hLogPal = CreatePalette(pPal);
LocalFree(pPal);
return(hLogPal);
}
12.4.10 Aufgaben 5.8.5
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
242
12 Lösungen
1. a) n = 4
f
1
f=1
for (int i=2;i<=n;i++)
i=2
i
i<=n
2
i<=n
f=f*i
i++
1*2 = 2
i<=n
f=f*i
i++
3*3 = 6
i<=n
f=f*i
i++
6*4 = 24
true
3
true
4
true
5
i<=n
false
24
b) u = 3, v = 5
x=u
y=v
z=0
while (x!=0)
x!=0
if (x&1) z=z+y
y=y*2
x=x/2
x!=0
if (x&1) z=z+y
y=y*2
x=x/2
x!=0
x
3
y
z
x!=0
5
0
true
0+5=5
2*5=10
3/2=1
true
5+10=15
2*10=20
1/2=0
false
0
20
15
c) u = 0, v = 5
x=u
y=v
z=0
while (x!=0)
x
0
y
z
x!=0
5
0
0
5
0
d) u = 3, v = 2
p=1
n=v
x=u
while (n>0)
n>0
if (n&1) p=p*x
n=n/2
x=x*x
n>0
if (n&1) p=p*x
n=n/2
x=x*x
n>0
p
1
n
x
n > 0
2
3
true
1
3*3=9
true
1*9=9
0
9*9=81
false
9
0
81
2. In Aufgabe 5.7.3.5 wurde nachgewiesen, dass die Beziehung
p*xn = uv
eine Invariante für diese Anweisungen ist. Diese Beziehung gilt wegen
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
243
p = 1;
n = v;
x = u;
auch vor der ersten Ausführung der while-Schleife. Da nach dem Verlassen der Schleife n=0 gilt, falls die Funktion
mit n >= 0 aufgerufen wird, ist das Ergebnis der gesamten Anweisungsfolge
p = uv.
Nach Aufgabe 5.8.4.1 b) wird diese Schleife auch nicht zu einer Endlosschleife, falls beim Aufruf der Schleife n >=
0 gilt.
Deshalb wird diese Schleife immer verlassen, und anschließend gilt tatsächlich
p = uv.
3. int ggt(int x, int y)
{
// x=x0, y=y0, ggT(x,y) = ggT(x0,y0)
if (x<0) x=-x; // Damit die Funktion auch mit negativen
if (y<0) y=-y; // Argumenten das richtige Ergebnis hat.
// ggT(x,y) = ggT(x0,y0)
while (y != 0)
// Ablaufprot. für den Schleifenkörper
{//
x
y
r
// ggT(x,y)=ggT(x0,y0)
x0
y0
int r = x%y; //
x0%y0
x = y;
//
y0
y = r;
//
x0%y0
// ggT(x,y) = ggT(y0,x0%y0) = ggT(x0,y0)
}
// ggT(x,y) = ggT(x0,y0) und y=0
return x; // ggT(x,y)==x;
}
4. a) Am Anfang des Schleifenkörpers gilt nach den ersten Durchläufen der while-Schleife jeweils
i = 1, s = 0,
i = 2, s = p[n]
i = 3, s = p[n]*x + p[n–1]
i = 4, s = p[n]*x2 + p[n–1]*x + p[n–2]
Diese Beziehungen lassen die Schleifeninvariante
s = p[n]*xi–2 + p[n–1]*xi–3 + ... + p[n–i+2]*x0
erwarten, die sich mit dem folgenden symbolischen Ablaufprotokoll auch nachweisen lässt. Dabei wurde io anstelle
von i0 geschrieben, da ich – oder mein Textverarbeitungssystem – nicht doppelt tief indizieren kann:
i
io
s
s0 = p[n]*xio–2 + p[n–1]*xio–3 + ... + p[n – io + 2]
s = s*x + p[n–i+1];
i++ ;
s0*x+p[n–io+1]
= p[n]*xio–1 + p[n–1]*xio–2 + ... + p[n–io+2]*x + p[n–io+1]
io+1
p[n]*xio–2 + p[n–1]*xio–3 + ... + p[n–io+3]*x + p[n–io+2]
Damit gilt nach dem Verlassen der while-Schleife wegen i=n+2 tatsächlich die Behauptung.
Dass die Schleife auch verlassen wird, ergibt sich aus der Konstruktion der Schleife: Ausgehend
von i = 1 wird i hochgezählt, bis i einen Wert größer als n hat.
Diese Funktion entspricht den im C++Builder (aber nicht im C++-Standard) vordefinierten
Funktionen (ohne die Ableitung)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
244
12 Lösungen
double poly(double x, int degree, double coeffs[]); bzw.
long double polyl(long double x, int degree, long double coeffs[]);
b) const int n = 10; // Der Grad des Polynoms.
double horner(double x, double* p, double& Ableitung)
{
double s = 0;
int i = 1;
Ableitung = 0;
while (i <= n+1)
{
Ableitung = Ableitung*x + s;
s = s*x + p[n-i+1];
i++;
}
return s;
}
12.4.11 Aufgaben 5.10.8
// Datei: d:\Loesungen\kap-5\5.10\ExceptU.cpp
#include "ExceptU.h"
//----- Aufgabe 1 -----------------------------------------------/*
a) in keiner der Funktionen f1, f2 usw. eine Exception ausgelöst wird:
Lösung: f1, f2, f4
b) in f1 eine Exception ausgelöst wird
Lösung: f1, f3, f4
c) in f2 eine Exception ausgelöst wird
Lösung: f1, f2, f3, f4
d) in f1 und f3 eine Exception ausgelöst wird
Lösung: f1, f3, f5
e) in f1 und f4 eine Exception ausgelöst wird
Lösung: f1, f3, f4, f5
*/
void f1(char c)
{
Form1->Memo1->Lines->Add("f1");
if (c=='b') throw 1;
if (c=='d') throw 1;
if (c=='e') throw 1;
};
void f2(char c)
{
Form1->Memo1->Lines->Add("f2");
if (c=='c') throw 2;
};
void f3(char c)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
{
Form1->Memo1->Lines->Add("f3");
if (c=='d') throw 3;
};
void f4(char c)
{
Form1->Memo1->Lines->Add("f4");
if (c=='e') throw 4;
};
void f5(char c)
{
Form1->Memo1->Lines->Add("f5");
};
void f_(char c)
{
Form1->Memo1->Lines->Add(c);
try { try {
f1(c);
f2(c);
}
catch(...)
{
f3(c);
}
f4(c);
}
catch (...)
{
f5(c);
}
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
f_('a');
f_('b');
f_('c');
f_('d');
f_('e');
}
//----- Aufgabe 2 -----------------------------------------------#include <exception>
#include <stdexcept> // für logic_error
using namespace std;
void Aufg2(int i)
{
if (i==0) throw i+1;
else if (i==1) throw 'A';
else if (i==2) throw 1.0*i;
else if (i==3) throw "Hallo";
else if (i==3) throw exception();
else throw logic_error("Vorbedingung nicht erfüllt");
}
void test_Aufg2(int i)
{
try { Aufg2(i);
}
catch(int i)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
245
246
Form1->Memo1->Lines->Add("int Exception i="+IntToStr(i));
}
catch(char i)
{
Form1->Memo1->Lines->Add("char Exception i="+IntToStr(i));
}
catch(double i)
{
Form1->Memo1->Lines->Add("double Exception i="+FloatToStr(i));
}
catch(char* i)
{
Form1->Memo1->Lines->Add(AnsiString("char* Exception i=")+i);
}
catch(logic_error& i)
{
Form1->Memo1->Lines->Add(AnsiString("Exception i=")+i.what());
}
catch(exception& i)
{
Form1->Memo1->Lines->Add(AnsiString("Exception i=")+i.what());
}
};
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
test_Aufg2(0);
test_Aufg2(1);
test_Aufg2(2);
test_Aufg2(3);
test_Aufg2(4);
test_Aufg2(5);
}
//----- Aufgabe 3 -----------------------------------------------#include <stdexcept>
void f(int i)
{
if (i==0) throw exception();
else if (i==1) throw logic_error("logic error");
else if (i==2) throw range_error("range error");
else if (i==3) throw out_of_range("out of range error");
else throw exception();
}
// a)
void g1(int i)
{
try { f(i); }
catch(logic_error& e)
{ Form1->Memo1->Lines->Add("logisch"); }
catch(out_of_range& e)
{ Form1->Memo1->Lines->Add("range"); }
catch(exception& e)
{ Form1->Memo1->Lines->Add("exception"); }
};
void test_Aufg3a()
{
g1(0); // exception
g1(1); // logisch
g1(2); // exception
g1(3); // logisch
}
// b)
void g2(int i)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.4 Lösungen Kapitel 5
247
{
try { f(i); }
catch(logic_error& e)
{ Form1->Memo1->Lines->Add(e.what()); }
catch(out_of_range& e)
{ Form1->Memo1->Lines->Add(e.what()); }
catch(exception& e)
{ Form1->Memo1->Lines->Add(e.what()); }
};
void test_Aufg3b()
{
g2(0); // exception::what(),
"no named exception thrown"
g2(1); // logic_error::what(), "logic error"
g2(2); // range_error::what(), "range error"
g2(3); // out_of_range::what(), "out of range error"
// Dieses Ergebnis erhält man einfacher wie in d)
}
// c)
void g3(int i)
{
try { f(i); }
catch(exception e)
{ Form1->Memo1->Lines->Add(e.what()); }
};
void test_Aufg3c()
{
g3(0); // exception::what(), "no named exception thrown"
g3(1); // exception::what(), "no named exception thrown"
g3(2); // exception::what(), "no named exception thrown"
g3(3); // exception::what(), "no named exception thrown"
// Da der Datentyp im Exception-Handler keine Referenz
// ist, wird immer die Funktion 'what' von 'exception'
// aufgerufen.
}
// d)
void g4(int i)
{
try { f(i); }
catch(exception& e)
{ Form1->Memo1->Lines->Add(e.what()); }
catch(...)
{ Form1->Memo1->Lines->Add("irgendeine Exception"); }
};
void test_Aufg3d()
{
g4(0); // exception::what(),
g4(1); // logic_error::what(),
g4(2); // range_error::what(),
g4(3); // out_of_range::what(),
"no named exception thrown"
"logic error"
"range error"
"out of range error"
}
void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
test_Aufg3a();
test_Aufg3b();
test_Aufg3c();
test_Aufg3d();
}
//----- Aufgabe 4 -----------------------------------------------#include <sstream>
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
248
12 Lösungen
unsigned int except_flags=ios::failbit;
double StrToFloat(string s)
{
istringstream is(s);
is.exceptions(except_flags);
double d;
is>>d;
return d;
}
int StrToInt(string s)
{
istringstream is(s);
is.exceptions(ios::failbit|ios::badbit);
int i;
is>>i;
return i;
}
void test_StrToInt1(string str)
{
string s;
int i=-17;
try {
// i=StrToInt(str);
i=StrToFloat(str);
}
catch(exception& e)
{ s=e.what(); }
catch(...)
{ s="StrToError"; }
Form1->Memo1->Lines->Add(s.c_str()+AnsiString(": ")+IntToStr(i));
}
void test_StrToFloat2(unsigned flag)
{
Form1->Memo1->Lines->Add(" ##"+IntToStr(flag));
except_flags=flag;
test_StrToInt1("123");
test_StrToInt1(" 123 ");
test_StrToInt1("1 23");
test_StrToInt1("12A3");
test_StrToInt1("123A");
test_StrToInt1("");
}
void __fastcall TForm1::Aufg4Click(TObject *Sender)
{
// Mit IntToStr:
test_StrToFloat2(0);
// 123
123
1
12
test_StrToFloat2(ios::failbit);
// 123
123
1
12
test_StrToFloat2(ios::eofbit);
// 123
123
1
12
test_StrToFloat2(ios::badbit);
// 123
123
1
12
123
failbit
123
failbit
123
failbit
123
failbit
123
0
123
failbit
// Mit FloatToStr:
test_StrToFloat2(0);
// 123
123
1
12
test_StrToFloat2(ios::failbit);
// 123
123
1
12
test_StrToFloat2(ios::eofbit);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
// eofbit 123
1
12
test_StrToFloat2(ios::badbit);
// 123
123
1
12
249
123
eofbit
123
0
}
//----- Aufgabe 5 -----------------------------------------------class my_exception:public exception {
string str;
public:
my_exception(const string& msg):str(msg) {
const char* what() const
{
return str.c_str();
}
};
};
void f()
{
throw my_exception("ich war's");
}
void Aufgabe5()
{
try { f(); }
catch (exception& e)
{ // fängt alle von exception abgeleiteten Exceptions ab
ShowMessage(e.what());
}
}
void __fastcall TForm1::Aufg5Click(TObject *Sender)
{
Aufgabe5();
}
//----- Aufgabe 6 -----------------------------------------------class so_nicht:public exception {
char* str;
public:
so_nicht(char* msg):str(msg) { };
const char* what() const
{
return str;
}
};
void f1()
{
char s[]="ich war's"; // existiert nach dem Verlassen von f1 nicht mehr
throw so_nicht(s);
}
void Aufgabe6()
{
try { f1(); }
catch (exception& e)
{ // fängt alle von exception abgeleiteten Exceptions ab
ShowMessage(e.what());
}
}
void __fastcall TForm1::Aufg6Click(TObject *Sender)
{
Aufgabe6();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
250
12 Lösungen
//----- Aufgabe 7 -----------------------------------------------// Siehe auch Betrand Meyer, Object Oriented Software Construction,
//
Abschnitt 12.2
class ENegative {};
double Sqrt(double d)
{
try {
if (d<0) throw ENegative();
return sqrt(d);
}
catch(...)
{
ShowMessage("negativ");
return 0;
}
}
void __fastcall TForm1::Aufg7Click(TObject *Sender)
{
double d=Sqrt(1)+Sqrt(-1);
Form1->Memo1->Lines->Add(FloatToStr(d));
}
//----- Aufgabe 8 -----------------------------------------------struct CDatum {
int Tag;
int Monat;
int Jahr;
};
CDatum StrToCDatum(AnsiString s)
{
// Das Datum kann man wie in Aufgabe 4.1.1 konvertieren oder so:
// Ende Datum
TDateTime d;
d = StrToDate(s);
unsigned short year, month, day;
d.DecodeDate(&year, &month, &day);
CDatum dat={day,month,year};
return dat;
}
struct CKontobewegung {
int KontoNr;
AnsiString NameInhaber;
CDatum Datum;
char BewArt;
Currency Betrag;
};
#include <vector>
vector <CKontobewegung> v;
CKontobewegung getData1(TForm1* Form)
{ // TForm1 soll ein Formular sein, das die Edit-Felder EKontoNr->Text usw.
enthält
// Exceptions nicht abfangen, nur weitergeben
CKontobewegung k;
k.KontoNr=StrToInt(Form->EKontoNr->Text);
k.NameInhaber=Form->EName->Text;
k.Datum=StrToCDatum(Form->EDatum->Text.c_str());
Form->EBewart->Text.Trim();
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.4 Lösungen Kapitel 5
if (Form->EBewart->Text.Length() >= 1)
k.BewArt =Form1->EBewart->Text[1]; // sehr einfach
k.Betrag = StrToCurr(Form->EBetrag->Text);
return k;
}
void saveData1(TForm1* Form)
{
try {
CKontobewegung k;
k=getData1(Form);
v.push_back(k);
}
catch(...)
{ ShowMessage("Fehler"); }
}
CKontobewegung getData2(TForm1* Form)
{ // Alle Exceptions in einem einzigen try abfangen:
// schlecht, da undefinierte Werte zurückgegeben werden können
CKontobewegung k;
try {
k.KontoNr=StrToInt(Form->EKontoNr->Text);
k.NameInhaber=Form->EName->Text;
k.Datum=StrToCDatum(Form->EDatum->Text.c_str());
Form->EBewart->Text.Trim();
if (Form->EBewart->Text.Length() >= 1)
k.BewArt =Form1->EBewart->Text[1]; // sehr einfach
k.Betrag = StrToCurr(Form->EBetrag->Text);
}
catch(...)
{ ShowMessage("Fehler"); }
return k;
}
void saveData2(TForm1* Form)
{
try {
CKontobewegung k;
k=getData2(Form1);
v.push_back(k);
}
catch(...)
{
ShowMessage("Fehler");
}
}
CKontobewegung getData3(TForm1* Form)
{ // Jede Exceptions in einem eigenen try abfangen
CKontobewegung k;
Form->EBewart->Text.Trim();
if (Form->EBewart->Text.Length() >= 1)
k.BewArt =Form1->EBewart->Text[1]; // sehr einfach
k.Betrag = StrToCurr(Form->EBetrag->Text);
try { k.KontoNr =StrToInt(Form->EKontoNr->Text); }
catch(...)
{
Form1->EKontoNr->SetFocus();
throw; //
}
k.NameInhaber=Form->EName->Text; // müsste immer gehen
try { k.Datum=StrToCDatum(Form->EDatum->Text.c_str()); }
catch(...)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
251
252
{
Form1->EDatum->SetFocus();
throw;
}
return k;
} // kann mit saveData1 verwendet werden
void saveData3(TForm1* Form)
{
try {
CKontobewegung k;
k=getData3(Form);
v.push_back(k);
}
catch(...)
{
ShowMessage("Fehler");
}
}
void __fastcall TForm1::Aufg8Click(TObject *Sender)
{
saveData1(Form1);
}
//----- Aufgabe 9 -----------------------------------------------void h1()
{ // kann zu einem Speicherleck führen
vector<int*> v;
int* p=new int(17);
v.push_back(p);
f();
for (vector<int*>::iterator i=v.begin(); i!=v.end(); i++)
delete i;
}
class C {
vector<int*> v;
public:
void push_back(int* p)
{
v.push_back(p);
}
~C()
{
for (vector<int*>::iterator i=v.begin(); i!=v.end(); i++)
delete i;
}
};
void h2()
{
C v;
v.push_back(new int(17));
f();
}
void __fastcall TForm1::Aufg9Click(TObject *Sender)
{
h1();
h2();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.5 Lösungen Kapitel 6
253
12.5 Lösungen Kapitel 6
12.5.1 Aufgabe 6.3.5 und 6.3.7
// Datei: d:\Loesungen\kap-6\6.3\FktParmU.cpp
#include "FktParmU.h"
//----- Aufgaben 6.3.5 ----------------------------------------------//----- Aufgabe 1 -----------------------------------------------int a=0;
void Init1()
{
a=1; // globale Variable
}
void call_Init1()
{
Init1;
// Ohne den Aufrufoperator ist das kein Funktionsaufruf. Richtig wäre:
// Init1();
}
void __fastcall TForm1::Aufg6351Click(TObject *Sender)
{
call_Init1();
}
//----- Aufgabe 2 -----------------------------------------------char* Nachkommastellen(char *s)
{
char w[200];
strcpy(w, s);
return strstr(w, ",")+1;
// Diese Funktion gibt einen Zeiger auf eine Variable auf dem Stack zurück,
// die nach dem Aufruf nicht mehr existiert. Mit
return strstr(s, ",")+1;
// erhält man das erwartete Ergebnis.
}
void __fastcall TForm1::Aufg6352Click(TObject *Sender)
{
Memo1->Lines->Add(Nachkommastellen("0,123"));
Memo1->Lines->Add(Nachkommastellen("1,23"));
Memo1->Lines->Add(Nachkommastellen("12,3"));
}
//----- Aufgabe 3 -----------------------------------------------void __fastcall TForm1::Aufg6353Click(TObject *Sender)
{
Memo1->Lines->Text=Memo1->Lines->Text+
"\nBei der Parameterübergabe im Stil von C müssen in der "
"Funktionsdefinition alle Verwendungen des Parameters dereferenziert werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
"
254
12 Lösungen
"In allen Aufrufen der Funktion muss der Parameter durch seine Adresse "
"ersetzt werden. Bei Referenzparametern muss dagegen nur der Datentyp in "
"der Funktionsdefinition in einen Referenzparameter geändert werden. \n";
}
//----- Aufgabe 4 -----------------------------------------------void Aendere_Zeiger_ptr(int** p)
{
(*p)=0;
}
void Aendere_Zeiger_ref(int*& p)
{
p=0;
}
void __fastcall TForm1::Aufg6354Click(TObject *Sender)
{
int i=17,j=18;
int* p=&i;
Aendere_Zeiger_ptr(&p);
int* q=&j;
Aendere_Zeiger_ref(q);
}
//----- Aufgabe 5 -----------------------------------------------void zero(void* p, int n)
{
for (int i=0; i<n; i++) *(((char*)p)+i)=0;
// Anstelle dieser Funktion kann man auch die Bibliotheksfunktion
// memset verwenden
}
void __fastcall TForm1::Aufg6355Click(TObject *Sender)
{
// Überprüfe die Ergebnisse im Debugger, z. B. in einem
// Watch-Fenster als Speicherauszug, wobei mehr Bytes angezeigt
// werden als die jeweilige Variable benötigt:
//
i,10m
//
d,10m
//
c,10m
int i=17;
zero(&i,sizeof(i));
char c='A';
zero(&c,sizeof(c));
double d=3.14;
zero(&d,sizeof(d));
}
//----- Aufgabe 6 -----------------------------------------------int f6(int& x)
{
x++;
return x;
}
int g6(int& x)
{
x=2*x;
return x;
}
void __fastcall TForm1::Aufg6356Click(TObject *Sender)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{
{ // a) wenn x den Datentyp int hat.
int x;
x = 0;
int y1=f6(x)+g6(x);
x = 0;
int y2=g6(x)+f6(x);
Memo1->Lines->Add("y1="+IntToStr(y1)+" y2="+IntToStr(y2));
// Da hier das Argument denselben Datentyp wie der Parameter hat, werden
// die Anweisungen in den Funktionen mit dem Argument durchgeführt, was
// einen Seiteneffekt zur Folge hat.
}
{ // b) wenn x den Datentyp double hat.
double x;
x = 0;
int y1=f6(x)+g6(x);
x = 0;
int y2=g6(x)+f6(x);
Memo1->Lines->Add("y1="+IntToStr(y1)+" y2="+IntToStr(y2));
// In b) und c) hat das Argument einen anderen Datentyp als der
// Parameter. Deswegen werden die Anweisungen in den Funktionen nicht
// mit dem Argument durchgeführt, sondern mit einer temporären
// Variablen. Deshalb haben diese Funktionen hier keinen Seiteneffekt.
}
{ // c) wenn x den Datentyp char hat.
char x;
x = 0;
int y1=f6(x)+g6(x);
x = 0;
int y2=g6(x)+f6(x);
Memo1->Lines->Add("y1="+IntToStr(y1)+" y2="+IntToStr(y2));
// siehe b)
}
}
//----- Aufgaben 6.3.8 ----------------------------------------------//----- Aufgabe 1 -----------------------------------------------struct C2DPunkt {
int x, y;
};
C2DPunkt InitPunkt(int x=0, int y=0)
{
C2DPunkt p={x,y};
return p;
}
void __fastcall TForm1::AInitPunktClick(TObject *Sender)
{
C2DPunkt p1, p2, p3;
p1=InitPunkt();
p2=InitPunkt(1);
p3=InitPunkt(2,3);
}
//----- Aufgabe 2 -----------------------------------------------void f1(void)
{};
int f2(int,int)
{return 0;};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
255
256
12 Lösungen
double f3(double)
{return 0;};
char* f4(char*)
{return "";};
// a1)
typedef
typedef
typedef
typedef
F1
F2
F3
F4
void (*F1)();
int (*F2)(int,int);
double (*F3)(double);
char* (*F4)(char*);
A1[10];
A2[10];
A3[10];
A4[10];
// a2)
void
int
double
char*
(*a1[10])(void);
(*a2[10])(int,int);
(*a3[10])(double);
(*a4[10])(char*);
// b)
void test()
{
a1[0]=f1;
a1[0]();
a2[0]=f2;
a2[0](1,1);
a3[0]=f3;
a3[0](1);
a4[0]=f4;
a4[0]("");
A1[0]=f1;
A1[0]();
A2[0]=f2;
A2[0](1,1);
A3[0]=f3;
A3[0](1);
A4[0]=f4;
A4[0]("");
}
// c)
#include <typeinfo>
void __fastcall TForm1::FktTypenClick(TObject *Sender)
{
test();
Form1->Memo1->Lines->Add(typeid(F1).name());
Form1->Memo1->Lines->Add(typeid(F2).name());
Form1->Memo1->Lines->Add(typeid(F3).name());
Form1->Memo1->Lines->Add(typeid(F4).name());
}
//----- Aufgabe 3 -----------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
typedef double(*real_func)(double) ;
#include <math>
#include <limits> // für numeric_limits<double>::epsilon
// a) Für die Ableitung wird ein Näherungswert berechnet.
void Newton_Nullstelle(double x0, real_func f,
int& i, double& x)
{
const double eps=std::numeric_limits<double>::epsilon()*100;
// epsilon ist der Unterschied zwischen 1 und der nächstgrößeren
// double-Zahl.
// const double eps=1E-12 kann ebenso anstelle von epsilon verwendet werden
// i zählt die Iterationen - zur Vermeidung von Endlosschleifen
// Abl: Näherungswert für die Ableitung bei x0
double Abl, h = 0.00001; // für die Berechnung der Ableitung: möglichst
// klein, aber doch so groß, daß f(x+h)<>f(x)
i = 0;
x = x0;
do { x0=x; // Startwert für die nächste Iteration
i++;
Abl = (f(x0 + h) - f(x0))/h;
x = x0 - f(x0)/Abl;
}
while ((fabs(x-x0) >= eps) && (i <= 100));
};
// b) Die Ableitung wird ebenfalls als Funktion übergeben.
void Newton_Nullstelle_mit_Abl(double x0,
real_func f, real_func Abl, int& i, double& x)
{
const double eps=std::numeric_limits<double>::epsilon()*100;
// const double eps=1E-12;
// i zählt die Iterationen - zur Vermeidung von Endlosschleifen
i = 0;
x = x0;
do { x0 =x; // Startwert für die nächste Iteration
i++;
x = x0 - f(x0)/Abl(x0);
}
while ((fabs(x-x0) >= eps) && (i <= 100));
}
// c) Testen mit der Funktion f(x) = x^2–r und Vergleich
//
der Ergebnisse mit sqrt(r).
int r; // damit r an die Quadratfunktion übergeben werden
// kann, ohne die Parameterliste zu ändern - nicht schön
double quadrat(double x)
{
return x*x - r;
}
double quadrat_abl(double x)
{
return 2*x;
}
void Teste_Newton1()
{
for (r=1; r<=100; r++)
{
int i;
double x;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
257
258
12 Lösungen
Newton_Nullstelle(r, quadrat, i, x);
Form1->Memo1->Lines->Add(Format("r=%g it=%d newt=%g sqrt=%g d=%g",
OPENARRAY(TVarRec,(1.0*r,i,x,sqrt(r),fabs(x-sqrt(r))))));
Newton_Nullstelle_mit_Abl(r, quadrat,quadrat_abl, i, x);
Form1->Memo1->Lines->Add(Format("r=%g it=%d newt=%g sqrt=%g d=%g",
OPENARRAY(TVarRec,(1.0*r,i,x,sqrt(r),fabs(x-sqrt(r))))));
};
}
void __fastcall TForm1::NewtonClick(TObject *Sender)
{
Teste_Newton1();
}
//----- Aufgabe 4 -----------------------------------------------// a)
double Trapezsumme(real_func f, double a, double b, int n)
{
double s = (f(a) + f(b))/2;
double h = (b-a)/n;
for (int i=1; i<n; i++)
s = s + f(a+i*h);
return s*h;
}
double Simpsonsumme(real_func f, double a, double b, int n)
{
double s = f(a)-f(b);
double h = (b-a)/n;
for (int i=0; i<=(n/2)-1; i++)
s = s + 4*f(a+(2*i+1)*h) + 2*f(a+2*(i+1)*h);
return s*h/3;
}
double f(double x)
{
return sqrt(1-x*x);
}
// b)
typedef double (*IntegrationsVerfahren)(real_func f, double a, double b, int n);
double iterate(double a, double b, real_func f, IntegrationsVerfahren I)
{
const double eps = 1e-8;
double x_alt = I(f,a,b,1);
double x
= I(f,a,b,2);
int n = 2;
while (fabs(x - x_alt) > eps)
{
x_alt = x;
n = 2*n;
x = I(f,a,b,n);
};
return x;
};
// c) Testprogramm für die Integrationsverfahren:
double q2,q1,q0;
double quadratPoly(double x)
{
return q2*x*x + q1*x + q0;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
double quadrat_Stamm(double x)
{
return q2*x*x*x/3 + q1*x*x/2 + q0*x;
}
void Teste_Integration()
{
q2 = 1; q1 = 0; q0 = 0;
for (int i=-10; i<=10; i++)
for (int j=i+1; j<=10;j++)
{
double a=i;
double b=j;
double t=iterate(a,b,quadratPoly,Trapezsumme);
double s=iterate(a,b,quadrat,Simpsonsumme);
double x=quadrat_Stamm(b) - quadrat_Stamm(a);
Form1->Memo1->Lines->Add(Format("t=%g s=%g x=%g",
OPENARRAY(TVarRec,(t,s,x))));
Application->ProcessMessages();
}
};
void __fastcall TForm1::NumIntClick(TObject *Sender)
{
double t=Trapezsumme(f,0,1,10000);
Memo1->Lines->Add("T="+FloatToStr(4*t));
double s=Simpsonsumme(f,0,1,10000);
Memo1->Lines->Add("S="+FloatToStr(4*s));
Teste_Integration();
}
//----- Aufgabe 5 -----------------------------------------------#include "\Loesungen\CppUtils\GraphUtils.cpp"
void PlotFunction(double x0, double x1, real_func f, TImage* Image1)
{
// a) das alte Bild löschen:
Image1->Canvas->Brush->Color = clWhite;
Image1->Canvas->Rectangle(0,0,Image1->Width,Image1->Height);
// b) bestimme den minimalen und maximalen Wert von f(x) im Bereich [x0,x1]:
double y0=f(x_Welt(0, x0, x1, Image1->Width)); // y0=min(f(x))
double y1=y0; // y1=max(f(x))
for (int px=1; px<=Form1->Image1->Width-/*–*/1; px++)
{
// transformiere px in Welt-Koordinaten
double x=x_Welt(px, x0, x1, Image1->Width);
double y=f(x);
if (y<y0) y0=y;
if (y>y1) y1=y;
};
// zeichne die Kurve:
Image1->Canvas->Pen->Color = clRed;
for (int px=0; px<=Form1->Image1->Width-/*–*/1; px++)
{
// transformiere px in Welt-Koordinaten
double x=x_Welt(px, x0, x1, Image1->Width);
// transformiere y in Bildkoordinaten
int py=y_Bildschirm(f(x), y0, y1, Image1->Height);
if (px == 0) Form1->Image1->Canvas->MoveTo(px,py);
else
Form1->Image1->Canvas->LineTo(px,py);
}
// c) zeichne das Gitternetz:
double dx=(x1-x0)/10; // 10 Gitterlinien
double dy=(y1-y0)/10; // 10 Gitterlinien
Gitternetz(Image1, x0,x1,dx, Image1->ClientWidth,
y0,y1,dy, Image1->ClientHeight);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
259
260
12 Lösungen
}
// d)
const int max_n_functions=10;
real_func pf=sin;
real_func f_arr[max_n_functions];
void __fastcall TForm1::PlotFunctionClick(TObject *Sender)
{
::PlotFunction(-10, 10, pf, Image1);
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ListBox1->Items->Add("sin");
f_arr[0]=sin;
ListBox1->Items->Add("cos");
f_arr[1]=cos;
ListBox1->Items->Add("exp");
f_arr[2]=exp;
}
void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
pf=f_arr[ListBox1->ItemIndex];
}
12.5.2 Aufgabe 6.3.8.6
// Datei: d:\Loesungen\kap-6\6.3\Fraktal2U.cpp
#include "Fraktal2U.h"
#include <float.h> // for
_control87
//----- Aufgabe 6.3.8.6 ---------------------------------------------// Die folgenden beiden Variablen werden in FormCreate
// bei mehr als 256 Bildschirmfarben
bool moreThan256Colors=false; // auf true gesetzt
bool usePalette=true;
// auf false gesetzt
// a)
double
double
x0= -2;
y0= 1.25;
double
double
x1 = 0.5; // rechts unten
y1 = -1.25;
enum
// links oben
{Mandelbrot, Julia} Art_des_Fraktals=Mandelbrot;
long double jx= -0.745, jy= 0.1125; // für die Julia-Mengen
bool aborted=false; // b) Damit die Schleife unterbrochen werden kann
TColor IndexColor(int i)
{
// return PALETTEINDEX (i%16);
if (moreThan256Colors)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{
i=i%256; // maximal 255 Farben
int r30=i%30;
// i->[0..29]
int t=255-5*r30; // [0..29]->[255..145]
if (i<30)
return RGB(255-8*i,255,255); // weiss..türkis
else if (i<60) return RGB(0,255-8*r30,255); // türkis..blau
else if (i<90) return RGB(0,0,t); // blau .. dunkelblau
else if (i<120) return RGB(0,t,0); // grün .. dunkelgrün
else if (i<150) return RGB(t,0,0); // rot .. dunkelrot
else if (i<180) return RGB(t,t,0);
else if (i<210) return RGB(t,0,t);
else if (i<240) return RGB(0,t,t);
else
return RGB(t,t,t); // Graustufen
}
else if (usePalette)
return PALETTEINDEX (i);
else
{
static TColor c[17]={clWhite, clLtGray, clGray, clDkGray, clGreen, clLime,
clNavy, clOlive, clAqua, clBlue, clPurple, clRed, clFuchsia,
clMaroon, clSilver, clTeal, clYellow};
return c[i%17];
}
}
#include "\Loesungen\CppUtils\GraphUtils.cpp"
void ZeichneFraktal(TImage* Image)
{
const int max_it = 200; // 50,100,200
aborted = false; // für b)
for (int px=0; (px <= Image->Width-1) && (!aborted); px++)
{
for (int py=0;(py<=Image->Height-1) && (!aborted); py++)
{
double x = x_Welt(px,x0,x1,Image->Width);
double y = y_Welt(py,y1,y0,Image->Height);
long double x_it_alt = x;
long double y_it_alt = y;
long double dist;
int i = 0;
do {
i++;
/*
Eigentlich müssen diese Berechnungen durchgeführt werden:
x_it = x_it_alt*x_it_alt - y_it_alt*y_it_alt + x;
y_it = 2*x_it_alt*y_it_alt + y;
dist = x_it*x_it+y_it*y_it;
Die folgenden Operationen sparen aber einige Multiplikationen:
*/
long double sqr_x = x_it_alt*x_it_alt;
long double sqr_y = y_it_alt*y_it_alt;
long double pr_xy = x_it_alt*y_it_alt;
long double x_it,y_it;
if (Art_des_Fraktals==Mandelbrot)
{
x_it = sqr_x - sqr_y + x;
y_it = pr_xy + pr_xy + y;
}
else if (Art_des_Fraktals==Julia)
{
x_it = sqr_x - sqr_y + jx;
y_it = pr_xy + pr_xy + jy;
}
dist = sqr_x + sqr_y;
x_it_alt = x_it;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
261
262
12 Lösungen
y_it_alt = y_it;
}
while ((i <= max_it) && (dist <= 4));
TColor color=IndexColor(i);
if (i > max_it) color = clBlack;
Image->Canvas->Pixels[px][py] = color;
}
// Damit ein eventuelles Anklicken des Buttons "Abbruch" erkannt wird
// und die Pixel gezeichnet werden.
if (!usePalette && px%5==0) Application->ProcessMessages();
}
}
void __fastcall TForm1::AbbruchClick(TObject *Sender)
{
// Da das Zeichnen eines Fraktals relativ lange dauern kann,
// kann man es durch ein Anklicken dieses Buttons abbrechen.
aborted=true;
}
// c)
double x_start, y_start; // Position bei MouseDown
void __fastcall TForm1::Image1MouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
x_start = x_Welt(X,x0,x1,Image1->Width);
y_start = y_Welt(Y,y1,y0,Image1->Height);
}
void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
x1 = x_Welt(X,x0,x1,Image1->Width);
y1 = y_Welt(Y,y1,y0,Image1->Height);
x0 = x_start;
y0 = y_start;
}
// f)
struct TMandelbrotParam {
char* Name;
double x0; double y0;
double x1; double y1;
};
const int nMBAW=13;
TMandelbrotParam MBPar[nMBAW]=
{
{"Apfelmännchen",
-2,
1.25,
0.5, -1.25},
{"Seepferdchen",
-0.88, 0.18, -0.70, 0.0},
{"Seepferdchen",
-0.73206,0.1570,-0.73196,0.1569},
{"Apfelmännchen unten", -0.17, -1.02, -0.15, -1.04},
{"Apfelmännchen links",-1.80,
0.03, -1.74, -0.03},
{"Spirale",
-0.750, 0.105, -0.740, 0.095}
};
struct TJuliaParam {
char* Name;
double x0; double y0;
double x1; double y1;
double jx; double jy;
};
const int nJAW=9;
TJuliaParam JPar[nJAW]=
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{ // Bezeichnungen teilweise nach Peitgen u. a. (1986)
{ "Peitgen Fig. 15", -1.6, -1.2, 1.6, 1.2, -0.74543, 0.11301},
{ "Peitgen Map 18",
-1, -1.2,
1, 1.2,
0.32, 0.043},
{ "Peitgen Map 20",
-2, -1.5,
2, 1.5, -0.12375, 0.56508},
{ "Siegel Disk", -2, -1.5,
2, 1.5, -0.39054, -0.58679},
{ "Fatou Staub", -2, -1.5,
2, 1.5, 0.11031, -0.67037},
{ "Zweig",
-2, -1.5,
2, 1.5, 0, 1},
{ "Zweig mit Perlen",
-2, -1.5,
2, 1.5, -0.15652, 1.03225},
{ "Julia 6",
-2, -1.5,
2, 1.5, -0.72, 0.53},
{ "Julia 7",
-2, -1.5,
2, 1.5, -1.776, 0.4},
};
void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
Art_des_Fraktals=Mandelbrot;
int i=ListBox1->ItemIndex;
if (i<0) i=0;
x0 = MBPar[i].x0;
y0 = MBPar[i].y0; // links oben
x1 = MBPar[i].x1;
y1 = MBPar[i].y1; // rechts unten
}
void __fastcall TForm1::ListBox2Click(TObject *Sender)
{
Art_des_Fraktals=Julia;
int i=ListBox2->ItemIndex;
if (i<0) i=0;
x0 = JPar[i].x0;
y0 = JPar[i].y0;
x1 = JPar[i].x1;
y1 = JPar[i].y1;
jx = JPar[i].jx;
jy = JPar[i].jy;
}
HPALETTE CreateMyPalette()
{ // Diese Funktion ist nur dann notwendig, wenn für den Bildschirm nur 256
// Farben eingestellt sind. Sie erzeugt eine eigene Farbpalette, damit auch
// mit dieser Einstellung mehr als die ca. 20 Systemfarben verfügbar sind.
const int nColMyPalette=6*6*6; // Anzahl der Farben in der eigenene Palette:
// Weniger als 256-20, damit bei einer Bildschirmeinstellung
// mit 256 Farben die 20 Systemfarben nicht beeinflußt werden.
PLOGPALETTE pPal = (PLOGPALETTE)LocalAlloc(LPTR, sizeof(LOGPALETTE) +
nColMyPalette * sizeof(PALETTEENTRY));
//
pPal = (COLORREF *)LocalAlloc(LPTR, sizeof(COLORREF) *(N+1));
if (!pPal) return(NULL);
pPal->palVersion = 0x300;
pPal->palNumEntries = nColMyPalette;
int i=0;
// Da schwarz eine spezielle Bedeutung hat,
// hier nicht mit schwarz=(0,0,0) anfangen
for (int r=1; r<=6; r++)
// wie in nColMyPalette
for (int g=1; g<=6; g++)
// wie in nColMyPalette
for (int b=1; b<=6; b++) // wie in nColMyPalette
if (i<nColMyPalette)
{
pPal->palPalEntry[i].peRed = r*nColMyPalette/6;
pPal->palPalEntry[i].peGreen = g*nColMyPalette/6;
pPal->palPalEntry[i].peBlue = b*nColMyPalette/6;
pPal->palPalEntry[i].peFlags = 0;
i++;
}
HPALETTE hLogPal = CreatePalette(pPal);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
263
264
12 Lösungen
LocalFree(pPal);
return(hLogPal);
}
void __fastcall TForm1::CountColorsClick(TObject *Sender)
{
const int max_col=257;
int a[max_col];
int s=0;
for (int i=0; i<max_col; i++) a[i]=0;
for (int px=0; (px <= Form1->Image1->Width-1) ; px++)
{
for (int py=0;(py<=Form1->Image1->Height-1) ; py++)
{
int col=Image1->Canvas->Pixels[px][py];
bool gefunden=false;
for (int j=0; j<s && !gefunden; j++)
if (a[j]==col) gefunden=true;
if (!gefunden)
{
if (0<=s && s<max_col-1) a[s]=col;
s++;
Application->ProcessMessages();
CountColors->Caption=IntToStr(s)+" "+IntToStr(px);
}
}
}
int n=0;
for (int i=0; i<max_col; i++)
if (a[i]>0) n++;
CountColors->Caption=IntToStr(n);
}
// 6. a)
#include <complex>
using namespace std;
typedef complex<double> Complex;
typedef Complex (*FComplex)(const Complex&);
void ZeichneFraktalComplex(TImage* Image, FComplex f)
{
const int max_it = 200; // 50,100,200
aborted = false; // für b)
for (int px=0; (px <= Image->Width-1) && (!aborted); px++)
{
for (int py=0;(py<=Image->Height-1) && (!aborted); py++)
{
Complex z(x_Welt(px,x0,x1,Image->Width),
y_Welt(py,y1,y0,Image->Height));
Complex // z_it_alt=z,
z_it=z;
int i = 0;
do {
i++;
if (Art_des_Fraktals==Mandelbrot)
z_it=f(z_it)+z;
else if (Art_des_Fraktals==Julia)
z_it=f(z_it)+Complex(jx,jy);
}
while ((i <= max_it) && (norm(z_it)<= 4));
TColor color=IndexColor(i);
if (i > max_it) color = clBlack;
Image->Canvas->Pixels[px][py] = color;
}
// Damit ein eventuelles Anklicken des Buttons "Abbruch" erkannt wird
// und die Pixel gezeichnet werden.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
265
if (!usePalette && px%5==0) Application->ProcessMessages();
}
}
Complex CFQuadr(const Complex& z)
Complex CFQuadr1(const Complex& z)
Complex CFQuadr2(const Complex& z)
Complex CFKubik(const Complex& z)
Complex CFxxexp(const Complex& z)
Complex CFNewtKubik(const Complex&
1.0)/(3.0*z*z+0.5*Complex(0,1));}
{ return z*z; }
{ return z*z - 0.5*z; }
{ return z*z +0.3*z.real(); }
{ return z*z*z; }
{ return z*z*exp(-z)+1.0; }
z) { return z-(z*z*z-
struct TFktItParam {
char* Name;
FComplex f;
};
const int nFktItPar=6;
TFktItParam FktItPar[nFktItPar]=
{
{ "z^2, reell ", 0},
{ "z^2, komplex", CFQuadr},
{ "z^2-0.5z", CFQuadr2},
{ "z^3", CFKubik},
{ "z*z*exp(-z)+1", CFxxexp},
{ "z-(z*z*z-1)/(3*z*z+0.5*Complex(0,1)",CFNewtKubik },
};
FComplex ItFkt=0;
void __fastcall TForm1::ListBox4Click(TObject *Sender)
{
int i=ListBox4->ItemIndex;
if (i<0) i=0;
ItFkt = FktItPar[i].f;
}
void __fastcall TForm1::ZeichneClick(TObject *Sender)
{
Form1->Caption="Fraktal: x0="+FloatToStr(x0)+" y0="+FloatToStr(y0)+
" x1="+FloatToStr(x1)+" y1="+FloatToStr(y1);
HPALETTE hpal;
if (usePalette) // nur bei 256 Farben
{
HPALETTE CreateMyPalette(); // Prototyp, Definition unten
HPALETTE hpal=CreateMyPalette();
SelectPalette(Image1->Canvas->Handle,hpal,true);
RealizePalette(Image1->Canvas->Handle);
}
if (ItFkt==0) ZeichneFraktal(Image1);
else
ZeichneFraktalComplex(Image1,ItFkt);
if (usePalette) DeleteObject(hpal);
// Form1->Image1->Picture->SaveToFile("c:\\Fraktal.bmp");
}
// 6. b)
TColor NewtonColor(int col,Complex z0, int n)
{ // nur für eine Bildschirmeinstellung mit mehr als 256 Farben
col=col%256;
for (int i=0; i<n; i++)
{ // ew ist die i-te Einheitswurzel von n:
Complex ew=Complex(cos(2*M_PI*double(i)/n),sin(2*M_PI*double(i)/n));
if (norm(z0-ew)<1E-10)
{ // die Lösung liegt nahe bei einer n-ten Einheitswurzel
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
266
12 Lösungen
if (col < 64)
{
col=col*4; //
if (i%3==0)
return RGB(256-col,0,0);
else if (i%3==1) return RGB(0,256-col,0);
else
return RGB(0,0,256-col);
}
else return RGB(0,256-col,256-col); // türkis
}
}
// falls keine Lösung paßt:
return RGB(256-col,256-col,256-col); // grau
}
int GradEinheitsPolynom;
void ZeichneNewtonFraktale(TImage* Image, FComplex f, FComplex abl)
{
const int max_it = 200; // 50,100,200
aborted = false; // für b)
for (int px=0; (px <= Image->Width-1) && (!aborted); px++)
{
for (int py=0;(py<=Image->Height-1) && (!aborted); py++)
{
Complex z0, z(x_Welt(px,x0,x1,Image->Width),
y_Welt(py,y1,y0,Image->Height));
int i = 0;
do {
z0 = z;
z = z0 - f(z0)/abl(z0);
i++;
}
while ((i <= max_it) && (norm(z-z0)>1E-10));
TColor color;
if (GradEinheitsPolynom==0) color=IndexColor(i);
else color=NewtonColor(i,z0,GradEinheitsPolynom);
if (i > max_it) color = clBlack;
Image->Canvas->Pixels[px][py] = color;
}
// Damit ein eventuelles Anklicken des Buttons "Abbruch" erkannt wird
// und die Pixel gezeichnet werden.
if (!usePalette && px%5==0) Application->ProcessMessages();
}
// Form1->Image1->Picture->SaveToFile("c:\\Newton.bmp");
}
Complex fu2(const Complex& z)
{ return z*z-1.0; }
Complex fu2_abl(const Complex& z) { return 2.0*z; }
Complex fu3(const Complex& z)
{ return z*z*z-1.0; }
Complex fu3_abl(const Complex& z) { return 3.0*z*z; }
Complex fu4(const Complex& z)
{ return z*z*z*z-1.0; }
Complex fu4_abl(const Complex& z) { return 4.0*z*z*z; }
Complex fu5(const Complex& z)
{ return z*z*z*z*z-1.0; }
Complex fu5_abl(const Complex& z) { return 5.0*z*z*z*z; }
Complex fu6(const Complex& z)
{ return z*z*z*z*z*z-1.0; }
Complex fu6_abl(const Complex& z) { return 6.0*z*z*z*z*z; }
Complex f1(const Complex& z)
Complex f1_abl(const Complex& z)
{ return z*z+1.0; }
{ return 2.0*z; }
Complex f3(const Complex& z)
Complex f3_abl(const Complex& z)
{ return z*z*z-z; }
{ return 3.0*z*z-1.0; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
267
Complex f4(const Complex& z)
Complex f4_abl(const Complex& z)
{ return exp(z)-z; }
{ return exp(z)-1.0; }
Complex f5(const Complex& z)
Complex f5_abl(const Complex& z)
{ return z*z*z-z*z+z+1.0; }
{ return 3.0*z*z-2.0*z+1.0; }
struct TNewtonParam {
char* Name;
double x0; double y0;
double x1; double y1;
FComplex f; FComplex abl;
int GradEinheitsPolynom;
};
const int nNewtPar=10;
TNewtonParam NPar[nNewtPar]=
{
{ "z^2-1", -10, -10, 10,
{ "z^3-1", -10, -10, 10,
{ "z^4-1", -10, -10, 10,
{ "z^5-1", -10, -10, 10,
{ "z^6-1", -10, -10, 10,
{
{
{
{
{
};
10,
10,
10,
10,
10,
fu2,
fu3,
fu4,
fu5,
fu6,
fu2_abl,
fu3_abl,
fu4_abl,
fu5_abl,
fu6_abl,
2},
3},
4},
5},
6},
"z^2+1", -10, -10, 10, 10, f1, f1_abl, 0},
"z^3-z", -10, -10, 10, 10, f3, f3_abl, 0},
"sin", -10, -10, 10, 10, sin, cos, 0},
"e^z-z", -10, -10, 10, 10, f4, f4_abl, 0},
"z^3-z^2+z+1", -10, -10, 10, 10, f5, f5_abl,0},
FComplex fNewt=f1, fNewt_abl=f1_abl;
void __fastcall TForm1::ListBox3Click(TObject *Sender)
{
int i=ListBox3->ItemIndex;
if (i<0) i=0;
x0 = NPar[i].x0;
y0 = NPar[i].y0; // links oben
x1 = NPar[i].x1;
y1 = NPar[i].y1; // rechts unten
fNewt=NPar[i].f;
fNewt_abl=NPar[i].abl;
GradEinheitsPolynom=NPar[i].GradEinheitsPolynom;
}
void __fastcall TForm1::NewtonClick(TObject *Sender)
{
Form1->Caption="Newton-Fraktal: x0="+FloatToStr(x0)+" y0="+FloatToStr(y0)+
" x1="+FloatToStr(x1)+" y1="+FloatToStr(y1);
ZeichneNewtonFraktale(Image1, fNewt, fNewt_abl);
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
int n=GetDeviceCaps(Image1->Canvas->Handle,NUMCOLORS);
moreThan256Colors = (n==-1); // -1, falls Farbtiefe größer als 8 Bits (256
Farben)
if (moreThan256Colors) usePalette=false;
// CountColors->Caption=IntToStr(n);
for (int i=0; i< nMBAW; i++)
ListBox1->Items->Add(MBPar[i].Name);
for (int i=0; i< nJAW; i++)
ListBox2->Items->Add(JPar[i].Name);
for (int i=0; i< nNewtPar; i++)
ListBox3->Items->Add(NPar[i].Name);
for (int i=0; i< nFktItPar; i++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
268
12 Lösungen
ListBox4->Items->Add(FktItPar[i].Name);
}
12.5.3 Aufgabe 6.5
// Datei: d:\Loesungen\kap-6\6.5\LogStilU.cpp
#include "LogStilU.h"
//--------- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Text=
"\nAufgabe 1 \n"
"Die Namen der Funktionen \n"
" \n"
" Quersumme, ZeigeQuersumme \n"
" Fibonacci, zeige_Fibonacci \n"
" prim, zeige_Primzahlen, zeige_Goldbach \n"
" zeige_PythagZahlentripel \n"
" f3nplus1, zeige_3nplus1 \n"
" \n"
"beschreiben die Aufgaben dieser Funktionen im jeweiligen Zusammenhang recht "
"präzise. \n";
}
//--------- Aufgabe 2 -----------------------------------------------double Mehrwertsteuer(double p2,double p3) {};
double druckeAdressaufkleber(double p2){};
double doitbabe(double i, int *p1, int p2, float p3)
{
int y=0;
if (p2==1)
for (int j=0;j<=i;j++) y=y+i;
else if (i==2)
y=Mehrwertsteuer(p2,5);
else if (i==3)
y=druckeAdressaufkleber(p2);
return y;
};
void __fastcall TForm1::Button2Click(TObject *Sender)
{
int p1=17;
double p3=18;
double z=doitbabe(19, &p1, 20, p3);
Memo1->Lines->Text=
"\nAufgabe 2 \n"
"Die Funktion 'doitbabe' führt in den verschiedenen if-Zweigen völlig
verschiedene "
"Aufgaben durch, die durch keinen Namen zusammengefasst werden können. "
"\n"
"Ein Aufruf dieser Funktion wird besser durch einen Aufruf der Funktionen "
"Mehrwertsteuer, druckeAdressaufkleber sowie einer Funktion Summe (für den "
"ersten Zweig) ersetzt. ";
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
269
}
//--------- Aufgabe 3 -----------------------------------------------int ggt(int x, int y)
{
// x=x0, y=y0, ggT(x,y) = ggT(x0,y0)
if (x<0) x=-x;
if (y<0) y=-y;
// ggT(x,y) = ggT(x0,y0)
while (y != 0)
// Ablaufprot. für den Schleifenkörper
{//
x
y
r
// ggT(x,y)=ggT(x0,y0)
x0
y0
int r = x%y; //
x = y;
//
y0
y = r;
//
x0%y0
// ggT(x,y) = ggT(y0,x0%y0) = ggT(x0,y0)
}
// ggT(x,y) = ggT(x0,y0) und y=0
return x; // ggT(x,y)==x;
}
x0%y0
double horner(double x, int n, double* p)
{
double s = 0;
int i = 1;
while (i <= n+1)
{
s = s*x + p[n-i+1];
i++;
}
return s;
}
void __fastcall TForm1::Button3Click(TObject *Sender)
{
Memo1->Lines->Text=
"\nAufgabe 3 \n"
"Für diesen Nachweis ist der Nachweis notwendig, dass die Funktion "
"immer das entsprechende Ergebnis hat. Das wurde aber gerade in den "
"Aufgaben von Kapitel 5.8.5 nachgewiesen. "
"\n"
"Da die Funktionen tatsächlich das machen, was ihr Name erwarten lässt, "
"ist der entsprechende Kommentar überflüssig, da er eine reine Wiederholung "
"der Anweisung davor ist. ";
int a=2,b=4;
double y=ggt(a,b);
// y==ggt(a,b)
int x=1,n=2;
double p[3]={1,2,3};
double z=horner(x,n,p);
// z==Horner(x,n,p)
}
12.5.4 Aufgaben 6.6.4 und 6.6.6
// Datei: d:\Loesungen\kap-6\6.6\RekursU.cpp
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
270
#include "RekursU.h"
//----- Aufgabe 6.6.4.1 ---------------------------------------------// Rekursive Funktionen
// a)
int rek_Fak(int n)
{ // Fakultät rekursiv berechnen
if (n<=0) return 1;
else return n*rek_Fak(n-1);
}
// b)
int rek_Fib(int n)
{ // Fibonacci rekursiv berechnen
if (n<=0) return 0;
else if (n==1) return 1;
else return rek_Fib(n-1) + rek_Fib(n-2);
}
// c)
int rek_ggT(int m, int n)
{ // ggT rekursiv berechnen
if (n==0) return m;
else return rek_ggT(n, m%n);
}
// a)
int it_Fak(int n)
{
int f=1;
for (int i=2; i<=n; i++)
f = f*i;
return f;
}
// b)
int it_Fib(int n)
{ // Fibonacci iterativ berechnen
int fib=0,f0=0,f1=1;
for (int i=0; i<n; i++)
{
fib = f0 + f1;
f1 = f0;
f0 = fib;
}
return fib;
}
// c)
int it_ggT(int x, int y)
{
// x=x0, y=y0, ggT(x,y) = ggT(x0,y0)
while (y != 0) //
Ablaufprot. für den Schleifenkörper
{
//
x
y
r
// ggT(x,y)=ggT(x0,y0)
x0
y0
r0
int r=x % y;//
x0 mod y0 +
x = y;
//
y0
y = r;
//
x0 mod y0
// ggT(x,y) = ggT(y0,x0 mod y0) = ggT(x0,y0)
// ggT(x,y)=ggT(x0,y0)
}
// ggT(x,y) = ggT(x0,y0) und y=0
return x;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.5 Lösungen Kapitel 6
};
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
RichEdit1->Lines->Add("No news are good news");
for (int i=1; i<50; i++)
for (int j=1; j<50; j++)
if (rek_ggT(i,j)!=it_ggT(i,j))
Form1->RichEdit1->Lines->Add(IntToStr(i)+" "+IntToStr(j));
for (int i=1; i<50; i++)
if (rek_Fak(i)!=it_Fak(i))
Form1->RichEdit1->Lines->Add(IntToStr(i));
for (int i=1; i<30; i++)
if (rek_Fib(i)!=it_Fib(i))
Form1->RichEdit1->Lines->Add(IntToStr(i));
}
//----- Aufgabe 6.6.4.2 ---------------------------------------------int ack(int n, int m)
{
if (n==0) return m+1;
else if (m==0) return ack(n-1,1);
else return ack(n-1,ack(n,m-1));
}
#include <math>
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
/*
ack(0,m) = m+1
ack(1,m) = m+2
ack(2,m) = 2*m+3
ack(3,m) = 2m+3+1
*/
RichEdit1->Lines->Add("No news are good news");
for (int m=0; m<50; m++)
if (ack(0,m)!=m+1)
RichEdit1->Lines->Add(m);
for (int m=0; m<50; m++)
if (ack(1,m)!=m+2)
RichEdit1->Lines->Add(m);
for (int m=0; m<50; m++)
if (ack(2,m)!=2*m+3)
RichEdit1->Lines->Add(m);
// für m>10 dauert das recht lange:
for (int m=0; m<10; m++)
{
int a=ack(3,m);
int e=pow(2,m+3)-3;
if (a!=e)
RichEdit1->Lines->Add(IntToStr(a)+" "+e);
}
}
//----- Aufgabe 6.6.4.3 ---------------------------------------------void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
RichEdit1->Lines->Add("Die Lösung ist das Programm 'Rechner'");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
271
272
12 Lösungen
}
//----- Aufgabe 6.6.4.4 ---------------------------------------------AnsiString dir="c:\\test";
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
fnMask->Text="*.*";
fnMask->Text="*";
DriveComboBox1->Drive='C';
}
void Ausgabe(AnsiString Name)
{
static int i=0;
i++;
Form1->RichEdit1->Lines->Add(IntToStr(i)+" "+Name);
}
void SearchSubdirs(const AnsiString Pfad,const AnsiString Mask)
{
WIN32_FIND_DATA FindFileData;
char FileName[MAX_PATH];
strcpy(FileName,(Pfad+Mask).c_str());
HANDLE h=FindFirstFile(
FileName, // Zeiger auf den Dateinamen
&FindFileData); // Zeiger auf struct
if (h!=INVALID_HANDLE_VALUE)
{
int found=1;
while (found)
{
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0)
{ // Unterverzeichnis
if (strcmp(FindFileData.cFileName,".")==0)
; // "." ignorieren
else if (strcmp(FindFileData.cFileName,"..")==0)
; // ".." ignorieren
else // Verzeichnis, rekursiv durchsuchen
{
Ausgabe(Pfad+FindFileData.cFileName);
SearchSubdirs(Pfad+FindFileData.cFileName+"\\",Mask);
}
}
else Ausgabe(Pfad+FindFileData.cFileName);
found=FindNextFile(h,&FindFileData);
}
FindClose(h);
}
};
void __fastcall TForm1::RecurseDirClick(TObject *Sender)
{
AnsiString Pfad=AnsiString(DriveComboBox1->Drive)+":\\";
AnsiString Mask=fnMask->Text;
RichEdit1->Enabled=false;
SearchSubdirs(Pfad,Mask);
RichEdit1->Enabled=true;
}
#include "TVFormU.h"
void SearchSubdirsTV(const AnsiString Pfad,const AnsiString Mask, TTreeNode* T);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
void __fastcall TForm1::TreeViewClick(TObject *Sender)
{
AnsiString Pfad=AnsiString(Form1->DriveComboBox1->Drive)+":\\";
AnsiString Mask=Form1->fnMask->Text;
TVForm->TreeView1->Items->Add(TVForm->TreeView1->TopItem, Pfad);
// Add fügt einen neuen Baumknoten am Ende der Liste ein,
// zu der TreeView1->TopItem gehört.
// TreeView1->TopItem ist der oberste Baumknoten in TreeView1.
SearchSubdirsTV(Pfad,Mask,TVForm->TreeView1->Items->Item[TVForm->TreeView1>Items->Count-1]);
// TreeView1->Items ist die Liste der Baumknoten in TreeView1.
// TreeView1->Items->Item[0] ist der erste Baumknoten in dieser Liste
TVForm->ShowModal();
}
//----- Aufgabe 6.6.4.5 ---------------------------------------------#include "RekFigU.h"
void __fastcall TForm1::Aufg5Click(TObject *Sender)
{
FormRekFig->ShowModal();
}
//----- Aufgabe 6.6.6.1 ---------------------------------------------typedef AnsiString DataType ; // Datentyp der Listendaten
typedef AnsiString KeyType ; // Datentyp der Schlüsselwerte
#include "\Loesungen\CppUtils\BinTree.cpp"
tree_node* t=0;
void __fastcall TForm1::TreeInsertClick(TObject *Sender)
{
insert_tree_node(t, Edit1->Text, Edit2->Text);
}
void process_tree_node(tree_node* n)
{
Form1->RichEdit1->Lines->Add(n->key);
for (list_node* i=n->first; i!=0; i=i->next)
Form1->RichEdit1->Lines->Add(" "+i->data);
};
void __fastcall TForm1::ShowTreeClick(TObject *Sender)
{
traverse_tree(t);
}
//----- Aufgabe 6.6.6.2 ---------------------------------------------/*
// Eigentlich wäre hier der Datentyp int für die Listendaten
angemessen:
typedef int DataType ; // Datentyp der Listendaten
typedef AnsiString KeyType ; // Datentyp der Schlüsselwerte
#include "\CppUtils\BinTree.cpp"
Damit die Definitionen von Aufgabe 6.6.6.1 verwendet werden
können, wird der Datentyp AnsiString verwendet.
*/
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
273
274
12 Lösungen
/*
bool use_map()
{
return Form1->MultiMap->Checked;
}
*/
#include "\Loesungen\CppUtils\StringUt.cpp"
#include <fstream>
tree_node *W;
void Text_lesen(char* ifn)
{
ifstream in;
in.exceptions(ios::badbit);
in.open(ifn);
int LineNr=0;
if (in)
{
string z; // string und nicht AnsiString, da getline einen String
// als Parameter hat
vector<AnsiString> v;
while (getline(in,z))
{
LineNr++;
v.clear();
parseString(AnsiString(z.c_str()),v);
for (vector<AnsiString>::iterator i=v.begin(); i!=v.end(); i++)
//
if (use_map) mm.insert(MMType::value_type(token.c_str(),LineNr));
//
else
insert_tree_node(W, *i, IntToStr(LineNr).c_str());
}
}
in.close(); // überflüssig
}
#include <iomanip>
// notwendig für setw
ofstream out;
void print_tree_node(tree_node* n)
{
const int Druckpos_Seitenzahl = 25;
const int n_pl = 80; // 8 // Anzahl Zahlen pro Zeile im Ausdruck
const int fw_pn = 6; // Feldbreite eine Zahl im Ausdruck
list_node* X=n->first;
out<<n->key.c_str()<<setw(Druckpos_Seitenzahl-n->key.Length())<<' '<<':';
int niLine = 0;
while (X!=0)
{
if (niLine == n_pl)
{
out<<endl;
niLine = 0;
out<<setw(Druckpos_Seitenzahl+1)<<' ';
};
niLine++;
out<<setw(fw_pn)<<(X->data).c_str();
X = X->next;
};
out<<endl;
};
void print_tree(tree_node* n)
{ // wie traverse_tree
if (n!=0)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
{
print_tree(n->left);
print_tree_node(n);
print_tree(n->right);
}
}
void GenerateConcordanceList(AnsiString infn,char* outfn)
{
Text_lesen(infn.c_str());
out.open(outfn,ios::trunc);
// if (use_map()) PrintMap();
// else traverse_tree(W);
print_tree(W);
out.close();
}
void __fastcall TForm1::KonkordanzClick(TObject *Sender)
{
OpenDialog1->Filter = "Textdateien|*.TXT";
OpenDialog1->InitialDir = dir;
if (OpenDialog1->Execute())
{
AnsiString fn=OpenDialog1->FileName;
RichEdit1->Lines->Add("Ausgabedatei: "+fn+".kon");
Application->ProcessMessages();
TCursor Save_Cursor = Screen->Cursor;
Screen->Cursor = crHourGlass;
// Sanduhr-Cursor anzeigen
Application->ProcessMessages();
GenerateConcordanceList(fn,(fn+".kon").c_str());
Screen->Cursor = Save_Cursor; // Alten Zustand wiederherstellen
}
}
12.5.5 Aufgabe 6.6.4.3
// Datei: d:\Loesungen\kap-6\6.6\RechnerU.cpp
#include "RechnerU.h"
//-------------------------------------------------------------------void whitespace_ueberlesen(AnsiString s, int& i)
{ // läßt sich auf Tabs usw. erweitern
while ((i <= s.Length()) && (s[i]==' ')) i++;
// (s[i] <> ' ') or "fertig"
}
bool SubstrInStr(AnsiString Substr, AnsiString Str)
{ // true, falls SubStr in Str enthalten ist
return Str.Pos(Substr);
// SubstrInStr("a","abc"); // true
// SubstrInStr("abc","a"); // false
// SubstrInStr("abc","abc");// true
// SubstrInStr("","");
// false
}
double Zahl_lesen(AnsiString s, int& i)
{
AnsiString ops;
while ((i<=s.Length())&& SubstrInStr(s[i],"0123456789,"))
ops = ops + s[i++]; // s[i] zuweisen, anschließend i++
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
275
276
12 Lösungen
// (s[i] keine Ziffer) or (fertig)
try {return StrToFloat(ops);}
catch(...)
{ // Falls s[i] keine Ziffer war ist ops==""
ShowMessage("Unerwartetes Zeichen: '"+AnsiString(s[i])+"'");
return 0;
}
}
char Operand_einlesen(AnsiString s, int& i)
{
if (i <= s.Length())
return s[i++];
return ' '; //damit kein undefinierter Wert zurückgegeben
// wird und der Fehler anschl. erkannt werden kann
}
double MultExpr(AnsiString s, int& i); // Prototyp für die rekursiven Aufrufe
double PrimExpr(AnsiString s, int& i); // Prototyp für die rekursiven Aufrufe
void errormessage(int i, AnsiString msg, AnsiString s)
{
AnsiString m=s;
m.Insert("<---",i+1);
if (msg=="") ShowMessage("Syntaxfehler bei Pos. "+IntToStr(i)+"\n"+m);
else ShowMessage(msg+IntToStr(i)+"\n"+m);
}
double AdditiveExpr(AnsiString s, int& i)
{
whitespace_ueberlesen(s,i);
double result = MultExpr(s,i);
/* 1 */
while ( (i<=s.Length()) && (SubstrInStr(s[i],"+-")) )
{
char op = Operand_einlesen(s,i);
whitespace_ueberlesen(s,i);
switch (op)
{
case '+': result = result + MultExpr(s,i); // term
break;
case '-': result = result - MultExpr(s,i); // term
break;
}
whitespace_ueberlesen(s,i);
}
return result;
}
double MultExpr(AnsiString s, int& i)
{
double result = PrimExpr(s,i);
whitespace_ueberlesen(s,i);
while ( (i <= s.Length()) && (SubstrInStr(s[i],"*/")) )
{
char op = Operand_einlesen(s,i);
whitespace_ueberlesen(s,i);
switch (op)
{
case '*': result = result * PrimExpr(s,i); // factor
break;
case '/': result = result / PrimExpr(s,i); // factor
break;
}
whitespace_ueberlesen(s,i);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
return result;
}
//----- Aufgabe 6.4.3. ----------------------------------------------#include <ctype.h>
AnsiString id_lesen(AnsiString s, int& i)
{ // ähnlich wie die Syntax für einen identifier
AnsiString result;
while ((i<=s.Length())&& (isalpha(s[i]) || isdigit(s[i]) || (s[i]=='_') ))
{
result = result + UpCase(s[i]); // s[i] zuweisen, anschließend i++
i++;
}
return result;
}
#include <math.h>
// Ende Lösung Aufgabe 3
double PrimExpr(AnsiString s, int& i)
{
double result;
if (SubstrInStr(s[i],"1234567890,"))
result=Zahl_lesen(s,i);
else if (s[i]=='(')
{
i++; // weiterlesen nach "(" nicht vergessen
whitespace_ueberlesen(s,i);
result = AdditiveExpr(s,i);
if (s[i]== ')')
{
i++;
whitespace_ueberlesen(s,i);
}
else errormessage(i,"')' expected: ",s);
}
else if (s[i]=='-')
{
i++; // weiterlesen nach "-–" nicht vergessen
whitespace_ueberlesen(s,i);
result = -PrimExpr(s,i);
}
// Lösung von Aufgabe 3.
else if (isalpha(s[i]))
{
AnsiString id=id_lesen(s,i);
whitespace_ueberlesen(s,i);
if (id=="PI") result=M_PI;
else if(id=="PIQ") result=M_PI*M_PI;
else if (s[i]=='(')
{
i++;
if (id=="SIN") result = sin(AdditiveExpr(s,i));
else if (id=="COS") result = cos(AdditiveExpr(s,i));
else
{
result = 0;
errormessage(i,"invalid function: ",s);
}
if (s[i]==')') i++;
else errormessage(i,"')' expected: ",s);
}
else errormessage(i,"'(' expected: ",s);
}
// Ende Lösung von Aufgabe 3.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
277
278
12 Lösungen
else errormessage(i,"",s);
return result;
}
double expression(AnsiString t)
{
int i=1;
return AdditiveExpr(t,i);
}
void __fastcall TForm1::rechneClick(TObject *Sender)
{
if (Edit1->Text.Trim()!="")
{
double result=expression(Edit1->Text);
Memo1->Lines->Add(Edit1->Text+" = "+result);
}
}
//-------------------------------------------------------------------void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (Key==13) rechneClick(Sender);
}
//-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Edit1->Text="";
Memo1->Clear();
}
//--------------------------------------------------------------------
void testeZeile(AnsiString s, double erg)
{
double r = expression(s);
if (erg != r) Form1->Memo1->Lines->Add(s+"="+FloatToStr(r)+
" nicht: "+FloatToStr(erg));
}
void teste_parser1()
{
testeZeile("1+(2 + 3
testeZeile("1+(2 - 3
testeZeile("1+(2 * 3
testeZeile("1+(6 / 3
)
)
)
)
",
",
",
",
6);
0);
7);
3);
testeZeile("1-(2
testeZeile("1-(2
testeZeile("1-(2
testeZeile("1-(6
+
*
/
3
3
3
3
)
)
)
)
",
",
",
",
-4);
2);
-5);
-1);
testeZeile("3*(2
testeZeile("3*(2
testeZeile("3*(2
testeZeile("3*(6
+
*
/
3
3
3
3
)
)
)
)
",
",
",
",
15);
-3);
18);
6);
testeZeile("10/(2
testeZeile("10/(2
testeZeile("12/(2
testeZeile("10/(6
}
+
*
/
3
3
3
3
)
)
)
)
",
",
",
",
2);
-10);
2);
5);
void teste_parser()
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
279
{
testeZeile(" 1+2/(1 + 1 )
",
2);
testeZeile("4 - 5 + (10 / 10)", 0);
testeZeile(" 1/sin(1) ",
1.188395105778);
testeZeile("sin(3)*sin(3)+cos(3)*cos(3)
",1);
testeZeile(" 1+2/(1 + 1 )
",2);
testeZeile(" -(3 + 4 )
",-7);
testeZeile(" 10 -(3 + 4 )
",3);
testeZeile(" 1
",1);
testeZeile(" -1
",-1);
testeZeile(" 1,1+ 2,2",3.3);
testeZeile("1,1+2,2+3,3",6.6 );
testeZeile("3*4 + 5*6 ",42);
testeZeile("(4 - 5) *10 / 10",-1);
testeZeile("4 -(5 + 10) / 10",2.5);
testeZeile("4 - 5 + -10 / 10",-2);
testeZeile("4 - 5 + (10) / 10)",0);
testeZeile("4 - 5 + (10 / 10)",0);
testeZeile(" sin(1) ",0.841470984807897);
testeZeile(" 1/sin(1) ",1.18839510577812);
testeZeile(" sin(1+sin(1)) ",0.963590724541833);
testeZeile(" sin(2) ",0.909297426825682);
testeZeile(" sin(1)+sin(2) ",1.75076841163358);
testeZeile(" 1/(sin(1)+sin(2)) ",0.571177771631678);
testeZeile(" 2+1/(sin(1)+sin(2)) ",2.57117777163168);
testeZeile("sin(3)*sin(3)+cos(3)*cos(3)
",1);
testeZeile(" 1+ pi ",4.14159265358979);
testeZeile(" 1/(1+ pi) ",0.241453007005224);
testeZeile(" piq/(pi*pi) ",1);
}
void __fastcall TForm1::TestClick(TObject *Sender)
{
teste_parser();
teste_parser1();
}
12.5.6 Aufgabe 6.6.4.4
// Datei: d:\Loesungen\kap-6\6.6\TVFormU.cpp
#include "TVFormU.h"
TTVForm *TVForm;
__fastcall TTVForm::TTVForm(TComponent* Owner)
: TForm(Owner)
{
}
void SearchSubdirsTV(const AnsiString Pfad,const AnsiString Mask, TTreeNode* T)
{
WIN32_FIND_DATA FindFileData;
char FileName[MAX_PATH] ;
strcpy(FileName,(Pfad+Mask).c_str());
HANDLE h=FindFirstFile(FileName, // Zeiger auf den Dateinamen
&FindFileData); // Zeiger auf struct
if (h!=INVALID_HANDLE_VALUE)
{
int found=1;
while (found)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
280
12 Lösungen
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0)
{ // Unterverzeichnis
if (strcmp(FindFileData.cFileName,".")==0)
; // "." ignorieren
else if (strcmp(FindFileData.cFileName,"..")==0)
; // ".." ignorieren
else // Verzeichnis, rekursiv durchsuchen
{
TVForm->TreeView1->Items->AddChild(T,FindFileData.cFileName);
// AddChild fügt TreeView1 einen neuen Knoten am Ende der Liste
// hinzu, auf die T zeigt
int n=T->Count; // Anzahl der Knoten in der Liste, auf die T zeigt
SearchSubdirsTV(Pfad+FindFileData.cFileName+"\\",Mask,T->Item[n1]);
// T->Item[n-1] ist der letzte Knoten in der Liste, auf die T zeigt
}
}
//else Ausgabe(Pfad+FindFileData.cFileName);
found=FindNextFile(h,&FindFileData);
}
FindClose(h);
T=T->Parent;
}
};
/*
void __fastcall TForm1::SortClick(TObject *Sender)
{
TreeView1->SortType=stBoth;
TreeView1->AlphaSort();
}
*/
// #include "RekursU.h"
void __fastcall TTVForm::FormCreate(TObject *Sender)
{
TreeView1->Align=alClient;
}
12.5.7 Aufgabe 6.6.4.5
// Datei: d:\Loesungen\kap-6\6.6\RekFigU.cpp
#include "RekFigU.h"
TFormRekFig *FormRekFig;
__fastcall TFormRekFig::TFormRekFig(TComponent* Owner)
: TForm(Owner)
{
}
//----------Koch'sche Kurven ------------------------------------int round(double x)
{
return int(x+0.5);
}
#include <cmath>
void Koch(TCanvas* C, int n, double leftx, double lefty,
double rightx, double righty)
{
const double r = std::sqrt(3)/6;//ca. r=0.29;
if (n<=1)
{
C->MoveTo(round(leftx), round(lefty));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
C->LineTo(round(rightx), round(righty));
}
else
{
Koch(C, n-1, leftx, lefty,
(rightx + 2.0*leftx)/3.0,(righty + 2.0*lefty)/3.0);
Koch(C,n-1,
(rightx + 2.0*leftx)/3.0,(righty + 2.0*lefty)/3.0,
0.5*(rightx+leftx) - r*(lefty-righty),
0.5*(righty + lefty) + r*(leftx-rightx));
Koch(C,n-1,
0.5*(rightx + leftx) - r*(lefty-righty),
0.5*(righty + lefty) + r*(leftx-rightx),
(2.0*rightx + leftx)/3.0,(2.0*righty + lefty)/3.0);
Koch(C,n-1,
(2.0*rightx + leftx)/3.0,(2.0*righty + lefty)/3.0,
rightx, righty);
}
}
void ClearImages(int W, int H)
{
FormRekFig->Image1->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H));
FormRekFig->Image2->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H));
FormRekFig->Image3->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H));
FormRekFig->Image4->Canvas->Brush->Color=clWhite;// FillRect(TRect(0,H,W,H));
FormRekFig->Image1->Canvas->FillRect(TRect(0,0,W,H));
FormRekFig->Image2->Canvas->FillRect(TRect(0,0,W,H));
FormRekFig->Image3->Canvas->FillRect(TRect(0,0,W,H));
FormRekFig->Image4->Canvas->FillRect(TRect(0,0,W,H));
}
int level=1;
void __fastcall TFormRekFig::Koch_Click(TObject *Sender)
{
FormRekFig->Caption = "Koch'sche Kurven, n=2, 3, 4, 5";
int W=Image1->ClientWidth-1;
int H=Image1->ClientHeight-3;
ClearImages(W, H+2);
Koch(FormRekFig->Image1->Canvas,2,0,H,W,H);
Koch(FormRekFig->Image2->Canvas,3,0,H,W,H);
Koch(FormRekFig->Image3->Canvas,4,0,H,W,H);
Koch(FormRekFig->Image4->Canvas,5,0,H,W,H);
}
//----- Aufgabe 6.6.4.5 ---------------------------------------------void Dreieck(TCanvas* C, int n, double x, double y, double L)
{
const double r = std::sqrt(3);
if (n<=1)
{ // zeichnet ein gleichseitiges Dreieck im Punkt (x,y)
// mit der Seitenlänge L
TPoint Eckpunkte[4];
Eckpunkte[0]=Point(x,y);
Eckpunkte[1]=Point(x+L,y);
Eckpunkte[2]=Point(x+L/2,y-L*r/2);
Eckpunkte[3]=Point(x,y);
C->Brush->Color = clBlack;
C->Polygon(Eckpunkte, 3);
}
else
{
Dreieck(C, n-1, x, y, L/2);
Dreieck(C, n-1, x+L/2, y, L/2);
Dreieck(C, n-1, x+L/4, y-L*r/4, L/2);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
281
282
}
}
void __fastcall TFormRekFig::rekDreiecke_Click(TObject *Sender)
{
FormRekFig->Caption = "Rekursive Dreiecke , n=1, 2, 5, 9";
int W=Image1->ClientWidth-1;
int H=Image1->ClientHeight-3;
ClearImages(W,H+2);
Dreieck(FormRekFig->Image1->Canvas,1,0,H,H);
Dreieck(FormRekFig->Image2->Canvas,2,0,H,H);
Dreieck(FormRekFig->Image3->Canvas,5,0,H,H);
Dreieck(FormRekFig->Image4->Canvas,9,0,H,H);
}
12.5.8 Aufgabe 6.6.6.3
// Datei: d:\Loesungen\kap-6\6.6\DupFilesU.cpp
#include "DupFilesU.h"
//----- Aufgabe 6.6.6.3 ---------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
DriveComboBox1->Drive='C';
RichEdit1->ScrollBars=ssVertical;
Form1->Caption="Duplicate Files";
Form1->MultiMap->Checked=true;
OneDrive->Caption="Ein Laufwerk";
AllDrives->Caption="Alle Laufwerke";
}
typedef AnsiString DataType ; // Datentyp der Listendaten
typedef AnsiString KeyType ; // Datentyp der Schlüsselwerte
#include "\Loesungen\CppUtils\BinTree.cpp"
tree_node* W=0;
void process_tree_node(tree_node* n)
{
if (n->first->next!=0)
{ // nur Listen mit mehr als einem Knoten ausgeben
Form1->RichEdit1->Lines->Add(n->key.Trim());
for (list_node* i=n->first; i!=0; i=i->next)
Form1->RichEdit1->Lines->Add(" "+i->data);
}
};
#include <map>
using namespace std;
typedef multimap<KeyType,DataType> MMType;
MMType mm;
void PrintMap()
{
for (MMType::iterator i=mm.begin(); i!=mm.end(); )
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.5 Lösungen Kapitel 6
MMType::iterator k, first=mm.lower_bound((*i).first);
MMType::iterator j, last=mm.upper_bound((*i).first);
k=first;
k++;
if (k!=last) // nur doppelte Einträge anzeigen
{
Form1->RichEdit1->Lines->Add((*i).first.Trim());
for (j=first; j!=last; j++)
{
Form1->RichEdit1->Lines->Add(" "+(*j).second);
i++; // Mit jedem gefundenen Wert hochzählen
};
}
else i++;
}
}
bool use_map()
{
return Form1->MultiMap->Checked;
}
AnsiString AddLeadingBlanks(AnsiString s, int n)
{ // Ergänzt den String s um so viele führende Nullen, daß
// der Funktionswert die Länge n hat. Mit dieser Funktion
// wird erreicht, daß ein Vergleich der Strings "12" < " 2"
// dasselbe Ergebnis hat wie der Vergleich der Zahlen 12<2.
// Ohne führendes Leerzeichen wäre das Ergebnis verschieden.
return s.StringOfChar(' ',n-s.Length())+s;
}
void SearchSubdirs(AnsiString Pfad)
{
WIN32_FIND_DATA FindFileData;
char FileName[MAX_PATH] ;
AnsiString Mask="*.*";
strcpy(FileName,(Pfad+Mask).c_str());
HANDLE h=FindFirstFile(
FileName, // Zeiger auf den Dateinamen
&FindFileData); // Zeiger auf struct
if (h!=INVALID_HANDLE_VALUE)
{
int found=1;
while (found)
{
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0)
{ // Unterverzeichnis
if (strcmp(FindFileData.cFileName,".")==0)
; // "." ignorieren
else if (strcmp(FindFileData.cFileName,"..")==0)
; // ".." ignorieren
else // Verzeichnis, rekursiv durchsuchen
SearchSubdirs(Pfad+FindFileData.cFileName+"\\");
}
else
{
AnsiString Low=IntToStr(FindFileData.nFileSizeLow);
AnsiString High=IntToStr(FindFileData.nFileSizeHigh);
if (High=="0") High=" ";
KeyType Key=AddLeadingBlanks(High,10)+AddLeadingBlanks(Low,10)+"
"+FindFileData.cFileName;
DataType Data=Pfad+FindFileData.cFileName;
if (use_map()) mm.insert(MMType::value_type(Key,Data));
else insert_tree_node(W, Key, Data);
}
found=FindNextFile(h,&FindFileData);
}
FindClose(h);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
283
284
12 Lösungen
}
};
void __fastcall TForm1::OneDriveClick(TObject *Sender)
{ // Durchsucht nur das ausgewählte Laufwerk
TCursor Save_Cursor = Screen->Cursor;
Screen->Cursor = crHourGlass;
// Cursor als Sanduhr
RichEdit1->Clear();
AnsiString Laufwerk=AnsiString(DriveComboBox1->Drive)+":\\";
SearchSubdirs(Laufwerk);
if (use_map()) PrintMap();
else traverse_tree(W);
Screen->Cursor = Save_Cursor;
}
//-------------------------------------------------------------------void __fastcall TForm1::AllDrivesClick(TObject *Sender)
{ // Durchsucht alle Laufwerke
TCursor Save_Cursor = Screen->Cursor;
Screen->Cursor = crHourGlass;
// Cursor als Sanduhr
RichEdit1->Clear();
for (int i=0; i<DriveComboBox1->Items->Count; i++)
{
AnsiString Laufwerk=AnsiString(DriveComboBox1->Items->Strings[i][1])+":\\";
SearchSubdirs(Laufwerk);
}
if (use_map()) PrintMap();
else traverse_tree(W);
Screen->Cursor = Save_Cursor;
}
//--------------------------------------------------------------------
12.5.9 Aufgabe 6.6.6.4
// Datei: d:\Loesungen\kap-6\6.6\DirTreeU.cpp
#include <vcl\system.hpp>
#include "DirTreeU.h"
#pragma link "cdiroutl"
//----- Aufgabe 6.6.6.4 ---------------------------------------------// a)
struct File_list_node; // Vorwärtsreferenz, da
// dir_node einen Zeiger auf File_list_node und
// umgekehrt
struct dir_node {
AnsiString DirName;
double FilesSize;
File_list_node* first;
File_list_node* last;
};
// für Teil d)
struct File_list_node {
AnsiString FileName;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
dir_node* sub_dir;
File_list_node* next;
};
void insert_File(AnsiString dn, AnsiString fn, dir_node* dir)
{ // fügt einen Knoten in die Liste ein
File_list_node* tmp=new File_list_node; // (1)
tmp->FileName = fn;
tmp->next
= 0;
tmp->sub_dir = 0; // kein Unterverzeichnis
if (dir->last==0) // erster Knoten
dir->first = tmp;
else // zweiter oder weiterer Knoten
dir->last->next = tmp;
dir->last = tmp;
}
void insert_Dir_node(AnsiString DirName, dir_node*& dir)
{
if (dir==0)
{
dir = new dir_node;
dir->first=0;
dir->last=0;
dir->DirName=DirName;
dir->FilesSize=0;
}
else ShowMessage("dir!=0: "+DirName);
}
// b)
double // Teil d), sonst void
SearchSubdirs(AnsiString Pfad, dir_node*& dir)
{
insert_Dir_node(Pfad,dir);
// <-- Änderung
WIN32_FIND_DATA FindFileData;
char FileName[MAX_PATH] ;
AnsiString Mask="*.*";
strcpy(FileName,(Pfad+Mask).c_str());
HANDLE h=FindFirstFile(
FileName, // Zeiger auf den Dateinamen
&FindFileData); // Zeiger auf struct
if (h!=INVALID_HANDLE_VALUE)
{
int found=1;
while (found)
{
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0)
{ // Unterverzeichnis
if (strcmp(FindFileData.cFileName,".")==0)
; // "." ignorieren
else if (strcmp(FindFileData.cFileName,"..")==0)
; // ".." ignorieren
else // Verzeichnis, rekursiv durchsuchen
{
insert_File(Pfad,FindFileData.cFileName, dir); // <-- Änderung
dir->FilesSize +=
// Teil d)
SearchSubdirs(Pfad+FindFileData.cFileName+"\\",
dir->last->sub_dir);
// <-- Änderung
}
}
else
insert_File(Pfad,FindFileData.cFileName,dir); // <-- Änderung
LARGE_INTEGER c;
// Teil d)
c.u.LowPart=FindFileData.nFileSizeLow;
// Teil d)
c.u.HighPart=FindFileData.nFileSizeHigh; // Teil d)
dir->FilesSize+=c.QuadPart;
// Teil d)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
285
286
12 Lösungen
found=FindNextFile(h,&FindFileData);
}
FindClose(h);
return dir->FilesSize;
// Teil d)
}
return -1;
};
/* nur zum Testen
void ShowDir(dir_node* Dirs)
{
//Form1->Memo1->Lines->Add(Dirs->DirName+FloatToStr(Dirs->FilesSize));
for (File_list_node* i=Dirs->first; i!=0; i=i->next)
{
//
Form1->Memo1->Lines->Add(i->FileName);
if (i->sub_dir!=0)
ShowDir(i->sub_dir);
}
}
*/
// c)
void ShowDirTree(dir_node* Dirs, TTreeNode* T)
{
for (File_list_node* i=Dirs->first; i!=0; i=i->next)
if (i->sub_dir!=0)
{
Form1->TreeView1->Items->AddChild(T, i->FileName+": "+ FloatToStrF(i>sub_dir->FilesSize,ffNumber,18,0));
ShowDirTree(i->sub_dir,T->Item[T->Count-1]);
}
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Form1->Caption="Show directory tree and size ";
TreeView1->Align=alLeft;
}
//-------------------------------------------------------------------void __fastcall TForm1::ShowDirClick(TObject *Sender)
{
// GetLogicalDriveStrings(BufSize,lpBuffer);
// ermöglicht es, die Laufwerke zu lesen
dir_node* Dirs=0;
AnsiString Laufwerk=AnsiString(DriveComboBox1->Drive)+":\\";
SearchSubdirs(Laufwerk,Dirs);
if (Dirs!=0)
{
TreeView1->Items->Add(TreeView1->TopItem, Dirs->DirName+": "+
FloatToStrF(Dirs->FilesSize,ffNumber,18,0));
ShowDirTree(Dirs, TreeView1->Items->Item[TreeView1->Items->Count-1]);
}
}
void __fastcall TForm1::SortClick(TObject *Sender)
{
TreeView1->SortType=stBoth;
TreeView1->AlphaSort();
}
12.5.10 Aufgabe 6.8
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
// Datei: d:\Loesungen\kap-6\6.8\OvrLoadU.cpp
#include "OvrLoadU.h"
//----- Aufgabe 1 -----------------------------------------------int Abs(int x)
{
if (x<0) return -x;
else return x;
}
double Abs(double x)
{
if (x<0) return -x;
else return x;
}
long double Abs(long double x)
{
if (x<0) return -x;
else return x;
}
void __fastcall TForm1::Abs_Click(TObject *Sender)
{
short b1;
Abs(b1); // Abs(int)
unsigned b2;
//Abs(b2); // Ambiguity between 'Abs(int)' and 'Abs(long double)'
enum {e1= -1, e2=1} b3;
Abs(b3); // Abs(int);
char b6;
Abs(b6); // Could not find a match for Abs(char*)
}
//----- Aufgabe 2 -----------------------------------------------typedef double(*real_func)(double);
#include <limits>
void Newton_Nullstelle_mit_Abl(double x0,
real_func f, real_func Abl, int& i, double& x)
{
const double eps=std::numeric_limits<double>::epsilon()*100;
// i zählt die Iterationen - zur Vermeidung von Endlosschleifen
i = 0;
x = x0;
do { x0 =x; // Startwert für die nächste Iteration
i++;
x = x0 - f(x0)/Abl(x0);
}
while ((Abs(x-x0) >= eps) && (i <= 100));
}
typedef long double(*ld_real_func)(long double);
void Newton_Nullstelle_mit_Abl(long double x0,
ld_real_func f, ld_real_func Abl, int& i, long double& x)
{
const long double eps=std::numeric_limits<long double>::epsilon()*100;
// i zählt die Iterationen - zur Vermeidung von Endlosschleifen
i = 0;
x = x0;
do { x0 =x; // Startwert für die nächste Iteration
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
287
288
12 Lösungen
i++;
x = x0 - f(x0)/Abl(x0);
}
while ((Abs(x-x0) >= eps) && (i <= 100));
}
int r; // damit r an die Quadratfunktion übergeben werden
// kann, ohne die Parameterliste zu ändern - nicht schön
double quadrat(double x)
{
return x*x - r;
}
double quadrat_abl(double x)
{
return 2*x;
}
long double quadrat_ld(long double x)
{
return x*x - r;
}
long double quadrat_abl_ld(long double x)
{
return 2*x;
}
void __fastcall TForm1::NewtonClick(TObject *Sender)
{
for (r=1; r<=100; r++)
{
int i;
long double xd;
Newton_Nullstelle_mit_Abl(r, quadrat_ld,quadrat_abl_ld, i, xd);
Form1->Memo1->Lines->Add(Format("LD r=%g it=%d newt=%g sqrt=%g d=%g",
OPENARRAY(TVarRec,(1.0*r,i,xd,sqrt(r),Abs(xd-sqrt(r))))));
double x;
Newton_Nullstelle_mit_Abl(r, quadrat,quadrat_abl, i, x);
Form1->Memo1->Lines->Add(Format("r=%g it=%d newt=%g sqrt=%g d=%g",
OPENARRAY(TVarRec,(1.0*r,i,x,sqrt(r),Abs(x-sqrt(r))))));
};
}
//----- Aufgabe 3 -----------------------------------------------void f(bool b) { }
void f(int i) { }
void __fastcall TForm1::BoolClick(TObject *Sender)
{
f(true); // f(bool)
f(TRUE); // f(int)
}
//----- Aufgabe 4 -----------------------------------------------void g(char *) { }
void g(const char *) { }
void h(char *, double d=0) { }
void h(const char *, int i=0) { }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
void __fastcall TForm1::FktAufrufeClick(TObject *Sender)
{
g("abc"); // g(char*)
const char* s1="abc";
char* s2="abc";
char* const s3="abc";
g(s1); // g(const char*)
g(s2); // g(char*)
g(s3); // g(char*)
const char s4[20]="abc";
char s5[20]="abc";
char const s6[20]="abc";
g(s4); // g(const char*)
g(s5); // g(char*)
g(s6); // g(const char*)
//
//
//
h(s1,1); // h(const char*,int)
h(s2,1); // Ambiguity between h(char*,double) and h(const char*,int)
h(s3,1); // Ambiguity between h(char*,double) and h(const char*,int)
h(s4,1); // h(const char*,int)
h(s5,1); // Ambiguity between h(char*,double) and h(const char*,int)
h(s6,1); // h(const char*,int)
}
12.5.11 Aufgaben 6.9
// Datei: d:\Loesungen\kap-6\6.9\GlOpFktU.cpp
#include "GlOpFktU.h"
//----- Aufgabe 1 -----------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Text=
"Der Operator ^ kann nur für selbstdefinierte Datentypen überladen "
"werden. Da '-' eine höhere Priorität als '^' hat, wird x^n-1 als "
"x^(n-1) ausgewertet. Üblicherweise ist das aber (x^n)-1";
}
//----- Aufgabe 2 -----------------------------------------------struct CBruch {
int z,n; // z: Zähler, n: Nenner
};
// a) ==, !=
inline bool operator==(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n==q2.z*q1.n;
}
// Die folgenden Anweisungen wurden mit leichten Änderungen aus utility.h
// übernommen. Die Definitionen aus utility.h gehören zum C++-Standard:
inline bool operator!=(const CBruch& x, const CBruch& y)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
289
290
12 Lösungen
{
return !(x == y);
}
// b) <, <=, >, >=
inline bool operator<(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n < q2.z*q1.n;
}
// Die folgenden Anweisungen wurden mit leichten Änderungen aus utility.h
// übernommen. Die Definitionen aus utility.h gehören zum C++-Standard:
inline bool operator>(const CBruch& x, const CBruch& y)
{
return y < x;
}
inline bool operator<=(const CBruch& x, const CBruch& y)
{
return !(y < x);
}
inline bool operator>=(const CBruch& x, const CBruch& y)
{
return !(x < y);
}
// c) +, -, *, /, skalare Multiplikation mit kürzen (-) == *(-1)
int ggT(int a, int b)
{ // siehe Aufgabe 5.8.5.3
int x=a;
int y=b;
while (y != 0)
{
int r = x%y;
x = y;
y = r;
}
return x; // ggT(a,b)==x;
}
CBruch kuerze_Bruch(CBruch q)
{
int t = ggT(q.z,q.n);
if (t==0) return q;
CBruch result = {q.z/t,q.n/t};
return result;
};
inline CBruch operator+(const CBruch& p, const CBruch& q)
{
CBruch result={p.z*q.n + q.z*p.n , p.n*q.n};
return kuerze_Bruch(result);
};
inline CBruch operator-(const CBruch& p, const CBruch& q)
{
CBruch result={p.z*q.n - q.z*p.n , p.n*q.n};
return kuerze_Bruch(result);
};
inline CBruch operator*(const CBruch& p, const CBruch& q)
{
CBruch result={p.z*q.z,p.n*q.n};
return kuerze_Bruch(result);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.5 Lösungen Kapitel 6
};
inline CBruch operator/(const CBruch& p, const CBruch& q)
{
CBruch result={p.z*q.n,p.n*q.z};
return kuerze_Bruch(result);
};
AnsiString BruchToAnsiString(CBruch q)
{
return IntToStr(q.z)+'/'+IntToStr(q.n);
};
// d) Zum Initialisieren:
CBruch InitCBruch(int z,int n=1)
{ // InitBruch(x): x/1
CBruch b={z,n};
return b;
}
CBruch Test_geom_Reihe1(CBruch p,int n)
{ // summiere die Potenzen: 1 + q + q^2 ... + q^n
CBruch s=InitCBruch(1), q=InitCBruch(1);
for (int i=1; i<=n; i++)
{
q=q*p;
s=s+q;
}
return s;
}
CBruch Test_geom_Reihe2(CBruch p,int n)
{ // Summenformel: (p^(n+1) - 1)/(p-1)
CBruch q=p, Eins=InitCBruch(1);
for (int i=2; i<=n+1; i++) q=q*p;
return (q-Eins)/(p-Eins);
}
#include <math.h> // für pow
void Test_geom_Reihe(int max_z, int max_n)
{
for (int z=-max_z; z<max_z; z++)
for (int n=-max_n; n<max_n; n++)
if (z!=n && n!=0)
{
int N=abs(n);
CBruch p=InitCBruch(z,n); // z/n
CBruch g1=Test_geom_Reihe1(p,N);
CBruch g2=Test_geom_Reihe2(p,N);
// x: setze z/n in die Summenformel ein
CBruch x=kuerze_Bruch(InitCBruch(pow(z,N+1)-pow(n,N+1),pow(n,N)*(z-n)));
if (g1!=g2 || g1!=x) // no news are good news
Form1->Memo1->Lines->Add("p="+BruchToAnsiString(p)+ "
g1="+BruchToAnsiString(g1)+ " g2="+BruchToAnsiString(g2)+ "
x="+BruchToAnsiString(x));
}
}
void __fastcall TForm1::BruchClick(TObject *Sender)
{
Test_geom_Reihe(7,7); // größere Werte: Bereichsüberlauf
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
291
292
12 Lösungen
//----- Aufgabe 3 -----------------------------------------------#include <iostream>
using namespace std;
ostream& operator<<(ostream& f, const CBruch& b)
{
return f<<b.z<<"|"<<b.n;
}
istream& operator>>(istream& f, CBruch& b)
{
char Bruchstrich;
f>>b.z>>Bruchstrich>>b.n;
return f;
}
#include <string>
#include <sstream>
string BruchToStr(CBruch b)
{
ostringstream o;
o<<b;
return o.str();
}
CBruch StrToBruch(string s)
{
istringstream i(s);
CBruch b;
i>>b;
return b;
}
void __fastcall TForm1::Bruch_IO_OpClick(TObject *Sender)
{
CBruch b1={1,2};
Memo1->Lines->Add(BruchToStr(b1).c_str());
string s="3/4";
CBruch b2=StrToBruch(s);
Memo1->Lines->Add(BruchToStr(b2).c_str());
}
12.6 Lösungen Kapitel 7
12.6.1 Aufgaben 7.1
// Datei: d:\Loesungen\kap-7\7.1\Unit0.cpp
// Diese include-Anweisung für Aufgabe b) ausklammern:
// #include "Unit0.h"
double x0;
//
//
double x1=1;
//
//
const double c=3; //
//
a)
b)
a)
b)
a)
b)
Compiler Fehler: Bezeichner 'x0' mehrfach deklariert
Linker Warnung: Public symbol defined in both ...
Compiler Fehler: Bezeichner 'x1' mehrfach deklariert
Keine Meldung des Linkers
Compiler Fehler: Bezeichner 'c' mehrfach deklariert
Keine Meldung des Linkers
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
extern const double d=3;//
//
extern const int e=3;
//
//
void f(double)
//
{
//
x0++;
};
293
a)
b)
a)
b)
a)
b)
Compiler Fehler: Bezeichner 'd' mehrfach deklariert
Keine Meldung des Linkers
Compiler Fehler: Bezeichner 'd' mehrfach deklariert
Keine Meldung des Linkers
Keine Meldung des Compilers
Linker-Fehler: Unresolved external 'f(int)'
// Datei: d:\Loesungen\kap-7\7.1\Unit1.cpp
#include "Unit1.h"
//------------ Aufgaben 7.1 -----------------------------------------//------------ Aufgabe 1 --------------------------------------------// Die Datei Unit0.cpp wurde mit "Projekt|Dem Projekt hinzufügen"
// dem Projekt hinzugefügt.
// Damit die Aufgaben 2 und 3 kompiliert werden können, muss
// das Makro Aufgabe1 auf den Wert 0 gesetzt werden:
// #define Aufgabe1 1
#define Aufgabe1 0
#if Aufgabe1
#include "Unit0.h"
#endif
void call_f()
{
#if Aufgabe1
f(4); // Damit die Funktion aus Unit0.cpp aufgerufen wird.
#endif
}
//------------ Aufgabe 2 --------------------------------------------#include "Unit2.h" // manuell eingefügt
#include "Unit3.h" // manuell eingefügt
void __fastcall TForm1::Button1Click(TObject *Sender)
{
call_f();
MischeKarten(Spielkarten);
if (AnzahlVerteilteKarten+AnzahlKartenProHand<AnzahlSpielkarten)
verteileKarten(Spielkarten, Hand[0]);
Label1->Caption="n="+IntToStr(AnzahlVerteilteKarten);
zeigeHand(Memo1,Hand[0]);
}
//------------ Aufgabe 3 --------------------------------------------#include "\Loesungen\CppLib\GraphUtils.h";
#include "\Loesungen\CppLib\TimeUtils.h";
// b)
// Die Dateien \Loesungen\CppLib\GraphUtils.cpp und
// \Loesungen\CppLib\TimeUtils.cpp werden in das Projekt aufgenommen
// c)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
294
12 Lösungen
// Entweder werden die folgenden obj-Dateien in das Projekt aufgenommen
// oder mit #pragma link eingebunden:
// #pragma link "\\Loesungen\\CppLib\\GraphUtils.obj"
// #pragma link "\\Loesungen\\CppLib\\TimeUtils.obj"
// d)
// Die Bibliothek "Mylib.lib" wird durch
// tlib MyLib +GraphUtils.obj+TimeUtils.obj
// angelegt und in das Projekt aufgenommen (tlib aus dem Verzeichnis
// 'bin' des C++Builders).
#include <math> // für sin
void __fastcall TForm1::Button2Click(TObject *Sender)
{
double Start1=HRTimeInSec();
PlotFunction(-10, 10, sin, Image1);
double End1=HRTimeInSec();
Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
}
12.6.2 Aufgabe 7.1.2
// Datei: d:\Loesungen\kap-7\7.1\Unit3.h
#ifndef Unit3H
#define Unit3H
struct TSpielkarte {
char* Farbe;
int Wert;
};
const int AnzahlKartenProHand=5;
typedef TSpielkarte THand[AnzahlKartenProHand];
const int AnzahlSpielkarten=16;
extern TSpielkarte Spielkarten[AnzahlSpielkarten];
extern int AnzahlVerteilteKarten;
extern int AnzahlMitspieler;
const int MaxAnzahlMitspieler=100;
extern THand Hand[MaxAnzahlMitspieler];
void MischeKarten(TSpielkarte*);
#include <vcl.h> // für TMemo
void zeigeHand(TMemo* Memo, THand H);
inline void verteileKarten(TSpielkarte* K, THand H)
{
for (int i=0; i<AnzahlKartenProHand; i++)
H[i]=K[AnzahlVerteilteKarten+i];
AnzahlVerteilteKarten+=AnzahlKartenProHand;
}
#endif
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
// Datei: d:\Loesungen\kap-7\7.1\Unit3.cpp
#include "Unit3.h"
TSpielkarte Spielkarten[AnzahlSpielkarten] = {
{"Pik",9},{"Pik",10}, {"Pik",11}, {"Pik",12},
{"Herz",9},{"Herz",10}, {"Herz",11}, {"Herz",12},
{"Kreuz",9},{"Kreuz",10}, {"Kreuz",11}, {"Kreuz",12},
{"Karo",9},{"Karo",10}, {"Karo",11}, {"Karo",12}
}; // zur Vereinfachung nur wenig Karten
int AnzahlVerteilteKarten=0;
int AnzahlMitspieler=2;
THand Hand[MaxAnzahlMitspieler];
#include <algorith> // für swap
#include <stdlib>
// für rand()
void MischeKarten(TSpielkarte* K)
{
for (int i=0; i<50;i++)
{
int p=rand()%AnzahlSpielkarten;
int q=rand()%AnzahlSpielkarten;
std::swap(K[p],K[q]);
}
}
void zeigeHand(TMemo* Memo, THand H)
{
Memo->Clear();
for (int i=0; i<AnzahlKartenProHand; i++)
Memo->Lines->Add(AnsiString(H[i].Farbe)+" "+H[i].Wert);
}
// Datei: d:\Loesungen\kap-7\7.1\Unit2.cpp
#include "Unit2.h"
#include "Unit3.h" // manuell eingefügt
TForm2 *Form2;
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
Show();
}
void __fastcall TForm2::Button1Click(TObject *Sender)
{
MischeKarten(Spielkarten);
if (AnzahlVerteilteKarten+AnzahlKartenProHand<AnzahlSpielkarten)
verteileKarten(Spielkarten, Hand[1]);
Label1->Caption="n="+IntToStr(AnzahlVerteilteKarten);
zeigeHand(Memo1,Hand[1]);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
295
296
12 Lösungen
12.6.3 Aufgabe 7.1.3
// Datei: d:\Loesungen\CppLib\GraphUtils.h
#ifndef GraphUtilsH
#define GraphUtilsH
double x_Welt(int px, double x0, double x1, double W);
// transformiert px aus [0,W] in Welt-Koordinaten [x0,x1]
double y_Welt(int py, double y0, double y1, double H);
// transformiert py aus [0,H] in Welt-Koordinaten [y1,y0]
int x_Bildschirm(double x, double x0, double x1, double W);
// transformiert x aus [x0,x1] in Bildkoordinaten [0,W]
int y_Bildschirm(double y, double y0, double y1, double H);
// transformiert y aus [y0,y1] in Bildkoordinaten [H,0]
void Gitternetz(TImage* Image1, double x0,double x1,double dx, int W,
double y0,double y1,double dy, int H);
// void __fastcall TForm1::ClearImageClick(TObject *Sender)
void ClearImage(TImage* Image1);
typedef double(*real_func)(double) ;
void PlotFunction(double x0, double x1, real_func f, TImage* Image1);
#endif
// Datei: d:\Loesungen\CppLib\GraphUtils.cpp
#include "GraphUtils.h"
double x_Welt(int px, double x0, double x1, double W)
{ // transformiert px aus [0,W] in Welt-Koordinaten [x0,x1]
return x0 + px*(x1-x0)/W;
}
double y_Welt(int py, double y0, double y1, double H)
{ // transformiert py aus [0,H] in Welt-Koordinaten [y1,y0]
return y1 + py*(y0-y1)/H;
}
int x_Bildschirm(double x, double x0, double x1, double W)
{ // transformiert x aus [x0,x1] in Bildkoordinaten [0,W]
return (x-x0)*W/(x1-x0);
}
int y_Bildschirm(double y, double y0, double y1, double H)
{ // transformiert y aus [y0,y1] in Bildkoordinaten [H,0]
return (y-y1)*H/(y0-y1);
}
#include <math.h>
#include <vcl\vcl.h>
// für fabs
// für TImage
void Gitternetz(TImage* Image1, double x0,double x1,double dx, int W,
double y0,double y1,double dy, int H)
{
bool xAchse_sichtbar=false;
bool yAchse_sichtbar=false;
int xt=x_Bildschirm(0,x0,x1,W);
if (0<=xt && xt<= W) xAchse_sichtbar=true;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
else xt=0;
int yt=y_Bildschirm(0,y0,y1,H);
if (0<=yt && yt<=H) yAchse_sichtbar=true;
else yt=0;
int x=0, i=0;
Image1->Canvas->Pen->Color = clGray;
while (x<W)
{ // zeichne Parallelen zur y-Achse
Image1->Canvas->MoveTo(x,H);
Image1->Canvas->LineTo(x,0);
Image1->Canvas->TextOut(x,yt,FloatToStrF(x0+i*dx,ffGeneral,4,4));
i++;
x = x_Bildschirm(x0+i*dx,x0,x1,W);
}
int y=0;
i=0;
while (y<H)
{ // zeichne Parallelen zur x-Achse
Image1->Canvas->MoveTo(0,y);
Image1->Canvas->LineTo(W,y);
if (fabs(y1-i*dy) > 0.001) // sonst Schönheitsfehler
Image1->Canvas->TextOut(xt,y,FloatToStrF(y1-i*dy,ffGeneral,4,4));
i++;
y = y_Bildschirm(y1-i*dy,y0,y1,H);
}
// Koordinatenkreuz schwarz nachzeichnen:
Image1->Canvas->Pen->Color = clBlack;
if (xAchse_sichtbar)
{
Image1->Canvas->MoveTo(xt,H);
Image1->Canvas->LineTo(xt,0);
}
if (yAchse_sichtbar)
{
Image1->Canvas->MoveTo(0,yt);
Image1->Canvas->LineTo(W,yt);
}
}
void ClearImage(TImage* Image1)
{
TColor PenAlt = Image1->Canvas->Pen->Color;
Image1->Canvas->Brush->Color=clWhite;
Image1->Canvas->Pen->Color=clWhite;
Image1->Canvas->Rectangle(0,0,Image1->ClientWidth,Image1->ClientHeight);
Image1->Canvas->Pen->Color=PenAlt;
}
typedef double(*real_func)(double) ;
void PlotFunction(double x0, double x1, real_func f, TImage* Image1)
{
// a) das alte Bild löschen:
Image1->Canvas->Brush->Color = clWhite;
Image1->Canvas->Rectangle(0,0,Image1->Width,Image1->Height);
// b) bestimme den minimalen und maximalen Wert von f(x) im Bereich [x0,x1]:
double y0=f(x_Welt(0, x0, x1, Image1->Width)); // y0=min(f(x))
double y1=y0; // y1=max(f(x))
for (int px=1; px<=Image1->Width-/*–*/1; px++)
{
// transformiere px in Welt-Koordinaten
double x=x_Welt(px, x0, x1, Image1->Width);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
297
298
double y=f(x);
if (y<y0) y0=y;
if (y>y1) y1=y;
};
// zeichne die Kurve:
Image1->Canvas->Pen->Color = clRed;
for (int px=0; px<=Image1->Width-/*–*/1; px++)
{
// transformiere px in Welt-Koordinaten
double x=x_Welt(px, x0, x1, Image1->Width);
// transformiere y in Bildkoordinaten
int py=y_Bildschirm(f(x), y0, y1, Image1->Height);
if (px == 0) Image1->Canvas->MoveTo(px,py);
else
Image1->Canvas->LineTo(px,py);
}
// c) zeichne das Gitternetz:
double dx=(x1-x0)/10; // 10 Gitterlinien
double dy=(y1-y0)/10; // 10 Gitterlinien
Gitternetz(Image1, x0,x1,dx, Image1->ClientWidth,
y0,y1,dy, Image1->ClientHeight);
}
// Datei: d:\Loesungen\CppLib\TimeUtils.h
#ifndef TimeUtilsH
#define TimeUtilsH
// double GetHRFrequency();
// extern double HRFrequency;
double HRTimeInSec();
// Diese Routinen können wie folgt verwendet werden:
/*
double Start1=HRTimeInSec();
s=Sum1(n);
double End1=HRTimeInSec();
Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
*/
#endif
// Datei: d:\Loesungen\CppLib\TimeUtils.cpp
#include "TimeUtils.h"
double GetHRFrequency()
{
LARGE_INTEGER f;
BOOL bf=QueryPerformanceFrequency(&f);
if (bf) return f.QuadPart;
else return -1;
}
double HRFrequency=GetHRFrequency();;
double HRTimeInSec()
{
LARGE_INTEGER c;
BOOL bc=QueryPerformanceCounter(&c);
if (bc) return c.QuadPart/HRFrequency;
else return -1;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.6 Lösungen Kapitel 7
299
// Diese Routinen können wie folgt verwendet werden:
/*
double Start1=HRTimeInSec();
s=Sum1(n);
double End1=HRTimeInSec();
Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
*/
12.6.4 Aufgabe 7.2
// Datei: d:\Loesungen\kap-7\7.2\UseDLLExplU.cpp
#include "UseDLLExplU.h"
HINSTANCE hTimeDLL, hGraphDLL;
void __fastcall TForm1::FormCreate(TObject *Sender)
{
hTimeDLL = LoadLibrary("TimeDLL.dll");
if (hTimeDLL==0) ShowMessage("'TimeDLL' nicht geladen");
hGraphDLL = LoadLibrary("GraphDLL.dll");
if (hGraphDLL==0) ShowMessage("'GraphDLL' nicht geladen");
}
#include <math> // für sin
extern double HRFrequency;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
typedef int __stdcall TMin(int X, int Y);
typedef double(*real_func)(double) ;
/*
Das Ergebnis "timedll.def" von "impdef timedll timedll.dll":
LIBRARY
TIMEDLL.DLL
EXPORTS
@HRTimeInSec$qv
___CPPdebugHook
@1
@2
; HRTimeInSec()
; ___CPPdebugHook
Das Ergebnis "graphdll.def" von "impdef graphdll graphdll.dll":
LIBRARY
GRAPHDLL.DLL
EXPORTS
@ClearImage$qp15Extctrls@TImage @6
; ClearImage(Extctrls::TImage *)
@Gitternetz$qp15Extctrls@TImagedddidddi @5
; Gitternetz(Extctrls::TImage *,
double, double, double, int, double, double, double, int)
@PlotFunction$qddpqd$dp15Extctrls@TImage @7
; PlotFunction(double, double,
double (*)(double), Extctrls::TImage *)
@x_Bildschirm$qdddd
@3
; x_Bildschirm(double, double, double,
double)
@x_Welt$qiddd
@1
; x_Welt(int, double, double, double)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
300
@y_Bildschirm$qdddd
double)
@y_Welt$qiddd
___CPPdebugHook
12 Lösungen
@4
; y_Bildschirm(double, double, double,
@2
@8
; y_Welt(int, double, double, double)
; ___CPPdebugHook
*/
typedef double __stdcall THRTimeInSec();
THRTimeInSec* HRTimeInSec=(THRTimeInSec*)
GetProcAddress(hTimeDLL,"@HRTimeInSec$qv");
if (HRTimeInSec== 0) ShowMessage("'HRTimeInSec' nicht gefunden");
double Start1=HRTimeInSec();
typedef void __stdcall TPlotFunction(double x0, double x1, real_func f, TImage*
Image1);
TPlotFunction* PlotFunction=(TPlotFunction*)
GetProcAddress(hGraphDLL,"@PlotFunction$qddpqd$dp15Extctrls@TImage");
if (PlotFunction== 0) ShowMessage("'PlotFunction' nicht gefunden");
PlotFunction(-10, 10, sin, Image1);
double End1=HRTimeInSec();
Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
}
12.6.5 Aufgabe 7.2
// Datei: d:\Loesungen\kap-7\7.2\UseDLLImplU.cpp
#include "UseDLLImplU.h"
//-------------------------------------------------------------------// Die Importdatei wurde dem Projekt hinzugefügt
typedef double(*real_func)(double) ;
__declspec(dllimport) void PlotFunction
(double x0, double x1, real_func f, TImage* Image1);
__declspec(dllimport) double HRTimeInSec();
extern double HRFrequency;
#include <math> // für sin
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Start1=HRTimeInSec();
PlotFunction(-10, 10, sin, Image1);
double End1=HRTimeInSec();
Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
}
12.6.6 Aufgabe 7.3
// Datei: d:\Loesungen\kap-7\7.3\NameSpU.cpp
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.6 Lösungen Kapitel 7
301
#include "NameSpU.h"
//----------- Aufgabe 7.3.1. ----------------------------------------void f(){};
void g(){};
namespace A {
void f(){};
void g(){};
}
namespace B {
using ::f; // a) global f
using A::g; // b) A’s g
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
using namespace B;
f();
// c) calls ::f
//g();
// d) calls A::g
}
//----------- Aufgabe 7.3.2. ----------------------------------------#include <math>
#include "GraphUtils.h"
#include "TimeUtils.h"
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
using namespace TimeUtils;
using namespace GraphUtils;
double Start1=HRTimeInSec();
PlotFunction(-10, 10, sin, Form1->Image1);
double End1=HRTimeInSec();
Form1->Memo1->Lines->Add("t1="+FloatToStr(End1-Start1));
}
//----------- Aufgabe 7.3.3. ----------------------------------------// Man kann die beiden Dateien einfach in einen eigenen Namensbereich aufnehmen:
/*
N1.h:
#ifndef N1H
#define N1H
namespace N1 {
#include "f1.h"
}
#endif
N1.cpp:
#include "N1.h"
namespace N1 {
#include "f1.cpp"
}
Analog für N2.
*/
#include "N1.h"
// außerdem N1.cpp dem Projekt hinzufügen
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
302
#include "N2.h"
12 Lösungen
// außerdem N2.cpp dem Projekt hinzufügen
void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
int i1=N1::N::f(1);
int i2=N2::N::f(1);
}
//--------------------------------------------------------------------
// Datei: d:\Loesungen\kap-7\7.3\f1.cpp
#include "f1.h"
namespace N{
int f(const int i)
{
return i;
}
} // end of namespace
// Datei: d:\Loesungen\kap-7\7.3\f2.cpp
#include "f2.h"
namespace N{
int f(int i)
{
return i+1;
}
} // end of namespace
// Datei: d:\Loesungen\kap-7\7.3\N1.cpp
#include "N1.h"
namespace N1 {
#include "f1.cpp"
}
// Datei: d:\Loesungen\kap-7\7.3\N2.cpp
#include "N2.h"
namespace N2 {
#include "f2.cpp"
}
12.7 Lösungen Kapitel 8
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
12.7.1 Aufgabe 8.1.5 und 8.1.7
// Datei: d:\Loesungen\kap-8\8.1\KlassenU.cpp
#include "KlassenU.h"
// --- Aufgabe 1 ----------------------------------------------------/* Ein Konstruktor hat die Aufgabe, alle Datenelemente einer Klasse so zu
initialisieren, dass sie beim Aufruf einer beliebigen Elementfunktion
in einem definierten Zustand sind.
In einem Destruktor sollen alle Ressourcen wieder freigegeben werden,
die in einem Konstruktor reserviert wurden.
*/
class C {
int n, max;
int* a;
public:
C()
{
max=100;
a=new int[max];
} // Fehler: n wird nicht initialisiert, aber in show_data verwendet
C(int i)
{
n=1;
a=new int[100];
a[0]=i;
}; // Fehler: max wird nicht initialisiert, aber in add_data verwendet
C(int i,int j)
{
n=2;
max=100;
a=new int[100];
a[1]=i;
a[2]=j;
}; // Fehler: max und a[0] werden nicht initialisiert
void add_data(int d)
{
if (n<max-1)
{
n++;
a[n]=d;
}
}
void show_data()
{
for (int i=0; i<n; i++)
Form1->Memo1->Lines->Add(IntToStr(a[i]));
}
// Der Destruktor mit delete[] a fehlt
};
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
//
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
303
304
12 Lösungen
// --- Aufgabe 2 ----------------------------------------------------// a)
class C1 {
int x,y,z;
public:
C1(int x_=0, int y_=0, int z_=0)
{
x=x_; y=y_; z=z_;
}
}; // Da die Elemente nur initialisiert und keine Ressourcen reserviert
// werden, ist kein Destruktor notwendig
// b)
class C2 {
int* x;
public:
C2(int n)
{
x=new int[n];
}
}; // Destruktor mit delete[] x ist notwendig
// c)
#include <fstream>
using namespace std;
class C3 {
ifstream f;
public:
C3(char* FileName)
{
f.open(FileName);
}
}; // Da für die Klasse C3 kein expliziter Konstruktor definiert wird,
// erzeugt der Compiler einen. Dieser ruft die Destruktoren aller
// Elemente von C3 auf, also auch den von f. Da ifstream eine Klasse
// der Standardbibliothek von C++ ist und alle Klassen dieser Bibliothek
// Destruktoren haben, die sich anständig verhalten und alle ihre
// Ressourcen wieder freigeben, ist für C3 kein Destruktor notwendig.
// Ein Destruktor mit close ist aber auch kein Fehler.
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
//
}
// --- Aufgabe 3 ----------------------------------------------------const double pi=3.14159265358979323846;
// a)
class CKreis{
double r;
public:
CKreis(double Radius)
{
r=Radius;
}
double Radius()
{
return r;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
void setzeRadius(double Radius)
{
r=Radius;
}
double Flaeche()
{
return r*r*pi;
}
double Umfang()
{
return 2*r*pi;
}
AnsiString toStr()
{
return "Kreis mit Radius "+FloatToStr(r);
}
};
// b)
class CQuadrat{ // Elementfunktionen außerhalb definieren
double a;
public:
CQuadrat(double Seitenlaenge):a(Seitenlaenge){};
double Seitenlaenge();
void setze_Seitenlaengen(double Seitenlaenge);
double Flaeche();
double Umfang();
AnsiString toStr();
};
double CQuadrat::Seitenlaenge()
{
return a;
}
void CQuadrat::setze_Seitenlaengen(double Seitenlaenge)
{
a=Seitenlaenge;
}
double CQuadrat::Flaeche()
{
return a*a;
}
double CQuadrat::Umfang()
{
return 4*a;
}
AnsiString CQuadrat::toStr()
{
return "Quadrat mit Seitenlänge "+FloatToStr(a);
}
// c)
class CRechteck{
double a,b; // Seitenlängen
public:
CRechteck(double a_, double b_)
{
a=a_;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
305
306
b=b_;
};
double Seitenlaenge_a()
{
return a;
}
double Seitenlaenge_b()
{
return b;
}
void setze_Seitenlaengen(double a_,double b_)
{
a=a_;
b=b_;
}
double Flaeche()
{
return a*b;
}
double Umfang()
{
return 2*(a+b);
}
AnsiString toStr()
{
return "Rechteck mit a="+FloatToStr(a)+" b="+FloatToStr(b);
}
};
// e)
void test_Figuren()
{
CKreis k1(10);
Form1->Memo1->Lines->Add(k1.toStr());
Form1->Memo1->Lines->Add(k1.Umfang());
Form1->Memo1->Lines->Add(k1.Flaeche());
CKreis* pk=new CKreis(10);
Form1->Memo1->Lines->Add(pk->toStr());
Form1->Memo1->Lines->Add(pk->Umfang());
Form1->Memo1->Lines->Add(pk->Flaeche());
CQuadrat q(10);
Form1->Memo1->Lines->Add(q.toStr());
Form1->Memo1->Lines->Add(q.Umfang());
Form1->Memo1->Lines->Add(q.Flaeche());
CQuadrat* pq=new CQuadrat(10);
Form1->Memo1->Lines->Add(pq->toStr());
Form1->Memo1->Lines->Add(pq->Umfang());
Form1->Memo1->Lines->Add(pq->Flaeche());
CRechteck r(10,20);
Form1->Memo1->Lines->Add(r.toStr());
Form1->Memo1->Lines->Add(r.Umfang());
Form1->Memo1->Lines->Add(r.Flaeche());
CRechteck* pr=new CRechteck(7.5,12.5);
Form1->Memo1->Lines->Add(pr->toStr());
Form1->Memo1->Lines->Add(pr->Umfang());
Form1->Memo1->Lines->Add(pr->Flaeche());
}
void __fastcall TForm1::Aufg3Click(TObject *Sender)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
{
test_Figuren();
}
// --- Aufgabe 4 ----------------------------------------------------class CGrundstueck {
char* Anschrift;
double Kaufpreis;
double Flaeche;
public:
CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_)
{
Anschrift = new char[strlen(Anschrift_)+1];
strcpy(Anschrift,Anschrift_);
Kaufpreis=Kaufpreis_;
Flaeche=Flaeche_;
}
~CGrundstueck()
{
delete[] Anschrift;
}
AnsiString toStr() const // const: konstante Elementfunktion, siehe
{
// Abschnitt 8.2.10 "Konstante Klassenelemente und Objekte"
return "Grundstück in "+AnsiString(Anschrift)+ " KP: "+
FloatToStr(Kaufpreis)+ " DM Flaeche: "+FloatToStr(Flaeche);
}
};
void display(const CGrundstueck& g)
{
Form1->Memo1->Lines->Add(g.toStr());
}
/*
In den Klassen CGrundstueck usw. muss im Konstruktor der Speicherplatz für
die Anschrift mit new reserviert und das Argument Anschrift_ in diesen
Speicherbereich kopiert werden, da die Anschrift ein Zeiger auf einen
nullterminierten String ist. Wegen new ist außerdem ein Destruktor
notwendig.
Würde man als Datentyp für die Anschrift eine Stringklasse (z.B. string
oder AnsiString) wählen, wäre diese Klasse einfacher, da auch kein
Destruktor notwendig ist:
class CGrundstueck {
string Anschrift;
double Kaufpreis;
double Flaeche;
public:
CGrundstueck(AnsiString Anschrift_, double Kaufpreis_, double Flaeche_)
{
Anschrift = Anschrift;
Kaufpreis=Kaufpreis_;
Flaeche=Flaeche_;
}
string toStr() const // const: konstante Elementfunktion, siehe
{ // Abschnitt 8.2.10 "Konstante Klassenelemente und Objekte"
return "Grundstück in "+Anschrift+ " KP: "+FloatToStr(Kaufpreis)+
" DM Flaeche: "+FloatToStr(Flaeche);
}
};
Deshalb sollte man Stringklassen immer dem Datentyp char* vorziehen.
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
307
308
12 Lösungen
Es liegt nahe, die gemeinsamen Datenelemente 'Anschrift' und 'Kaufpreis'
der verschiedenen Klassen wiederzuverwenden. Diese Möglichkeit wird im
Kapitel über Vererbung behandelt.
Die folgenden Klassen sind ähnlich aufgebaut wie die Klasse CGrundstueck
und zeigen, welche Ersparnis an Arbeits- und Testaufwand die Stringklassen anstelle von nullterminierten Strings hätten.
*/
class CEigentumswohnung {
char* Anschrift;
double Kaufpreis;
double Wohnflaeche;
int AnzahlZimmer;
public:
CEigentumswohnung(char* Anschrift_, double Kaufpreis_,
double Wohnflaeche_, int AnzahlZimmer_)
{
Anschrift = new char[strlen(Anschrift_)+1];
strcpy(Anschrift,Anschrift_);
Kaufpreis=Kaufpreis_;
Wohnflaeche=Wohnflaeche_;
AnzahlZimmer=AnzahlZimmer_;
}
~CEigentumswohnung()
{
delete[] Anschrift;
}
AnsiString toStr() const
{
return "ETW in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis)+ " DM
Wohnfläche: "+FloatToStr(Wohnflaeche)+" Zimmer: "+IntToStr(AnzahlZimmer);
}
};
void display(const CEigentumswohnung& e)
{
Form1->Memo1->Lines->Add(e.toStr());
}
class CEinfamilienhaus {
char* Anschrift;
double Kaufpreis;
double Wohnflaeche;
double Grundstuecksgroesse;
int AnzahlZimmer;
public:
CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
double Grundstuecksgroesse_, int AnzahlZimmer_)
{
Anschrift = new char[strlen(Anschrift_)+1];
strcpy(Anschrift,Anschrift_);
Kaufpreis=Kaufpreis_;
Wohnflaeche=Wohnflaeche_;
Grundstuecksgroesse=Grundstuecksgroesse_;
AnzahlZimmer=AnzahlZimmer_;
}
~CEinfamilienhaus()
{
delete[] Anschrift;
}
AnsiString toStr() const
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
309
{
return "EFH in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis)+
" DM Wohnfläche: "+FloatToStr(Wohnflaeche)+" Anzahl Zimmer: "+
IntToStr(AnzahlZimmer)+" Grundstück: "+FloatToStr(Grundstuecksgroesse);
}
};
void display(const CEinfamilienhaus& e)
{
Form1->Memo1->Lines->Add(e.toStr());
}
class CSchloss {
char* Anschrift;
double Kaufpreis;
int AnzahlSchlossgeister;
public:
CSchloss(char* Anschrift_, double Kaufpreis_,
int AnzahlSchlossgeister_)
{
Anschrift = new char[strlen(Anschrift_)+1];
strcpy(Anschrift,Anschrift_);
Kaufpreis=Kaufpreis_;
AnzahlSchlossgeister=AnzahlSchlossgeister_;
}
~CSchloss()
{
delete[] Anschrift;
}
AnsiString toStr() const
{
return "Schloss in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis)
+" DM Schlossgeister: "+IntToStr(AnzahlSchlossgeister);
}
};
void display(const CSchloss& g)
{
Form1->Memo1->Lines->Add(g.toStr());
}
class CGewerbeobjekt {
char* Anschrift;
double Kaufpreis;
double Nutzflaeche;
char* Nutzungsart; // z. B. Büro, Fabrik, Restaurant
public:
CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, double Nutzflaeche_,
char* Nutzungsart_)
{
Anschrift = new char[strlen(Anschrift_)+1];
strcpy(Anschrift,Anschrift_);
Kaufpreis=Kaufpreis_;
Nutzflaeche=Nutzflaeche_;
Nutzungsart=new char[strlen(Nutzungsart_)+1];
strcpy(Nutzungsart,Nutzungsart_);
}
~CGewerbeobjekt()
{
delete[] Anschrift;
delete[] Nutzungsart;
}
AnsiString toStr() const
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
310
12 Lösungen
AnsiString s="Gewerbeobjekt in "+AnsiString(Anschrift)+ " KP: "+
FloatToStr(Kaufpreis)+" DM Nutzfläche: "+
FloatToStr(Nutzflaeche)+" Nutzungsart: "+Nutzungsart;
return s;
}
};
void display(const CGewerbeobjekt& g)
{
Form1->Memo1->Lines->Add(g.toStr());
}
void test_Immo()
{
// CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_)
CGrundstueck g("Tübingen",100000,400);
display(g);
// CEigentumswohnung(char* Anschrift_, double Kaufpreis_,
//
double Wohnflaeche_, int AnzahlZimmer_)
CEigentumswohnung etw("Stuttgart",500000,120,5);
display(etw);
// CEinfamilienhaus(char* Anschrift_, double Kaufpreis_,
//
double Wohnflaeche_, double Grundstuecksgroesse_, int AnzahlZimmer_)
CEinfamilienhaus efh("Kiel",300000,150,900,5);
display(efh);
CSchloss s("Neuschwanstein",30000000,5);
display(s);
// CGewerbeobjekt(char* Anschrift_, double Kaufpreis_,
//
double Nutzflaeche_, char* Nutzungsart_)
CGewerbeobjekt gew("München",400000,200,"Lagerhalle");
display(gew);
}
void __fastcall TForm1::Aufg4Click(TObject *Sender)
{
test_Immo();
}
// --- Aufgabe 5 ----------------------------------------------------namespace N_Aufgabe5 {
void display(AnsiString s, int i=-1)
{
if (i>=0) s=s+IntToStr(i);
Form1->Memo1->Lines->Add(s);
}
class C{
int x;
public:
C (int x_=0)
{ // Beim Aufruf ohne Argument ein Standardx=x_;
// konstruktor
display(" Konstruktor: ", x);
}
~C ()
{
display(" Destruktor: ",x);
}
};
void f1(C c)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
{
display("
};
in f1(): Werteparameter");
void f2(const C& c)
{
display(" in f2(): Referenzparameter");
};
C f3(int i)
{
display(" in f3(): return-Wert");
return C(i);
};
void test1()
{
C x(1);
C* z=new C(2);
display("vor x=C(3)");
x=C(3);
display("vor f1(4)");
f1(4);
display("vor f2(x)");
f2(x);
display("vor f3(5)");
x=f3(5);
delete z;
display("Ende von test()");
}
/*
Die Anweisungen der Funktion test1 erzeugen die eingerückten
Meldungen:
C x(1);
Konstruktor: 1
C* z=new C(2);
Konstruktor: 2
display("vor x=C(3)");
vor x=C(3)
x=C(3);
Konstruktor: 3
Destruktor: 3
display("vor f1(4)");
vor f1(4)
f1(4);
Konstruktor: 4
in f1(): Werteparameter
Destruktor: 4
display("vor f2(x)");
vor f2(x)
f2(x);
in f2(): Referenzparameter
display("vor f3(5)");
vor f3(5)
x=f3(5);
in f3(): return-Wert
Konstruktor: 5
Destruktor: 5
delete z;
Destruktor: 2
display("Ende von test()");
Ende von test()
}
Destruktor: 5
*/
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
311
312
12 Lösungen
void test2()
{
display("vor p1");
C* p1=new C[2];
delete[] p1;
display("vor p2");
C* p2=new C[2];
delete p2;
display("vor p3");
C* p3=new C;
delete[] p3;
display("vor p4");
C* p4=new C(4);
delete[] p4;
display("Ende von test()");
}
/*
b) Die Funktion test2 erzeugt unter anderem die folgende Ausgabe:
vor p1
Konstruktor: 0
Konstruktor: 0
Destruktor: 0
Destruktor: 0
vor p2
Konstruktor: 0
Konstruktor: 0
Destruktor: 0
Die Anweisungen nach display(„vor p3") sind falsch, da ein mit new[]
reservierter Speicherbereich mit delete (und nicht mit delete[]) wieder
freigegeben wird, bzw. ein mit new reservierter Speicherbereich mit delete[] (und
nicht mit delete).
Gibt man bei dem Ausdruck nach new runde Klammern an, sind die Werte in den
Klammern Argumente für den Konstruktor und nicht etwa Arraygrenzen. Deswegen muss
nach diesem Aufruf delete und nicht delete[] aufgerufen werden.
*/
} // end of namespace N_Aufgabe5
void __fastcall TForm1::Aufg5Click(TObject *Sender)
{
N_Aufgabe5::test1();
N_Aufgabe5::test2();
}
// --- Aufgabe 6 ----------------------------------------------------class MeinString {
char* s; // Zeiger auf nullterminierten String
int n;
// Länge des Strings
public:
MeinString(const char* p) // 1
{ // p muss auf einen nullterminierten String zeigen
n=strlen(p);
s=new char[n+1];
strcpy(s,p);
};
MeinString(char c)
// 2
{ // kann auch mit einem int-Argument aufgerufen werden
n=1;
s=new char[n+1];
*s=c;
*(s+1)='\0';
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
const char* c_str()
{ // 'strcpy(s1.c_str(),"abc")' geht ohne "const", aber nicht mit.
// Da ein solcher Aufruf die Klasseninvariante zerstören kann,
// ist hier const notwendig.
return s;
}
void replace(int from, char* x); // für Aufgabe 8.1.7.2
};
void test_6()
{
MeinString s("abc");
Form1->Memo1->Lines->Add(IntToStr(strlen(s.c_str())));
}
void __fastcall TForm1::Aufg6Click(TObject *Sender)
{
test_6();
}
// --- Aufgabe 7 ----------------------------------------------------namespace N_Aufgabe_7 {
class C {
typedef int T;
T x;
public:
C(T x_)
{
x=x_;
}
T& data() // T data() wäre zu langsam
{
return x;
}
};
void test()
{
C c(1);
c.data()=17;
// Da der Funktionswert von data ein Referenztyp ist, kann man mit dieser
// public Funktion den Wert des private Elements x verändern und damit die
// Einschränkung des Zugriffsrechts außer Kraft setzen.
// Diesen vermutlich nicht beabsichtigten Effekt kann man verhindern,
// indem man den Rückgabetyp der Funktion data als konstanten
// Referenzparameter definiert:
// Mit einer Elementfunktion, die eine Referenz zurückgibt, können auch
// Datenelemente von Objekten angesprochen werden, die nicht mehr
// existieren.
}
} // end of namespace N_Aufgabe_7
void __fastcall TForm1::Aufg7Click(TObject *Sender)
{
N_Aufgabe_7::test();
}
// --- Aufgabe 8 ----------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
313
314
class CHRTimer {
LARGE_INTEGER HRFrequency, start, end;
bool HRTimerSupported;
public:
CHRTimer()
{
HRTimerSupported=
QueryPerformanceFrequency(&HRFrequency);
QueryPerformanceCounter(&start);
}
void Start()
{
QueryPerformanceCounter(&start);
}
void End()
{
QueryPerformanceCounter(&end);
}
AnsiString TimeStr()
{
if (HRTimerSupported) // QuadPart hat den Datentyp int64
return FloatToStr((double(end.QuadPart)-start.QuadPart)/
HRFrequency.QuadPart);
else
return "No High Resolution Timer supported";
}
};
struct Ct {
int a[1000];
Ct(int i_) { a[0]=i_;};
};
int ft(const Ct& x)
{
return x.a[0];
}
void time_test(int n)
{
CHRTimer t1,t2;
t1.Start();
Ct c(1);
for (int i=0; i<n; i++)
ft(c);
t1.End();
Form1->Memo1->Lines->Add(t1.TimeStr());
t2.Start();
for (int i=0; i<n; i++)
ft(Ct(1));
t2.End();
Form1->Memo1->Lines->Add(t2.TimeStr());
}
void __fastcall TForm1::Aufg8Click(TObject *Sender)
{
time_test(1000);
time_test(10000);
time_test(100000);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
315
}
// --- Aufgabe 9 ----------------------------------------------------//
//
Mit diesem Trick lassen sich alle Vorteile des Zugriffsrechts
private zunichte machen.
//
#undef private ist hier unpassend.
//
//
//
Wenn eine Klasse aber nicht alle Elementfunktionen zur Verfügung
stellt, die ein Anwender benötigt (also nicht vollständig ist),
kann dieser Trick aber doch eine gewisse Berechtigung haben.
// --- Aufgabe 10 ---------------------------------------------------class MeinStack
{ // ein Stack mit einem dynamisch reservierten Array
typedef int T; // damit der Datentyp leicht geändert werden kann
int SPtr;
// Index des "obersten" Elements
T* Array;
// Zeiger auf das erste Element
int Max; // Anzahl der reservierten Arrayelemente
public:
MeinStack(int s):SPtr(-1),Max(s) { Array=new T[s];}
~MeinStack()
{ delete [] Array; }
void push(T a) { Array[++SPtr]=a; }
T pop()
{ return Array[SPtr--]; }
bool full()
{ return SPtr>=Max; }
bool empty()
{ return SPtr<0; }
};
void test_stack(int n)
{
Form1->Memo1->Lines->Add("test_stack n="+IntToStr(n));
MeinStack s(n);
for (int i=0; i<n; i++)
s.push(i);
while (!s.empty())
Form1->Memo1->Lines->Add(s.pop());
}
void __fastcall TForm1::Aufg9Click(TObject *Sender)
{
for (int i=0; i<10; i++)
test_stack(i);
}
// --- Aufgabe 11 ---------------------------------------------------class MeineListe {
typedef int T;
// T: der Datentyp der "Nutzdaten"
struct list_node { // Knoten einer verketteten Liste.
T data;
// Kann auch lokal in der Klasse
list_node* next; // definiert werden.
};
list_node *first,*last;
public:
MeineListe():first(0),last(0) {};
void insert(T data)
{ /* Erzeugt einen neuen Listen-Knoten und fügt diesen
nach last ein. Last zeigt anschließend auf das
letzte und first auf das erste Element der Liste. */
list_node* tmp=new list_node;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
316
tmp->data = data;
tmp->next = 0;
if (last == 0) first = tmp;
else last->next = tmp;
last = tmp;
}
12 Lösungen
//
//
//
//
2.
3.2 von insert
1.
3.
void show()
{
for (list_node* tmp=first; tmp != 0; tmp = tmp->next)
Form1->Memo1->Lines->Add(tmp->data);
}
~MeineListe()
{ // verwendet die private Hilfsfunktion von unten
while (first!=0) erase(first);
last=0;
}
void test_MeineListe(int n)
{
Form1->Memo1->Lines->Add("test_MeineListe n="+IntToStr(n));
MeineListe L;
for (int i=0; i<n; i++)
L.insert(i);
L.show();
}
private:
void erase(list_node*& position)
{ // list.h
if (position==0) return; // oder Fehlermeldung
list_node* tmp = position;
position = position->next;
delete tmp;
};
};
void __fastcall TForm1::Aufg10Click(TObject *Sender)
{
MeineListe list;
for (int i=0; i<10; i++)
list.test_MeineListe(i);
}
// --- Aufgabe 8.1.7 ------------------------------------------------AnsiString s2b1, s2b2;
void MeinString::replace(int from, char* x)
{ // ersetze die Zeichen ab 'from' durch die von x
int xn=strlen(x); // Vorbedingung für strlen: x zeigt auf einen
// nullterminierten String
for (int i=0; from+i<n && i<xn; i++)
s[from+i]=x[i];
s2b1=
"Mit der Vorbedingung 'from>=0' ist sichergestellt, dass der Index "
"'from+i' nie negativ ist. Die Schleifenbedingung garantiert, dass s "
"und x nie mit Indizes oberhalb ihrer Länge angesprochen werden. Da der "
"reservierte Bereich auf den s zeigt, ebensowenig verändert wird wie s "
"und n, bleibt die Schleifeninvariante erhalten.";
s2b2=
"Deshalb ist die Vorbedingung für diese Funktion: 'from>=0' und 'x "
"zeigt auf einen nullterminierten String'.";
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
void __fastcall TForm1::Aufg_817_12Click(TObject *Sender)
{
AnsiString s1a=
"Bei den geometrischen Figuren müssen die Seitenlängen bzw. der "
"Radius >= 0 sein.";
AnsiString s1b=
"Damit die Fläche und der Umfang immer stimmen, müssen sie in jeder "
"Elementfunktion aktualisiert werden. Das ist aber aufwendiger "
"als die Lösung mit den Funktionen.";
Memo1->Lines->Add("1. a)");
Memo1->Lines->Add(s1a);
Memo1->Lines->Add("1. b)");
Memo1->Lines->Add(s1b);
MeinString s("abcd");
s.replace(2,"12345");
Memo1->Lines->Add("2.");
Memo1->Lines->Add(s2b1);
Memo1->Lines->Add(s2b2);
}
12.7.2 Aufgabe 8.2.2
// Datei: d:\Loesungen\kap-8\8.2\Aufg2U.cpp
// --- Aufgabe 1 ----------------------------------------------------class E {
public:
E()
{
Form1->Memo1->Lines->Add("Standardkonstruktor");
}
E(int)
{
Form1->Memo1->Lines->Add("int-Konstruktor");
}
};
class C {
E e1,e2;
public:
C() { }
C(int i):e1(i) { }
C(int i,int j):e1(i),e2(j) { }
};
void Konstruktoren()
{
// Die folgenden Definitionen erzeugen die Meldungen:
C c0;
// Standardkonstruktor
C c1(1);
// Standardkonstruktor
// int-Konstruktor
C c2(1,2); // Standardkonstruktor
// int-Konstruktor
// int-Konstruktor
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
317
318
12 Lösungen
}
// --- Aufgabe 2 ----------------------------------------------------class C2DPunkt{
public: // Für CRechteck1 müssen x und y public sein
double x,y;
public:
C2DPunkt(const double& x_, const double& y_):x(x_),y(y_) { }
C2DPunkt(const C2DPunkt& pos):x(pos.x),y(pos.y) { }
// Später wird gezeigt, daß dieser zweite Konstruktor ein
// Copy-Konstruktor ist, der automatisch erzeugt wird und deswegen nicht
// definiert werden muß.
AnsiString toStr()
{
return "("+FloatToStr(x)+"|"+FloatToStr(y)+")";
}// { z. B. (2,345|3,45678) }
};
class C2DKreis{
double r;
C2DPunkt Position;
public:
C2DKreis(double Radius=1, double x=0, double y=0):
r(Radius), Position(x,y) { } // Reihenfolge der Deklaration
C2DKreis(double Radius, C2DPunkt pos):
r(Radius), Position(pos) { } // Reihenfolge der Deklaration
/*
Alternativ zu diesen beiden Konstruktoren sind auch die folgenden
möglich. Beim zweiten dieser Konstruktoren wird ein temporäres Objekt
als Default-Argument verwendet:
C2DKreis(double Radius, double x, double y):
r(Radius), Position(x,y) { } // Reihenfolge der Deklaration
C2DKreis(double Radius=0, C2DPunkt pos=C2DPunkt(0,0)):
r(Radius), Position(pos) { } // Reihenfolge der Deklaration
*/
AnsiString toStr()
{
return "Kreis mit Radius "+FloatToStr(r)+" in "+Position.toStr();
}
};
void Kreis()
{
C2DKreis k1(1,2,3);
Form1->Memo1->Lines->Add(k1.toStr());
C2DKreis k2(4);
Form1->Memo1->Lines->Add(k2.toStr());
C2DKreis k3;
Form1->Memo1->Lines->Add(k3.toStr());
C2DPunkt p(6,7);
C2DKreis k5(5,p);
Form1->Memo1->Lines->Add(k5.toStr());
C2DKreis k6(8,C2DPunkt(9,10));
Form1->Memo1->Lines->Add(k6.toStr());
}
// --- Aufgabe 3 ----------------------------------------------------class CRechteck1 {
C2DPunkt LinksOben; // Eckpunkt links oben
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
double a,b; // Seitenlängen
public:
CRechteck1(C2DPunkt Mittelpunkt, double a_, double b_):a(a_), b(b_),
LinksOben(Mittelpunkt.x-a/2, Mittelpunkt.y-b/2){ }
AnsiString toStr()
{
return FloatToStr(LinksOben.x)+"|"+FloatToStr(LinksOben.y);
}
AnsiString Kommentar()
{
return
"Die Elemente eines Objekts werden in der Reihenfolge initialisiert, "
"in der sie in der Klasse aufgeführt werden. Deshalb wird zuerst "
"LinksOben initialisiert, und dann erst a und b. Linksoben verwendet "
"dazu die bisher nicht initialisierten Werte a und b.\n\n"
"Wenn man die Elemente in einer Initialisiererliste in der Reihenfolge "
"ihrer Definition in der Klasse aufführt, ist die Gefahr eines solchen "
"Missverständnisses geringer.\n\n"
"Die fehlerhafte Initialisierung in der Klasse CRechteck1 vermeidet "
"man, indem man die Reihenfolge der Datenelemente ändert.";
}
};
class CRechteck2 { // Reihenfolge der Deklarationen vertauscht
double a,b; // Seitenlängen
C2DPunkt LinksOben; // Eckpunkt links oben
public:
CRechteck2(C2DPunkt Mittelpunkt, double a_,double b_):
a(a_),b(b_), LinksOben(Mittelpunkt.x-a/2, Mittelpunkt.y-b/2){ }
AnsiString toStr()
{
return FloatToStr(LinksOben.x)+"|"+
FloatToStr(LinksOben.y);
}
};
void Rechteck1()
{
C2DPunkt mp(100,100);
CRechteck1 r1(mp, 10, 20);
Form1->Memo1->Lines->Add(r1.toStr());
Form1->Memo1->Lines->Add(r1.Kommentar());
CRechteck2 r2(mp, 10, 20);
Form1->Memo1->Lines->Add(r2.toStr());
}
// --- Aufgabe 4 ----------------------------------------------------void MeinString_Konstr()
{
Form1->Memo1->Lines->Add(
"Da dieser Standardkonstruktor die anderen Konstruktoren ergänzen soll, "
"kann im Destruktor nicht mehr entschieden werden, ob der reservierte "
"Speicher mit delete oder mit delete[] freigegeben werden soll." );
}
12.7.3 Aufgabe 8.2.4
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
319
320
12 Lösungen
// Datei: d:\Loesungen\kap-8\8.2\Aufg4U.cpp
//--- Aufgabe 1 -----------------------------------------------------/*
Für Zähler und Nenner über 7 liegen bei diesen Rechnungen
Zwischenergebnisse außerhalb des Bereichs von int. Mit dem
Datentyp __int64 anstelle von int können diese Rechnungen
auf Zähler und Nenner bis 14 erweitert werden.
*/
// c)
int ggT(int x, int y)
{
if (x<0) x=-x;
if (y<0) y=-y;
while (y != 0)
{
int r = x%y;
x = y;
y = r;
}
return x; // ggT(x,y)==x;
}
class CBruch {
int z,n; // z: Zähler, n: Nenner
public:
CBruch(int z_, int n_=1):z(z_),n(n_)
{
int t = ggT(z,n);
if (t==0)
{
ShowMessage("t=0");
return;
}
z=z/t;
n=n/t;
};
friend inline bool operator==(const CBruch& q1, const CBruch& q2);
friend inline bool operator<(const CBruch& q1, const CBruch& q2);
friend CBruch operator+(const CBruch& p, const CBruch& q);
friend CBruch operator-(const CBruch& p, const CBruch& q);
friend CBruch operator*(const CBruch& p, const CBruch& q);
friend CBruch operator/(const CBruch& p, const CBruch& q);
AnsiString toStr()
{
// Mit __int64 FloatToStr anstelle IntToStr
return FloatToStr(z)+"/"+FloatToStr(n);
}
};
// a)
inline bool operator==(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n==q2.z*q1.n;
}
// Die folgenden Anweisungen wurden mit leichten Änderungen aus
// include\utility.h übernommen. Die Definitionen aus utility.h
// gehören zum C++-Standard.
// Da die folgende Funktion nicht auf die Elemente von CBruch zugreift,
// muß sie kein friend sein:
inline bool operator!=(const CBruch& x, const CBruch& y)
{
return !(x == y);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
// b)
inline bool operator<(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n < q2.z*q1.n;
}
// Auch die folgenden Anweisungen wurden mit leichten Änderungen
// aus utility übernommen.
inline bool operator>(const CBruch& x, const CBruch& y)
{
return y < x;
}
inline bool operator<=(const CBruch& x, const CBruch& y)
{
return !(y < x);
}
inline bool operator>=(const CBruch& x, const CBruch& y)
{
return !(x < y);
};
CBruch operator+(const CBruch& p, const CBruch& q)
{
int g=ggT(p.n,q.n);
return CBruch(p.z*(q.n/g) + q.z*(p.n/g) , p.n*(q.n/g));
};
CBruch operator-(const CBruch& p, const CBruch& q)
{
int g=ggT(p.n,q.n);
return CBruch(p.z*(q.n/g) - q.z*(p.n/g) , p.n*(q.n/g));
};
CBruch operator*(const CBruch& p, const CBruch& q)
{
return CBruch (p.z*q.z,p.n*q.n);
};
CBruch operator/(const CBruch& p, const CBruch& q)
{
return CBruch(p.z*q.n,p.n*q.z);
};
// d)
const CBruch Eins(1,1);
CBruch Test_geom_Reihe1(CBruch p,int n)
{ // summiere die Potenzen: 1 + p + p^2 ... + p^n
CBruch s=Eins, q=Eins;
for (int i=1; i<=n; i++)
{
q=q*p;
s=s+q;
}
return s;
}
CBruch Test_geom_Reihe2(CBruch p,int n)
{ // Summenformel: (p^(n+1) - 1)/(p-1)
CBruch q=Eins;
for (int i=1; i<=n+1; i++) q=q*p;
// q=p^(n+1)
return (q-Eins)/(p-Eins);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
321
322
int pot(int x, int n)
{
int p=1;
for (int i=0; i<n; i++) p=p*x;
return p;
}
void Test_geom_Reihe(int max_z, int max_n)
{
for (int z=-max_z; z<max_z; z++)
for (int n=-max_n; n<max_n; n++)
if (n!=0)
{
int N=abs(n);
CBruch p(z,n); // p=z/n
if (p!=CBruch(1))
{ // p=1/1 ergibt Bruchdivision durch 0 in g2
CBruch g1=Test_geom_Reihe1(p,N);
CBruch g2=Test_geom_Reihe2(p,N);
// x: setze z/n in die Summenformel ein
CBruch x=CBruch(pot(z,N+1)-pot(n,N+1),pot(n,N)*(z-n));
if (g1!=g2 || g1!=x) // no news are good news
Form1->Memo1->Lines->Add("p="+p.toStr()+" g1="+g1.toStr()+
" g2="+g2.toStr()+ " x="+x.toStr());
}
}
}
//--- Aufgabe 2 -----------------------------------------------------class AssozContainer {
// Dieser Container ist eine starke Vereinfachung und
// zeigt nur, wie man den Indexoperator so überladen kann,
// dass er sich wie in std::map verhält.
int n; // Anzahl der Elemente im Container
typedef AnsiString T1;
typedef AnsiString T2;
public:
struct Paar {
T1 first;
T2 second;
};
Paar a[100]; // nur zur Vereinfachung so einfach
AssozContainer():n(0) {};
// a)
T1& operator[](T2 s)
{ // lineares Suchen: sehr einfach
for (int i=0; i<n; i++)
if (a[i].first==s)
return a[i].second;
a[n].first=s;
return a[n++].second;
}
void show_all()
{
for (int i=0; i<n; i++)
Form1->Memo1->Lines->Add(a[i].first+": "+a[i].second);
}
// b)
class iterator {
Paar* Position;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
public:
iterator(Paar* pos):Position(pos) { }
bool operator!= (const iterator& y)
{
return Position != y.Position;
}
iterator& operator++(int)
{
Position++;
return *this;
}
Paar& operator* ()
{
return *Position;
}
Paar* operator-> ()
{
return Position;
}
};
iterator begin()
{
return a;
}
iterator end()
{
return a+n;
}
};
void test_AssozCont()
{
AssozContainer a;
Form1->Memo1->Lines->Add("1. ");
a.show_all();
a["Luigi Mafiosi"]="[email protected]";
Form1->Memo1->Lines->Add("2. ");
a.show_all();
a["Karl Erbschleicher"]="[email protected]";
Form1->Memo1->Lines->Add("3. ");
a.show_all();
a["Luigi Mafiosi"]="[email protected]"; // palermo.net war zu langsam
Form1->Memo1->Lines->Add("4. ");
a.show_all();
Form1->Memo1->Lines->Add(a["Karl Erbslaicher"]); // Schreibfehler
Form1->Memo1->Lines->Add("5. ");
a.show_all();
}
12.7.4 Aufgabe 8.2.6
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
323
324
12 Lösungen
// Datei: d:\Loesungen\kap-8\8.2\Aufg6U.cpp
// --- Aufgabe 1 ----------------------------------------------------void SpezEltFktNotwendig()
{
const int n=10;
AnsiString a[n]={
"a) Nicht notwendig, da alle Klassen keine Zeiger enthalten.",
"b) Nicht notwendig, da CBruch keine Zeiger enthält.",
"c) Notwendig, da alle Klassen Zeiger enthalten.",
"d) Mit string anstelle char* ist keine dieser Funktionen notwendig.",
"e) Mit char[100] anstelle char* ist keine dieser Funktionen notwendig.",
"f) Falls das Array mit new angelegt wird, sind diese Funktionen notwendig.",
"g) Mit einem vector sind diese Funktionen nicht notwendig.",
"Diese Beispiele zeigen insbesondere, dass man sich viel Arbeit "
"sparen kann, wenn man Stringklassen anstelle von nullterminierten "
"Strings verwendet.",
"",
"Wenn man alles richtig macht, ist es kein Fehler, diese Funktionen "
"selbst zu definieren, obwohl sie auch vom Compiler erzeugt werden. "
"Allerdings haben die selbst definierten Funktionen keine Vorteile "
"gegenüber den vom Compiler erzeugten. Sie haben aber den Nachteil, "
"dass sie bei jeder Änderung der Klasse angepasst werden müssen, "
"was leicht vergessen werden kann. Am besten ist es, wenn man alle"
"Klassen ohne Zeiger definieren kann."};
for (int i=0; i<n; i++)
Form1->Memo1->Lines->Add(a[i]);
}
// --- Aufgabe 2 ----------------------------------------------------void display(AnsiString s, int i=-1)
{
if (i>=0) s=s+IntToStr(i);
Form1->Memo1->Lines->Add(s);
}
class C{
int x;
public:
C (int x_=0)
{ // Beim Aufruf ohne Argument ein Standardx=x_;
// konstruktor
display(" Konstruktor: ", x);
}
C (const C& c)
{
x=c.x;
display(" Kopierkonstruktor: ", x);
}
C& operator=(const C& c)
{
x=c.x;
display(" operator=: ", x);
return *this;
}
~C ()
{
display("
}
Destruktor: ",x);
friend int f3(C c);
friend int f4(const C& c);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
325
friend C operator+(const C& c1,const C& c2);
};
C f1(int i)
{
return C(i);
}
C f2(int i)
{
C tmp(i);
return tmp;
}
int f3(C c)
{
return c.x;
}
int f4(const C& c)
{
return c.x;
}
void test1()
{
display("vor C x=C(1)");
C x=C(1); //
Konstruktor: 1
C y=x;
//
Kopierkonstruktor:
display("vor x=y");
x=y;
//
operator=: 1
display("vor C z(x)");
C z(x);
//
Kopierkonstruktor:
display("vor f1(2)");
f1(2);
//
Konstruktor: 2
//
Destruktor: 2
display("vor f2(3)");
f2(3);
//
Konstruktor: 3
//
Kopierkonstruktor:
//
Destruktor: 3
//
Destruktor: 3
display("vor f3(4)");
f3(4);
//
Konstruktor: 4
//
Destruktor: 4
display("vor f3(x)");
f3(x);
//
Kopierkonstruktor:
//
Destruktor: 1
display("vor f4(4)");
f4(4);
//
Konstruktor: 4
//
Destruktor: 4
display("vor f4(x)");
f4(x);
//
keine Ausgabe
display("Ende von test1");
}
//
Destruktor: 1
//
Destruktor: 1
//
Destruktor: 1
1
1
3
1
class D {
C c1;
C c2;
};
void test2()
{
display("vor D d1");
D d1;
// Konstruktor: 0
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
326
// Konstruktor: 0
display("vor D d2=d1");
D d2=d1; // Kopierkonstruktor: 0
// Kopierkonstruktor: 0
display("vor d2=d1");
d2=d1;
// operator=: 0
// operator=: 0
display("nach d2=d1");
}
// Destruktor: 0
// Destruktor: 0
// Destruktor: 0
// Destruktor: 0
// --- Aufgabe 3 ----------------------------------------------------class CBruch {
int z,n; // z: Zähler, n: Nenner
public:
CBruch(int z_, int n_=1):z(z_),n(n_){
int t = ggT(z,n); // ggT: siehe Aufgabe 8.2.4
if (t==0)
{
ShowMessage("t=0");
return;
}
z=z/t;
n=n/t;
};
friend inline bool operator==(const CBruch& q1, const CBruch& q2);
friend inline bool operator<(const CBruch& q1, const CBruch& q2);
CBruch& operator+=(const CBruch& q)
{
int g=ggT(n,q.n);
z=z*(q.n/g) + q.z*(n/g);
n=n*(q.n/g);
return *this;
};
CBruch& operator-=(const CBruch& q)
{
int g=ggT(n,q.n);
z=z*(q.n/g) - q.z*(n/g);
n=n*(q.n/g);
return *this;
};
CBruch& operator*=(const CBruch& q)
{
z=z*q.z;
n=n*q.n;
return *this;
};
CBruch& operator/=(const CBruch& q)
{
z=z*q.n;
n=n*q.z;
return *this;
};
AnsiString toStr()
{
// Mit __int64 FloatToStr anstelle IntToStr
return IntToStr(z)+"/"+IntToStr(n);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
};
// a)
inline bool operator==(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n==q2.z*q1.n;
}
// Die folgenden Anweisungen wurden mit leichten Änderungen aus
// utility übernommen. Die Definitionen aus utility gehören
// zum C++-Standard:
inline bool operator!=(const CBruch& x, const CBruch& y)
{
return !(x == y);
}
// b)
inline bool operator<(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n < q2.z*q1.n;
}
// Auch die folgenden Anweisungen wurden mit leichten Änderungen
// aus utility übernommen.
inline bool operator>(const CBruch& x, const CBruch& y)
{
return y < x;
}
inline bool operator<=(const CBruch& x, const CBruch& y)
{
return !(y < x);
}
inline bool operator>=(const CBruch& x, const CBruch& y)
{
return !(x < y);
};
inline CBruch& operator+(const CBruch& p, const CBruch& q)
{
CBruch tmp=p;
return tmp+=q;;
};
inline CBruch& operator-(const CBruch& p, const CBruch& q)
{
CBruch tmp=p;
return tmp-=q;;
};
inline CBruch operator*(const CBruch& p, const CBruch& q)
{
CBruch tmp=p;
return tmp*=q;;
};
inline CBruch operator/(const CBruch& p, const CBruch& q)
{
CBruch tmp=p;
return tmp/=q;;
};
// d)
CBruch Test_geom_Reihe1(CBruch p,int n)
{ // summiere die Potenzen: 1 + p + p^2 ... + p^n
CBruch s(1), q(1);
for (int i=1; i<=n; i++)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
327
328
12 Lösungen
q=q*p;
s=s+q;
}
return s;
}
CBruch Test_geom_Reihe2(CBruch p,int n)
{ // Summenformel: (p^(n+1) - 1)/(p-1)
CBruch q(1);
for (int i=1; i<=n+1; i++) q=q*p;
// q=p^(n+1)
return (q-CBruch(1))/(p-CBruch(1));
}
int pot(int x, int n)
{
int p=1;
for (int i=0; i<n; i++) p=p*x;
return p;
}
void Test_geom_Reihe(int max_z, int max_n)
{
for (int z=-max_z; z<max_z; z++)
for (int n=-max_n; n<max_n; n++)
if (n!=0)
{
int N=abs(n);
CBruch p(z,n); // p=z/n
if (p!=CBruch(1))
{ // p=1/1 ergibt Bruchdivision durch 0 in g2
CBruch g1=Test_geom_Reihe1(p,N);
CBruch g2=Test_geom_Reihe2(p,N);
// x: setze z/n in die Summenformel ein
CBruch x=CBruch(pot(z,N+1)-pot(n,N+1),pot(n,N)*(z-n));
if (g1!=g2 || g1!=x) // no news are good news
Form1->Memo1->Lines->Add("p="+p.toStr()+" g1="+g1.toStr()+
" g2="+g2.toStr()+ " x="+x.toStr());
}
}
}
// --- Aufgabe 4 ----------------------------------------------------void MemCpy()
{
AnsiString s="Das kann zu flachen Kopien führen.";
Form1->Memo1->Lines->Add(s);
}
// --- Aufgabe 5 ----------------------------------------------------#include "\Loesungen\CppUtils\TimeUtils.cpp"
class C1 {
public:
C1():x(0),y(0) {}
C1(int a):x(a),y(a) {}
int x,y;
};
void test_PrefixPostfixClick()
{
const int n=100000;
typedef vector<int> T;
T v(n);
for (int i=0; i<n; i++) v.push_back(i);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
329
CHRTimer t1,t2;
int s=0;
t1.Start();
for (T::iterator i=v.begin(); i!=v.end(); i++)
s+=*i;;
t1.End();
t2.Start();
for (T::iterator i=v.begin(); i!=v.end(); ++i)
s+=*i;;
t2.End();
CHRTimer t10,t20;
t10.Start();
for (int i=0; i<n; i++)
s+=i;;
t10.End();
t20.Start();
for (int i=0; i<n; ++i)
s+=i;;
t20.End();
Form1->Memo1->Lines->Add("Postfix:"+t1.TimeStr());
Form1->Memo1->Lines->Add("Präfix: "+t2.TimeStr());
Form1->Memo1->Lines->Add("int Postfix:"+t10.TimeStr());
Form1->Memo1->Lines->Add("int Präfix: "+t20.TimeStr());
/*
Projekt|Optionen|Compiler: Endgültige Version
Mit "typedef vector<C1> T;" ergaben sich die folgenden Zeiten:
Postfix:0,00211703179738179
Präfix: 0,00133927823128111
Postfix:0,00205333646222699
Präfix: 0,00133927823128111
Mit "typedef vector<int> T;" sind dagegen überraschenderweise
die Postfix-Operationen schneller:
Postfix:0,00133927823128111
Präfix: 0,0020365745319231
Postfix:0,00140548785598149
Präfix: 0,00200640305737609
*/
}
// --- Aufgabe 6 ----------------------------------------------------class Festkomma64 {
__int64 i;
public:
Festkomma64(): i(0) { }
Festkomma64(int i_): i(i_*10000) { }
Festkomma64(double d_): i((d_+0.000005)*10000) { }
Festkomma64(const Festkomma64& f_): i(f_.i) { }
AnsiString toStr()
{
AnsiString s=FloatToStr(i);
while (s.Length()<5) s = "0"+s;
s.Insert(",",s.Length()-3);
return s;
}
Festkomma64& operator+=(Festkomma64 a)
Festkomma64& operator-=(Festkomma64 a)
{i+=a.i; return *this;}
{i-=a.i; return *this; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
330
Festkomma64& operator*=(Festkomma64 a)
Festkomma64& operator/=(Festkomma64 a)
12 Lösungen
{i*=a.i; i /= 10000; return *this; }
{i*=10000; i/=a.i; return *this; }
friend bool operator<=(Festkomma64 a,Festkomma64 b);
};
bool operator<=(Festkomma64 a,Festkomma64 b)
{
return a.i<=b.i;
}
Festkomma64 operator+(Festkomma64 a,Festkomma64 b)
{
return a+=b;
}
Festkomma64 operator*(Festkomma64 a,Festkomma64 b)
{
return a*=b;
}
Festkomma64 operator-(Festkomma64 a,Festkomma64 b)
{
return a-=b;
}
Festkomma64 operator/(Festkomma64 a,Festkomma64 b)
{
return a/=b;
}
Festkomma64 est(Festkomma64 x)
{ // Einkommensteuertarif nach § 32a (1)
Festkomma64 est;
x=(x/54)*54;
if (x <= 12365) est = 0;
else if (x <= 58643)
{
Festkomma64 y = (x-12312)/10000;
est = (91.19*y+2590)*y;
}
else if (x <= 120041)
{
Festkomma64 z = (x-58590)/10000;
est = (151.91*z + 3434)*z +13938;
}
else est = 0.53*x -22843;
return 10000*(est/10000);
};
// Der Datentyp Currency ist im wesentlichen so definiert wie hier der
// Datentyp Festkomma64.
//typedef Festkomma64 T;
typedef Currency T;
T Est(T x)
{ // Einkommensteuertarif nach § 32a (1)
T est;
x=(x/54)*54;
if (x <= 12365) est = 0;
else if (x <= 58643)
{
T y = (x-12312)/10000;
est = (91.19*y+2590)*y;
}
else if (x <= 120041)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
331
T z = (x-58590)/10000;
est = (151.91*z + 3434)*z +13938;
}
else est = 0.53*x -22843;
return 10000*(est/10000);
};
void EstVergl(Festkomma64 a,Festkomma64 b)
{
Form1->Memo1->Lines->Add(a.toStr()+": "+est(a).toStr()+" Soll="+b.toStr());
}
void test_Est()
{
EstVergl(10800,0);
EstVergl(16200,1020);
EstVergl(21600,2484);
EstVergl(27000,4000);
EstVergl(32400,5570);
EstVergl(37800,7193);
EstVergl(43200,8870);
EstVergl(48600,10599);
EstVergl(54000,12381);
EstVergl(59400,14217);
EstVergl(64800,16129);
EstVergl(70200,18129);
EstVergl(75600,20218);
EstVergl(81000,22396);
EstVergl(86400,24663);
EstVergl(91800,27018);
EstVergl(97200,29461);
EstVergl(102600,31994);
EstVergl(108000,34615);
EstVergl(113400,37324);
EstVergl(118800,40123);
}
// --- Aufgabe 7 ----------------------------------------------------void KonstrAufrufeVermeiden()
{
AnsiString s="Indem man sie private deklariert. ";
Form1->Memo1->Lines->Add(s);
}
// --- Aufgabe 8 ----------------------------------------------------C operator+(const C& c1,const C& c2)
{ // friend in der Klasse C
display(" operator+: ", c1.x+c2.x);
return C(c1.x+c2.x);
}
void test3()
{
C s1,s2,s3,s4;
display("1. vor +
s1=C(1)+C(3);
display("2. vor +
C x=C(5),y=C(7);
s2=x+y;
display("3. vor +
s3=C(9);
s3=s3+C(11);
display("4. vor +
");
");
");
");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
332
12 Lösungen
s4=x;
s4=s4+y;
display("Ende von test");
}
12.7.5 Aufgabe 8.2.13
// Datei: d:\Loesungen\kap-8\8.2\CQuadrat.h
#ifndef CQuadratH
#define CQuadratH
class CQuadrat{ // Elementfunktionen außerhalb definieren
double a;
public:
CQuadrat(double Seitenlaenge):a(Seitenlaenge){};
double Seitenlaenge() const;
void setze_Seitenlaenge(double Seitenlaenge);
double Flaeche()
const;
double Umfang()
const;
AnsiString toStr() const;
};
#endif
12.7.6 Aufgabe 8.2.13
// Datei: d:\Loesungen\kap-8\8.2\CQuadrat.cpp
namespace N_Aufg13U {
#include "CQuadrat.h"
double CQuadrat::Seitenlaenge() const
{
return a;
}
void CQuadrat::setze_Seitenlaenge(double Seitenlaenge)
{
a=Seitenlaenge;
}
double CQuadrat::Flaeche()
{
return a*a;
}
const
double CQuadrat::Umfang()
{
return 4*a;
}
const
AnsiString CQuadrat::toStr() const
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
return "Quadrat mit Seitenlänge "+FloatToStr(a);
}
} // end of namespace N_Aufg13U
12.7.7 Aufgabe 8.2.13
// Datei: d:\Loesungen\kap-8\8.2\Aufg13U.cpp
//----- Aufgabe 1 und 2 ---------------------------------------------#include "CQuadrat.h"
#include "CKreis.h"
// "CQuadrat.cpp" und "CKreis.cpp" werden mit "Projekt|Dem Projekt
// hinzufügen" dem Projekt hinzugefügt
void Aufg1_2()
{
CQuadrat q(10);
Form1->Memo1->Lines->Add(q.toStr());
Form1->Memo1->Lines->Add(q.Umfang());
Form1->Memo1->Lines->Add(q.Flaeche());
CQuadrat* pq=new CQuadrat(10);
Form1->Memo1->Lines->Add(pq->toStr());
Form1->Memo1->Lines->Add(pq->Umfang());
Form1->Memo1->Lines->Add(pq->Flaeche());
CKreis k(10);
Form1->Memo1->Lines->Add(k.toStr());
Form1->Memo1->Lines->Add(k.Umfang());
Form1->Memo1->Lines->Add(k.Flaeche());
CKreis* pk=new CKreis(10);
Form1->Memo1->Lines->Add(pk->toStr());
Form1->Memo1->Lines->Add(pk->Umfang());
Form1->Memo1->Lines->Add(pk->Flaeche());
}
//----- Aufgabe 3 ---------------------------------------------------void Aufg_3()
{
/*
3. Bei der Änderung einer Klassendefinition (also insbesondere der in der
Klasse definierten inline-Funktion) muss der Compiler alle Dateien
übersetzen, die diese Datei mit #include verwenden.
Wenn dagegen die Funktionsdefinition einer außerhalb der Klasse
definierten Funktion geändert wird, muss nur diese Datei neu kompiliert
werden.
Dieser geringere Aufwand beim Kompilieren hat allerdings eine längere
Laufzeit des Programms zur Folge, da die Funktionen dann nicht mehr
inline sind.
Auch wenn diese Überlegungen prinzipiell eher gegen als für inlineFunktionen sprechen, können diese Nachteile oft vernachlässigt werden.
Bei inline-Funktionen mit wenig Anweisungen kann der bei einer Expansion
erzeugte Code sogar kleiner sein als der für einen Funktionsaufruf.
*/
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
333
334
12 Lösungen
//----- Aufgabe 4 ---------------------------------------------------class C {
static int count_;
public:
static int count() { return count_; }
void* operator new(size_t size)
{
count_++;
return ::new C;
};
void* operator new[](size_t size)
{
count_++;
return ::operator new(size);
};
void operator delete(void *p)
{
count_--;
::delete(p);
};
void operator delete[](void *p)
{
count_--;
::delete[](p);
};
};
int C::count_=0;
void Aufg_4()
{
C* p1=new C;
delete p1;
C* p2=new C[10];
delete[] p2;
}
//----- Aufgabe 5 ---------------------------------------------------class Singleton {
static Singleton* instance_;
public:
static Singleton* Instance();
int data; // besser private und Zugriffsfunktionen
private: // protected, falls Singleton als Basisklasse verwendet werden soll
Singleton(){data=17;};
};
Singleton* Singleton::Instance()
{
if (instance_==0)
instance_=new Singleton();
return instance_;
};
Singleton* Singleton::instance_=0;
Singleton* p1=Singleton::Instance();
void Aufg_Singleton()
{
Singleton* p2=Singleton::Instance();
// p2->data=19;
Form1->Memo1->Lines->Add(IntToStr(p1->data)); // p1->data=19
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
// falls p1 ein gewöhnliches globales Objekt wäre, hätte die
// globale Variable den Wert 17
}
12.7.8 Aufgaben 8.3
// Datei: d:\Loesungen\kap-8\8.3\VererbU.cpp
#include "VererbU.h"
//-------------------------------------------------------------------// Aufgaben 8.3.5
//-------------------------------------------------------------------//------ Aufgabe 1 --------------------------------------------------class C {
int i,j;
public:
C(int x,int y): i(x),j(y)
{
Form1->Memo1->Lines->Add("Konstruktor C");
}
C(): i(0),j(0)
{
Form1->Memo1->Lines->Add("Standardkonstruktor C");
}
~C()
{
Form1->Memo1->Lines->Add("Destruktor C");
}
};
class D : public C {
int k,a,b;
C c;
public:
D(int x):C(x,17),a(x),b(0),c(x,1),k(19)
{
Form1->Memo1->Lines->Add("Konstruktor-1 D");
}
D(int x,int y, int z):C(x,y),a(1),b(2),c(x,y),k(z)
{
Form1->Memo1->Lines->Add("Konstruktor-2 D");
}
~D()
{
Form1->Memo1->Lines->Add("Destruktor D");
}
};
class E : public D {
int m;
C c;
public:
E(int x,int y, int z):D(x,3,z),m(z)
{
Form1->Memo1->Lines->Add("Konstruktor E");
}
~E() {
Form1->Memo1->Lines->Add("Destruktor E");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
335
336
12 Lösungen
}
};
void __fastcall
{
C c(1,2);
//
D d(1,2,3); //
//
//
E e(1,2,3); //
//
//
//
//
TForm1::Aufg_1Click(TObject *Sender)
}
Destruktor
Destruktor
Destruktor
Destruktor
Destruktor
Destruktor
Destruktor
Destruktor
Destruktor
//
//
//
//
//
//
//
//
//
Konstruktor C
Konstruktor C
Konstruktor C
Konstruktor-2 D
Konstruktor C
Konstruktor C
Konstruktor-2 D
Standardkonstruktor C
Konstruktor E
E
C
D
C
C
D
C
C
C
//------ Aufgabe 2 --------------------------------------------------class C1DPunkt {
protected: // private wäre besser. Dazu wäre aber eine Funktion notwendig,
double x;
// die C1DPunkt::x zurückgibt
AnsiString toStr()
{
return FloatToStr(x);
}
public:
C1DPunkt(double x_):x(x_)
{ }
void display()
{
Form1->Memo1->Lines->Add(toStr());
}
};
class C2DPunkt:public C1DPunkt {
protected: // private wäre besser
double y;
AnsiString toStr()
{
return FloatToStr(x)+"|"+FloatToStr(y);
}
public:
C2DPunkt(double x_,double y_):C1DPunkt(x_),y(y_)
void display()
{
Form1->Memo1->Lines->Add(toStr());
}
};
{ }
class C3DPunkt : public C2DPunkt {
double z;
AnsiString toStr()
{
return FloatToStr(x)+"|"+FloatToStr(y)+"|"+FloatToStr(z);
}
public:
C3DPunkt(double x_,double y_,double z_):C2DPunkt(x_,y_),z(z_) {
void display()
{
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
Form1->Memo1->Lines->Add(toStr());
}
};
void test_Punkte()
{
C1DPunkt p1(1);
C2DPunkt p2(1,2);
C3DPunkt p3(1,2,3);
p1.display();
p2.display();
p3.display();
}
void __fastcall TForm1::Aufg_2Click(TObject *Sender)
{
test_Punkte();
}
//------ Aufgabe 3 --------------------------------------------------// a)
// Es liegt nahe, alle gemeinsamen Elemente der Klassen in
// einer gemeinsamen Basisklasse zusammenzufassen:
class CImmobilie {
protected: // Nur zur Vereinfachung - private wäre besser
char* Anschrift;
double Kaufpreis;
public:
CImmobilie(char* Anschrift_, double Kaufpreis_):Kaufpreis(Kaufpreis_)
{
Anschrift = new char[strlen(Anschrift_)+1];
strcpy(Anschrift,Anschrift_);
}
~CImmobilie()
{
delete[] Anschrift;
}
AnsiString toStr() const
{
return " in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis) + " DM ";
}
CImmobilie(const CImmobilie& c);
// für b)
CImmobilie& operator=(const CImmobilie& rhs); // für b)
};
void display(const CImmobilie* i, TMemo* Memo)
{
Memo->Lines->Add(i->toStr());
}
class CGrundstueck : public CImmobilie {
double Flaeche;
public:
CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_):
CImmobilie(Anschrift_,Kaufpreis_),Flaeche(Flaeche_) { }
AnsiString toStr() const
{
return "Grundstück"+CImmobilie::toStr()+ " Fläche: "+FloatToStr(Flaeche)+
" qm";
}
};
void display(const CGrundstueck* i, TMemo* Memo)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
337
338
12 Lösungen
{
Memo->Lines->Add(i->toStr());
}
class CWohnImmobilie : public CImmobilie {
// Die Notwendigkeit für diese Klasse ergibt sich nicht direkt aus der
// Aufgabenstellung.
protected: // Nur zur Vereinfachung - private wäre besser
double Wohnflaeche;
int AnzahlZimmer;
public:
CWohnImmobilie(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
int AnzahlZimmer_): CImmobilie(Anschrift_,Kaufpreis_), Wohnflaeche
(Wohnflaeche_), AnzahlZimmer(AnzahlZimmer_){}
AnsiString toStr() const
{
return CImmobilie::toStr()+" Wohnfläche: "+
FloatToStr(Wohnflaeche)+" qm Zimmer: "+IntToStr(AnzahlZimmer);
}
};
void display(const CWohnImmobilie* i, TMemo* Memo)
{
Memo->Lines->Add(i->toStr());
}
class CEigentumswohnung : CWohnImmobilie {
public:
CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double
Wohnflaeche_, int AnzahlZimmer_):
CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_){}
AnsiString toStr() const
{
return "ETW"+CWohnImmobilie::toStr();
}
};
void display(const CEigentumswohnung* i, TMemo* Memo)
{
Memo->Lines->Add(i->toStr());
}
class CEinfamilienhaus :CWohnImmobilie {
double Grundstuecksgroesse;
public:
CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
double Grundstuecksgroesse_, int AnzahlZimmer_):
CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_),
Grundstuecksgroesse(Grundstuecksgroesse_){}
AnsiString toStr() const
{
return "EFH"+CWohnImmobilie::toStr();
}
};
void display(const CEinfamilienhaus* i, TMemo* Memo)
{
Memo->Lines->Add(i->toStr());
}
class CGewerbeobjekt : CImmobilie {
double Nutzflaeche;
char* Nutzungsart; // z. B. "Büro", "Fabrik"
public:
CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, double Nutzflaeche_,
char* Nutzungsart_):CImmobilie(Anschrift_,Kaufpreis_),
Nutzflaeche(Nutzflaeche_)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
339
Nutzungsart=new char[strlen(Nutzungsart_)+1];
strcpy(Nutzungsart,Nutzungsart_);
}
~CGewerbeobjekt()
{
delete[] Nutzungsart;
}
AnsiString toStr() const
{
return "Gewerbeobjekt"+ CImmobilie::toStr()+
" Nutzfläche: "+FloatToStr(Nutzflaeche)+" qm Nutzungsart: "+Nutzungsart;
}
CGewerbeobjekt(const CGewerbeobjekt& c);
// für b)
CGewerbeobjekt& operator=(const CGewerbeobjekt& rhs); // für b)
};
void display(const CGewerbeobjekt* i, TMemo* Memo)
{
Memo->Lines->Add(i->toStr());
}
void test()
{
// CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_)
CGrundstueck g("Tübingen",100000,400);
display(&g, Form1->Memo1);
// CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
int AnzahlZimmer_)
CEigentumswohnung etw("Stuttgart",500000,120,5);
display(&etw, Form1->Memo1);
// CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
double Grundstuecksgroesse_, int AnzahlZimmer_)
CEinfamilienhaus efh("Kiel",300000,150,900,5);
display(&efh, Form1->Memo1);
// CGewerbeobjekt(char* Anschrift_, double Kaufpreis_, double Nutzflaeche_,
char* Nutzungsart_)
CGewerbeobjekt gew("München",400000,200,"Lagerhalle");
display(&gew, Form1->Memo1);
}
// b)
AnsiString b=
"Alle Klassen, die Zeiger enthalten, benötigen einen Destruktor, "
"Copy-Konstruktor und Zuweisungsoperator.";
CImmobilie::CImmobilie(const CImmobilie& x):Kaufpreis(x.Kaufpreis)
{
Anschrift = new char[strlen(x.Anschrift)+1];
strcpy(Anschrift,x.Anschrift);
}
CImmobilie& CImmobilie::operator=(const CImmobilie& rhs)
{
if (this!=&rhs)
{
delete Anschrift;
Anschrift = new char[strlen(rhs.Anschrift)+1];
strcpy(Anschrift,rhs.Anschrift);
Kaufpreis=rhs.Kaufpreis;
}
return *this;
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
340
12 Lösungen
CGewerbeobjekt::CGewerbeobjekt(const CGewerbeobjekt&
x):CImmobilie(x),Nutzflaeche(x.Nutzflaeche)
{
Nutzungsart= new char[strlen(x.Nutzungsart)+1];
strcpy(Nutzungsart,x.Nutzungsart);
}
CGewerbeobjekt& CGewerbeobjekt::operator=(const CGewerbeobjekt& rhs)
{
if (this!=&rhs)
{
CImmobilie::operator=(rhs); // Aufruf von this->C::operator=
delete Nutzungsart;
Nutzungsart = new char[strlen(rhs.Nutzungsart)+1];
strcpy(Nutzungsart,rhs.Nutzungsart);
Kaufpreis=rhs.Kaufpreis;
}
return *this;
};
// c)
AnsiString c=
"Diese Hierarchie ist einfacher als die Definition der einzelnen Klassen, "
"bei denen keine Elemente wiederverwendet werden. In dieser Hierarchie "
"ist auch nur für die Klassen ein explizit definierter Copy-Konstruktor, "
"Zuweisungsoperator und Destruktor notwendig, die Zeiger enthalten. Bei "
"Klassen, die Zeiger aus den Basisklassen erben, reicht der Aufruf dieser "
"Funktionen aus der Basisklasse aus. ";
// d)
AnsiString d1=
"Wenn diese Klassen Strings der Klassen string bzw. AnsiString statt "
"Zeigern auf nullterminierte Strings enthalten, ist für keine dieser "
"Klassen ein explizit definierter Copy-Konstruktor, Zuweisungsoperator "
"und Destruktor notwendig.";
AnsiString d2=
"Das erspart die Definition dieser Funktionen und damit viel Arbeit. "
"Deshalb sollte man auf Klassen mit Zeigern möglichst verzichten und "
"Stringklassen anstelle von Zeigern auf nullterminierte Strings verwenden.";
void __fastcall TForm1::Aufg_3Click(TObject *Sender)
{
test();
Memo1->Lines->Add("b)");
Memo1->Lines->Add(b);
Memo1->Lines->Add("c)");
Memo1->Lines->Add(c);
Memo1->Lines->Add("d)");
Memo1->Lines->Add(d1);
Memo1->Lines->Add("");
Memo1->Lines->Add(d2);
}
//------ Aufgabe 4 --------------------------------------------------namespace N_Aufgabe_4 {
// a)
class CQuadrat1{
double a;
public:
CQuadrat1(double a_):a(a_) {}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
};
class CRechteck1:public CQuadrat1{
double b;
public:
CRechteck1(double a_, double b_):CQuadrat1(a_),b(b_) {}
};
// b)
class CRechteck2{
double a,b;
public:
CRechteck2(double a_,double b_):a(a_),b(b_){};
};
class CQuadrat2:public CRechteck2 {
public:
CQuadrat2(double a_):CRechteck2(a_,a_) {}
};
// c)
// Bei der Hierarchie b) benötigt jedes Quadrat doppelt soviel
// Speicherplatz wie bei der unter b).
} // end of namespace N_Aufgabe_4
void __fastcall TForm1::Aufg_4Click(TObject *Sender)
{
// N_Aufgabe_4::test();
}
//------ Aufgabe 5 --------------------------------------------------namespace N_Aufgabe_5 {
struct C {
C()
{
Form1->Memo1->Lines->Add("Standardkonstruktor C");
}
C(const C& c)
{
Form1->Memo1->Lines->Add("Copy-Konstruktor C");
}
C& operator=(const C&)
{
Form1->Memo1->Lines->Add("operator= C");
return *this;
}
~C()
{
Form1->Memo1->Lines->Add("Destruktor C");
}
};
struct D:C {
};
void test()
{
D d1;
//
D d2=d1; //
d2=d1;
//
}
//
//
Standardkonstruktor C
Copy-Konstruktor C
operator= C
Destruktor C
Destruktor C
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
341
342
12 Lösungen
} // end of namespace N_Aufgabe_5
void __fastcall TForm1::Aufg_5Click(TObject *Sender)
{
N_Aufgabe_5::test();
}
//------ Aufgabe 6 --------------------------------------------------void __fastcall TForm1::Aufg_6Click(TObject *Sender)
{
AnsiString s=
"Da Konstruktoren nicht vererbt werden, stehen diese in der abgeleiteten "
"Klasse nicht zur Verfügung. Bei einer Klasse mit vielen nützlichen "
"Konstruktoren wie z.B. einer Stringklasse kann das ein gravierender "
"Nachteil sein. Man kann diese zwar in der abgeleiteten Klasse wieder "
"alle definieren. Es ist aber fraglich, ob sich dieser Aufwand lohnt und "
"man nicht besser beim Aufruf eines vorhandenen Konstruktors eine "
"Funktion wie StrToInt oder StrToFloat verwendet.";
Memo1->Lines->Add("6.");
Memo1->Lines->Add(s);
}
//-------------------------------------------------------------------// Aufgaben 8.3.9
//-------------------------------------------------------------------//------ Aufgabe 1 --------------------------------------------------void __fastcall TForm1::Aufg9_1Click(TObject *Sender)
{
AnsiString a=
"Ein Automobil hat einen Motor und Räder. Vermutlich wird nie jemand "
"sagen, dass ein Automobil ein Motor oder ein Rad ist.";
AnsiString b=
"Eine Katze bzw. ein Hund ist Tier. Von einer 'hat ein'-Beziehung kann "
"man höchstens dann reden, wenn ein Hund eine Katze gefressen hat oder "
"eine Katze einen Hund hält.";
AnsiString c=
"Ein Landfahrzeug bzw. ein Wasserfahrzeug ist ein Fahrzeug. Ein Automobil "
"ist ein Landfahrzeug, ein Segelboot ist ein Wasserfahrzeug. Von einer "
"'hat ein'-Beziehung kann man höchstens dann reden, wenn ein "
"Wasserfahrzeug ein Landfahrzeug transportiert.";
AnsiString d=
"Hier sind beide Sichtweisen möglich: Ein Abteilungsleiter und eine "
"Sekretärin sind Mitarbeiter. Ein Abteilungsleiter hat eine Sekretärin "
"und Mitarbeiter. Im ersten Fall werden die Mitarbeiter einer Firma "
"modelliert. Im zweiten Fall wird eine Abteilung modelliert. Es soll "
"auch Sekretärinnen geben, die einen Abteilungsleiter haben.";
Memo1->Lines->Add("1.");
Memo1->Lines->Add("a)");
Memo1->Lines->Add(a);
Memo1->Lines->Add("b)");
Memo1->Lines->Add(b);
Memo1->Lines->Add("c)");
Memo1->Lines->Add(c);
Memo1->Lines->Add("d)");
Memo1->Lines->Add(d);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
}
//------ Aufgabe 2 --------------------------------------------------namespace N_Aufgabe_9_2 {
// a)
class CQuadrat1{
double a;
public:
CQuadrat1(double a_):a(a_) {}
double Flaeche() { return a*a; }
double Umfang() { return 4*a; }
};
class CRechteck1:public CQuadrat1{
double b;
public:
CRechteck1(double a_, double b_):CQuadrat1(a_),b(b_) {}
};
// b)
class CRechteck2{
double a,b;
public:
CRechteck2(double a_,double b_):a(a_),b(b_){};
double Flaeche() { return a*b; }
double Umfang() { return 2*(a+b); }
};
class CQuadrat2:public CRechteck2 {
public:
CQuadrat2(double a_):CRechteck2(a_,a_) {}
};
} // end of namespace N_Aufgabe_4
void __fastcall TForm1::Aufg9_2Click(TObject *Sender)
{
AnsiString s=
"Bei der Ableitung eines Quadrats von einem Rechteck sind die aus der "
"Basisklasse geerbten Funktionen Flaeche und Umfang auch in der "
"abgeleiteten Klasse korrekt. Bei der umgekehrten Ableitung ist das "
"nicht richtig.";
Memo1->Lines->Add("2.");
Memo1->Lines->Add(s);
}
//------ Aufgabe 3 --------------------------------------------------void __fastcall TForm1::Aufg9_3Click(TObject *Sender)
{
AnsiString a=
"Ein Grundstück, eine Eigentumswohnung, ein Schloss und ein Gewerbeobjekt "
"sind eine Immobilie. Ein Einfamilienhaus ist dagegen keine "
"Eigentumswohnung. ";
Memo1->Lines->Add("3.");
Memo1->Lines->Add("a)");
Memo1->Lines->Add(a);
AnsiString b=
"Durch eine Klasse wie CWohnimmobilie, von der eine Eigentumswohnung "
"und ein Einfamilienhaus abgeleitet werden. ";
Memo1->Lines->Add("b)");
Memo1->Lines->Add(b);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
343
344
12 Lösungen
//------ Aufgabe 4 --------------------------------------------------void __fastcall TForm1::Aufg9_4Click(TObject *Sender)
{
AnsiString s1=
"Bei der Funktion Abstand stellt sich die Frage, was der Abstand eines "
"Quadrats vom Ursprung überhaupt sein soll: Soll vom Mittelpunkt, der "
"linken unteren Ecke oder einem anderen Punkt gemessen werden?";
AnsiString s2=
"Bei der Funktion drehe erwartet man normalerweise, dass die Seiten des "
"Quadrats nach einer Drehung um z.B. 70 Grad nicht mehr parallel zu den "
"Koordinatenachsen sind. ";
Memo1->Lines->Add("4.");
Memo1->Lines->Add(s1);
Memo1->Lines->Add(s2);
}
//------ Aufgabe 5 --------------------------------------------------void __fastcall TForm1::Aufg9_5Click(TObject *Sender)
{
AnsiString s=
"Mit einer Konversion der Basisklasse in eine abgeleitete Klasse wäre der "
"Aufruf einer Funktion der Basisklasse über ein Objekt der abgeleiteten "
"Klasse möglich. Eine solche Funktion muss aber überhaupt nicht "
"existieren. ";
Memo1->Lines->Add("5.");
Memo1->Lines->Add(s);
}
//-------------------------------------------------------------------// Aufgaben 8.3.11
//-------------------------------------------------------------------#include <math.h>
namespace N_Aufg_8_3_11 {
//------ Aufgabe 1 --------------------------------------------------class CKreis{
double r;
static const double pi;
public:
CKreis(double Radius)
{
r=Radius;
}
double Flaeche()
{
return r*r*pi;
}
double Umfang()
{
return 2*r*pi;
}
AnsiString toStr()
{
return "Kreis mit Radius "+FloatToStr(r);
}
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
const double CKreis::pi=3.14;
class C2DPunkt {
double x,y;
public:
C2DPunkt(double x_,double y_):x(x_),y(y_) { }
double Abstand()
{
return sqrt(x*x+y*y);
}
AnsiString toStr()
{
return FloatToStr(x)+"|"+FloatToStr(y);
}
};
// a)
namespace N_a {
class C2DKreis:public CKreis, public C2DPunkt {
public:
C2DKreis(double Radius=1, double x=0, double y=0):
CKreis(Radius), C2DPunkt(x,y) { }
C2DKreis(double Radius, C2DPunkt pos):
CKreis(Radius), C2DPunkt(pos) { }
/* Eine in dieser Klasse definierte Funktion toStr verdeckt die Funktionen
toStr der Basisklasse:
AnsiString toStr()
{
return CKreis::toStr()+" Pos: "+C2DPunkt::toStr();
}
*/
};
AnsiString s1=
"C2DKreis erbt alle public Elementfunktionen der Basisklassen, also "
"Flaeche, Umfang und toStr. Ein Aufruf von toStr über ein Objekt der "
"Klasse C2DKreis ist dann mehrdeutig.";
AnsiString s2=
"Diese Hierarchie ist nicht geeignet, da ein Kreis mit einer Position "
"zwar ein Kreis, aber kein Punkt ist.";
} // end of namespace N_a
// b)
namespace N_b {
class C2DKreis {
CKreis Kreis;
C2DPunkt Position;
public:
C2DKreis(double Radius=1, double x=0, double y=0):
Kreis(Radius), Position(x,y) { }
C2DKreis(double Radius, C2DPunkt pos):
Kreis(Radius), Position(pos) { }
// Eine in dieser Klasse definierte Funktion toStr verdeckt keine
// Funktionen der Basisklasse:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
345
346
12 Lösungen
AnsiString toStr()
{
return Kreis.toStr()+" Pos: "+Position.toStr();
}
};
AnsiString s1=
"Da hier keine Vererbung verwendet wird, erbt C2DKreis keine "
"Elementfunktionen der Klasse CKreis und kann die Funktionen dieser "
"Klasse CKreis auch nicht wiederverwenden.";
AnsiString s2=
"Diese Hierarchie ist nicht geeignet, da ein Kreis mit einer Position ein "
"Kreis ist, aber die Funktionen der Klasse CKreis nicht verwendet.";
} // end of namespace N_b
// c)
namespace N_c {
class C2DKreis:public CKreis {
C2DPunkt Position;
public:
C2DKreis(double Radius=1, double x=0, double y=0):
CKreis(Radius), Position(x,y) { }
C2DKreis(double Radius, C2DPunkt pos):
CKreis(Radius), Position(pos) { }
};
AnsiString s1=
"C2DKreis erbt alle public Elementfunktionen der Basisklasse, also "
"Flaeche, Umfang und toStr. Alle diese Funktionen liefern auch über "
"ein Objekt der Klasse C2DKreis richtige Ergebnisse. Die "
"Elementfunktionen von C2DPunkt stehen dagegen nicht zur Verfügung.";
AnsiString s2=
"Diese Hierarchie ist geeignet, da ein C2DKreis ein Kreis, aber kein "
"C2DPunkt ist.";
} // end of namespace N_c
// d)
namespace N_d {
class C2DKreis{// Lösung von Aufgabe 8.2.2.2
double r;
C2DPunkt Position;
public:
C2DKreis(double Radius=1, double x=0, double y=0):
r(Radius), Position(x,y) { } // Reihenfolge der Deklaration
C2DKreis(double Radius, C2DPunkt pos):
r(Radius), Position(pos) { } // Reihenfolge der Deklaration
AnsiString toStr()
{
return "Kreis mit Radius "+FloatToStr(r)+" in Position "+Position.toStr();
}
};
AnsiString s1=
"Wenn man in der Klasse C2DKreis die Funktionen Flaeche, Abstand usw. "
"für einen Kreis haben will, muss man sie erneut definieren. Diese "
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
347
"Redundanz sollte man vermeiden.";
AnsiString s2=
"Abgesehen davon ist diese Alternative geeignet.";
} // end of namespace N_d
void test()
{
// a)
Form1->Memo1->Lines->Add("a)");
N_a::C2DKreis ka(1,2,3);
Form1->Memo1->Lines->Add(FloatToStr(ka.Umfang()));
Form1->Memo1->Lines->Add(FloatToStr(ka.Abstand()));
Form1->Memo1->Lines->Add(FloatToStr(ka.Flaeche()));
//Form1->Memo1->Lines->Add(ka.toStr()); // Fehler:
// das geht
// das geht
// das geht
Mehrdeutig
Form1->Memo1->Lines->Add(N_a::s1);
Form1->Memo1->Lines->Add("");
Form1->Memo1->Lines->Add(N_a::s2);
// b)
Form1->Memo1->Lines->Add("b)");
N_b::C2DKreis kb(1,2,3);
// Form1->Memo1->Lines->Add(FloatToStr(kb.Umfang()));
// Form1->Memo1->Lines->Add(FloatToStr(kb.Abstand()));
// Form1->Memo1->Lines->Add(FloatToStr(kb.Flaeche()));
Form1->Memo1->Lines->Add(kb.toStr());
//
//
//
//
geht nicht
geht nicht
geht nicht
das geht
//
//
//
//
das geht
geht nicht
das geht
das geht
Form1->Memo1->Lines->Add(N_b::s1);
Form1->Memo1->Lines->Add("");
Form1->Memo1->Lines->Add(N_b::s2);
// c)
Form1->Memo1->Lines->Add("c)");
N_c::C2DKreis kc(1,2,3);
Form1->Memo1->Lines->Add(FloatToStr(kc.Umfang()));
// Form1->Memo1->Lines->Add(FloatToStr(kc.Abstand()));
Form1->Memo1->Lines->Add(FloatToStr(kc.Flaeche()));
Form1->Memo1->Lines->Add(kc.toStr());
Form1->Memo1->Lines->Add(N_c::s1);
Form1->Memo1->Lines->Add("");
Form1->Memo1->Lines->Add(N_c::s2);
// d)
Form1->Memo1->Lines->Add("d)");
N_d::C2DKreis kd(1,2,3);
// Form1->Memo1->Lines->Add(FloatToStr(kd.Umfang())); // geht nicht
// Form1->Memo1->Lines->Add(FloatToStr(kd.Abstand())); // geht nicht
Form1->Memo1->Lines->Add(N_d::s1);
Form1->Memo1->Lines->Add("");
Form1->Memo1->Lines->Add(N_d::s2);
}
} // end of namespace N_Aufg_8_3_11
void __fastcall TForm1::Aufg11_1Click(TObject *Sender)
{
N_Aufg_8_3_11::test();
}
//------ Aufgabe 2 --------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
348
namespace N_Aufg_8_3_11 {
class C {
public:
C() { Form1->Memo1->Lines->Add("C"); }
~C() { Form1->Memo1->Lines->Add("~C"); }
int a;
};
class D3:virtual public C {
public:
D3() { Form1->Memo1->Lines->Add("D3"); }
~D3() { Form1->Memo1->Lines->Add("~D3"); }
};
class D4: public virtual C {
public:
D4() { Form1->Memo1->Lines->Add("D4"); }
~D4() { Form1->Memo1->Lines->Add("~D4"); }
};
class D5: public virtual C {
public:
D5() { Form1->Memo1->Lines->Add("D5"); }
~D5() { Form1->Memo1->Lines->Add("~D5"); }
};
class E:public D3, public D4, public D5 {
public:
E() { Form1->Memo1->Lines->Add("E"); }
~E() { Form1->Memo1->Lines->Add("~E"); }
};
} // end of namespace N_Aufg_8_3_11
void __fastcall TForm1::Aufg11_2Click(TObject *Sender)
{
N_Aufg_8_3_11::E e; // C
// D3
// D4
// D5
// E
} // ~E
// ~D5
// ~D4
// ~D3
// ~C
12.7.9 Aufgaben 8.4
// Datei: d:\Loesungen\kap-8\8.4\VirtFktU.cpp
#include "VirtFktU.h"
//----- Aufgabe 8.4.3.1 ---------------------------------------------class C1DPunkt {
protected: // private wäre besser
double x;
public:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
C1DPunkt(double x_):x(x_)
349
{
}
virtual AnsiString toStr()
{
return FloatToStr(x);
}
void display()
{
Form1->Memo1->Lines->Add(toStr());
}
// b)
virtual double length();
};
class C2DPunkt:public C1DPunkt {
protected: // private wäre besser
double y;
public:
C2DPunkt(double x_,double y_):C1DPunkt(x_),y(y_)
{
}
AnsiString toStr()
{
return FloatToStr(x)+"|"+FloatToStr(y);
}
// b)
double length();
};
class C3DPunkt : public C2DPunkt {
double z;
public:
C3DPunkt(double x_,double y_,double z_):C2DPunkt(x_,y_),z(z_)
{
}
AnsiString toStr()
{
return FloatToStr(x)+"|"+FloatToStr(y)+"|"+FloatToStr(z);
}
// b)
double length();
};
void test_Punkte()
{
C1DPunkt p1(1);
C2DPunkt p2(1,2);
C3DPunkt p3(1,2,3);
p1.display();
// C1DPunkt::toStr
p2.display();
// C2DPunkt::toStr
p3.display();
// C3DPunkt::toStr
}
// b)
#include <math.h>
double C1DPunkt::length()
{
return fabs(x);
};
double C2DPunkt::length()
{
return sqrt(x*x + y*y);
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
350
12 Lösungen
double C3DPunkt::length()
{
return sqrt(x*x + y*y + z*z);
};
void show(C1DPunkt& p)
{
Form1->Memo1->Lines->Add(FloatToStr(p.length()));
};
void test_show()
{
C1DPunkt p1(1);
C2DPunkt p2(1,2);
C3DPunkt p3(1,2,3);
show(p1); // C1DPunkt::toStr
show(p2); // C2DPunkt::toStr
show(p3); // C3DPunkt::toStr
}
void __fastcall TForm1::Aufg_8_4_3_1Click(TObject *Sender)
{
test_Punkte();
test_show();
}
//----- Aufgabe 8.4.3.2 ---------------------------------------------void __fastcall TForm1::Aufg_8_4_3_2Click(TObject *Sender)
{
AnsiString s=
"Diese Funktionen können nicht virtuell definiert werden, da alle ihre "
"Parameterlisten verschieden sind.";
Memo1->Lines->Add(s);
}
//-----
Aufgabe 8.4.3.3 ---------------------------------------------
// b) Die Funktionsdefinitionen sind in ImmoUnit.cpp.
#include "immounit.h"
// a) Eine einzige Definition der Funktion display reicht aus:
void display(const CImmobilie* i, TMemo* Memo)
{
Memo->Lines->Add(i->toStr());
}
// Die übrigen Klassen werden dann von der Definition in der
// Header-Datei abgeleitet:
class CGrundstueck : public CImmobilie {
double Flaeche;
public:
CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_):
CImmobilie(Anschrift_,Kaufpreis_),Flaeche(Flaeche_) { }
AnsiString toStr() const
{
return "Grundstück"+CImmobilie::toStr()+ " Fläche: "+FloatToStr(Flaeche)+
" qm";
}
};
class CWohnImmobilie : public CImmobilie {
protected: // Nur zur Vereinfachung - private wäre besser
double Wohnflaeche;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
351
int AnzahlZimmer;
public:
CWohnImmobilie(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
int AnzahlZimmer_): CImmobilie(Anschrift_,Kaufpreis_), Wohnflaeche
(Wohnflaeche_), AnzahlZimmer(AnzahlZimmer_){}
AnsiString toStr() const
{
return CImmobilie::toStr()+" Wohnfläche: "+
FloatToStr(Wohnflaeche)+" qm Zimmer: "+IntToStr(AnzahlZimmer);
}
};
class CEigentumswohnung : public CWohnImmobilie {
public:
CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double
Wohnflaeche_, int AnzahlZimmer_):
CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_){}
AnsiString toStr() const
{
return "ETW"+CWohnImmobilie::toStr();
}
};
class CEinfamilienhaus : public CWohnImmobilie {
double Grundstuecksgroesse;
public:
CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
double Grundstuecksgroesse_, int AnzahlZimmer_):
CWohnImmobilie(Anschrift_, Kaufpreis_, Wohnflaeche_, AnzahlZimmer_),
Grundstuecksgroesse(Grundstuecksgroesse_){}
AnsiString toStr() const
{
return "EFH"+CWohnImmobilie::toStr();
}
};
void test_Imm()
{
// CImmobilie(char* Anschrift_, double Kaufpreis_):Kaufpreis(Kaufpreis_)
CImmobilie imm("", 100);
display (&imm, Form1->Memo1);
// CGrundstueck(char* Anschrift_, double Kaufpreis_, double Flaeche_)
CGrundstueck g("Tübingen",100000,400);
display(&g, Form1->Memo1);
// CEigentumswohnung(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
int AnzahlZimmer_)
CEigentumswohnung etw("Stuttgart",500000,120,5);
display(&etw, Form1->Memo1);
// CEinfamilienhaus(char* Anschrift_, double Kaufpreis_, double Wohnflaeche_,
double Grundstuecksgroesse_, int AnzahlZimmer_)
CEinfamilienhaus efh("Kiel",300000,150,900,5);
display(&efh, Form1->Memo1);
}
void __fastcall TForm1::Aufg_8_4_3_3Click(TObject *Sender)
{
test_Imm();
}
//----- Aufgabe 8.4.3.4 ---------------------------------------------struct C {
virtual int f(int i=1)
};
{ return i; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
352
struct D:public C {
virtual int f(int i=2)
};
12 Lösungen
{ return i; }
void test()
{
C* pc=new D;
int i=pc->f(); // pc->f(1);
C* pd=new D;
int j=pd->f(); // pd->f(1);
// Das Default-Argument wird immer nach dem statischen Datentyp bestimmt
Form1->Memo1->Lines->Add(i);
Form1->Memo1->Lines->Add(j);
};
void __fastcall TForm1::Aufg_8_4_3_4Click(TObject *Sender)
{
test();
}
//----- Aufgabe 8.4.3.5 ---------------------------------------------void __fastcall TForm1::Aufg_8_4_3_5Click(TObject *Sender)
{
/*
Wenn die Funktion D::f virtuell wäre, würde mit
C c;
D d;
der Aufruf
g(d,c);
zum Aufruf der Funktion
D::f(c)
führen. Diese Funktion würde auf c.e zugreifen, das in C aber gar nicht
existiert.
(siehe Mössenbeck, 1992, S. 75.)
*/
}
//----- Aufgabe 8.4.10 ----------------------------------------------//----- Aufgabe 8.4.10.1 --------------------------------------------namespace N_Aufgabe_1 {
class CFigur {
public:
virtual double Flaeche()=0;
virtual double Umfang()=0;
virtual AnsiString toStr()=0;
~CFigur() {};
};
class CRechteck : public CFigur {
double a,b;
public:
CRechteck(double a_,double b_): a(a_), b(b_) {}
double Flaeche() { return a*b; }
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
double Umfang() { return 2*(a+b); }
AnsiString toStr()
{
return "Rechteck mit a="+FloatToStr(a)+" und b="+FloatToStr(b);
}
};
class CQuadrat : public CFigur {
double a;
public:
CQuadrat(double a_):a(a_) { }
double Flaeche() { return a*a; }
double Umfang() { return 4*a; }
AnsiString toStr()
{
return "Quadrat mit a="+FloatToStr(a);
}
};
void test()
{
CQuadrat q=CQuadrat(1);
CRechteck r=CRechteck(2,3);
CFigur* a[2]={&r , &q };
for (int i=0; i<2; i++)
{
Form1->Memo1->Lines->Add(a[i]->toStr());
Form1->Memo1->Lines->Add("Fläche: "+FloatToStr(a[i]->Flaeche())+" Umfang:
"+FloatToStr(a[i]->Umfang()));
}
}
} // end of namespaace N_Aufgabe_1
void __fastcall TForm1::Aufg8_4_10_1Click(TObject *Sender)
{
N_Aufgabe_1::test();
}
//----- Aufgabe 8.4.10.2 --------------------------------------------#include <vector>
#include <fstream>
using namespace std;
namespace N_Aufg8_4_10_2 {
class C2DPunkt{
public: // Zur Vereinfachung, spart Zugriffsfunktionen
double x,y;
public:
C2DPunkt(double x_=0, double y_=0):x(x_),y(y_) { };
//
C2DPunkt (ifstream& f)
{ // erzeugt einen C2DPunkt mit den Daten aus dem ifstream
f>>*this;
}
AnsiString toStr() const
{
return "("+FloatToStr(x)+"|"+FloatToStr(y)+")";
}
friend ostream& operator<<(ostream& f, const C2DPunkt& p);
friend istream& operator>>(istream& f, C2DPunkt& p);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
353
354
void verschiebe_um(const C2DPunkt& pos)
{
x=x+pos.x;
y=y+pos.y;
};
};
ostream& operator<<(ostream& f, const C2DPunkt& p)
{
return f<<'('<<p.x<<'|'<<p.y<<')';
}
istream& operator>>(istream& f, C2DPunkt& p)
{
char k1,sep,k2;
f>>k1>>p.x>>sep>>p.y>>k2; // Einlesen der Elemente von T
return f;
}
class CFigur {
public:
// a)
virtual void zeichne() const =0;
virtual AnsiString toStr()=0;
virtual ~CFigur(){}; // den virtuellen Destruktor nicht vergessen
// c)
virtual void write(ofstream& f)=0;
// 8.4.11.1 a)
void loesche() const
{
TColor StiftFarbe=Form1->Image1->Canvas->Pen->Color;
Form1->Image1->Canvas->Pen->Color=clWhite; // Hintergrundfarbe
zeichne();
Form1->Image1->Canvas->Pen->Color=StiftFarbe;
}
virtual void verschiebe_Position_um(const C2DPunkt& deltaPos)=0;
// 8.4.11.1 b)
virtual void verschiebe_um(const C2DPunkt& deltaPos)
{
loesche();
verschiebe_Position_um(deltaPos);
zeichne();
}
};
// b)
class CZeichnung {
typedef vector<CFigur*> Container;
typedef vector<CFigur*>::iterator Iterator;
Container c;
public:
CZeichnung(){ };
void einfuegen(CFigur* p)
{
c.push_back(p);
}
// a)
void zeichne()
{
for (Iterator i=c.begin(); i!=c.end(); i++)
(*i)->zeichne();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
// c)
void write(char* Dateiname)
{
ofstream f(Dateiname);
for (Iterator i=c.begin(); i!=c.end(); i++)
(*i)->write(f);
f.close(); // überflüssig, aber nicht falsch
}
// d)
void LeseZeichnung(char* Dateiname);
void toMemo(TMemo* m)
{
for (Iterator i=c.begin(); i!=c.end(); i++)
m->Lines->Add((*i)->toStr().c_str());
}
void loesche()
{
for (Iterator i=c.begin(); i!=c.end(); i++)
(*i)->loesche();
}
void verschiebe_um(C2DPunkt deltaPos)
{
for (Iterator i=c.begin(); i!=c.end(); i++)
(*i)->verschiebe_um(deltaPos);
}
static const char Stringterminator;
};
const char CZeichnung::Stringterminator='\0';
class CGerade:public CFigur {
C2DPunkt ap,ep; // Anfangs- und Endpunkt
public:
CGerade(C2DPunkt ap_, C2DPunkt ep_):ap(ap_),ep(ep_) {}
void zeichne() const
{
Form1->Image1->Canvas->MoveTo(ap.x,ap.y);
Form1->Image1->Canvas->LineTo(ep.x,ep.y);
};
void verschiebe_Position_um(const C2DPunkt& deltaPos)
{
ap.verschiebe_um(deltaPos);
ep.verschiebe_um(deltaPos);
}
void write(ofstream& f)
{
f<<class_id<<CZeichnung::Stringterminator<<ap<<ep;
// f<<char* schreibt den Nullterminator nicht in die Datei
}
CGerade(ifstream& f):ap(f),ep(f)
{ } // Erzeugt eine Gerade mit den Daten aus dem ifstream
AnsiString toStr()
{
return "Gerade AP="+ap.toStr()+" EP="+ep.toStr();
}
static const char* class_id;
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
355
356
const char* CGerade::class_id="Gerade";
class CKreis:public CFigur {
C2DPunkt M; // Mittelpunkt
double r;
// Radius
public:
CKreis(C2DPunkt M_, double r_):M(M_),r(r_) {}
// a)
void zeichne() const
{
Form1->Image1->Canvas->Ellipse(M.x-r,M.y-r,M.x+r,M.y+r);
};
AnsiString toStr()
{
return "Kreis MP="+M.toStr()+" Radius="+FloatToStr(r);
}
// c)
void write(ofstream& f)
{
f<<class_id<<CZeichnung::Stringterminator<<M<<r<<'|';
}
CKreis(ifstream& f)
{ // erzeugt einen Kreis mit den Daten aus dem ifstream
char Separator;
f>>M>>r>>Separator;
}
static const char* class_id;
void verschiebe_Position_um(const C2DPunkt& deltaPos)
{
M.verschiebe_um(deltaPos);
}
};
const char* CKreis::class_id="Kreis";
class CRechteck:public CFigur {
C2DPunkt lu,ro; // Eckpunkte links unten und rechts oben
public:
CRechteck(C2DPunkt lu_, C2DPunkt ro_):lu(lu_),ro(ro_) {}
// a)
void zeichne() const
{
Form1->Image1->Canvas->Rectangle(lu.x,lu.y,ro.x,ro.y);
// zeichnet ein gefülltes Rechteck, das den
// Hintergrund verdeckt
};
AnsiString toStr()
{
return "Rechteck links unten="+lu.toStr()+" rechts oben="+ro.toStr();
}
// c)
void write(ofstream& f)
{
f<<class_id<<CZeichnung::Stringterminator<<lu<<ro;
}
CRechteck(ifstream& f):lu(f),ro(f)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.7 Lösungen Kapitel 8
357
{ } // erzeugt ein Rechteck mit den Daten aus dem ifstream
void verschiebe_Position_um(const C2DPunkt& deltaPos)
{
lu.verschiebe_um(deltaPos);
ro.verschiebe_um(deltaPos);
}
static const char* class_id;
};
const char* CRechteck::class_id="Rechteck";
void CZeichnung::LeseZeichnung(char* Dateiname)
{// Diese Funktion muss bei einer Erweiterung um neue Figuren angepasst werden.
ifstream f(Dateiname);
string s;
while (getline(f,s,Stringterminator)) // Lese nullterminierten String
{
CFigur* pf;
if (s==CGerade::class_id)
pf=new CGerade(f);
else if (s==CKreis::class_id)
pf=new CKreis(f);
else if (s==CRechteck::class_id)
pf=new CRechteck(f);
else
ShowMessage("unexpected data: "+AnsiString(s.c_str()));
c.push_back(pf);
}
f.close(); // überflüssig, aber nicht falsch
}
void test_ReadWrite(char* Dateiname)
{ // testet das Lesen und Schreiben einer Zeichnung.
// Erzeuge eine Zeichnung:
CZeichnung z;
for (int i=0; i<100; i++)
if (i%3==0)
z.einfuegen(new CGerade(C2DPunkt(i,i+1),C2DPunkt(i+2,i+3)));
else if (i%3==1)
z.einfuegen(new CKreis(C2DPunkt(i,i+1),i+2));
else if (i%3==2)
z.einfuegen(new CRechteck(C2DPunkt(i,i+1),C2DPunkt(i+2,i+3)));
// Schreibe die Zeichnung in eine Datei:
z.write(Dateiname);
// Lese die gespeicherten Daten:
z.LeseZeichnung(Dateiname);
// Zeige die Daten an:
z.toMemo(Form1->Memo1);
}
// b)
CZeichnung einfache_Zeichnung()
{
CZeichnung z;
z.einfuegen(new CRechteck(C2DPunkt(40,40),C2DPunkt(100,100)));
z.einfuegen(new CGerade(C2DPunkt(40,40),C2DPunkt(70,10)));
z.einfuegen(new CGerade(C2DPunkt(70,10),C2DPunkt(100,40)));
z.einfuegen(new CKreis(C2DPunkt(70,40),5));
return z;
}
CZeichnung z=einfache_Zeichnung();
} // end of namespace N_Aufg8_4_10_2
void __fastcall TForm1::Aufg8_4_10_2Click(TObject *Sender)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
358
12 Lösungen
{
using namespace N_Aufg8_4_10_2;
// test_ReadWrite("Zeichnung.drw");
// return;
static int i=0;
if (i<3)
{
z.verschiebe_um(N_Aufg8_4_10_2::C2DPunkt(10,10));
z.zeichne();
}
if (i==3) z.loesche();
if (i<4) i++;
else i=0;
}
//----- Aufgabe 8.4.10.3 --------------------------------------------void __fastcall TForm1::Aufg8_4_10_3Click(TObject *Sender)
{
AnsiString s=
"Bei dieser wie auch bei den meisten anderen Klassen kann man "
"nicht ausschliessen, dass sie so verwendet werden, dass ein "
"virtueller Destruktor notwendig ist. Da ein unnötiger virtueller "
"Destruktor nicht mit bemerkenswerten Nachteilen verbunden ist, "
"sollte man jede dieser Klassen mit einem virtuellen Destruktor definieren.";
Form1->Memo1->Lines->Add(s);
}
//----- Aufgabe 8.4.11 ----------------------------------------------//----- Aufgabe 8.4.11.1 --------------------------------------------void __fastcall TForm1::Aufg8_4_11_1Click(TObject *Sender)
{
AnsiString s=
"Die Lösung diese Aufgabe ist in der Lösung der Aufgabe 8.4.10 enthalten. "
"Die Funktionen sind in der Basisklasse CFigur definiert und werden "
"in den abgeleiteten Klassen überschrieben.";
Memo1->Lines->Add(s);
}
namespace N_8_4_11 {
//----- Aufgabe 8.4.11.2 --------------------------------------------// Dieses Design soll nur eine Skizze sein
class Uebertragung { // abstrakte Basisklasse
public:
virtual void VerbindungAufbauen(AnsiString Ziel)=0;
virtual void DatenUebertragen()=0;
virtual void VerbindungBeenden()=0;
};
class UMTS_Uebertragung : public Uebertragung {
virtual void VerbindungAufbauen(AnsiString Ziel)
{ // spezifische Anweisungen für UMTS
};
virtual void DatenUebertragen()
{ // spezifische Anweisungen für UMTS
};
virtual void VerbindungBeenden()
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
359
{ // spezifische Anweisungen für UMTS
};
};
class Modem_Uebertragung : public Uebertragung
{
virtual void VerbindungAufbauen(AnsiString Ziel)
{ // spezifische Anweisungen für Modems
};
virtual void DatenUebertragen()
{ // spezifische Anweisungen für Modems
};
virtual void VerbindungBeenden()
{ // spezifische Anweisungen für Modems
};
};
class ISDN_Uebertragung : public Uebertragung
{
virtual void VerbindungAufbauen(AnsiString Ziel)
{ // spezifische Anweisungen für ISDN
};
virtual void DatenUebertragen()
{ // spezifische Anweisungen für ISDN
};
virtual void VerbindungBeenden()
{ // spezifische Anweisungen für ISDN
};
};
void SendeDaten(Uebertragung& u,AnsiString Ziel)
{
u.VerbindungAufbauen(Ziel);
u.DatenUebertragen();
u.VerbindungBeenden();
};
} // end of namespace N_8_4_11
void __fastcall TForm1::Aufg8_4_11_2Click(TObject *Sender)
{
using namespace N_8_4_11;
ISDN_Uebertragung i;
Modem_Uebertragung m;
UMTS_Uebertragung u;
SendeDaten(u,"Ziel");
}
//----- Aufgabe 8.4.12 ----------------------------------------------namespace N_Gamma_Abstract_Windows_Factory {
void display (AnsiString s)
{
Form1->Memo1->Lines->Add(s);
}
class Button { // abstract product
public:
// definiert die Schnittstelle für das Produkt
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
360
12 Lösungen
virtual void OnClick () {};
// Gemeinsame Schnittsteller aller Buttons
};
class MSWindowsButton:public Button { // concrete product
public: // definiert, wie ein Produkt konstruiert wird,
// und implementiert seine Schnittstelle
MSWindowsButton() { display("MSWindowsButton erzeugt"); };
void OnClick () { display("MS click"); };
};
class XWindowsButton:public Button { // concrete product
public:
XWindowsButton() { display("XWindowsButton erzeugt"); };
void OnClick () { display("X click"); };
};
class Scrollbar { // abstract product
// Gemeinsame Schnittsteller aller Scrollbars
};
class MSWindowsScrollbar:public Scrollbar {
public: // definiert, wie ein Produkt konstruiert wird,
// und implementiert seine Schnittstelle
MSWindowsScrollbar() { display("MSWindowsScrollbar erzeugt"); };
};
class XWindowsScrollbar:public Scrollbar {
public: // definiert, wie ein Produkt konstruiert wird,
// und implementiert seine Schnittstelle
XWindowsScrollbar() { display("XWindowsScrollbar erzeugt");
};
};
class GUIFactory { // abstract factory
public: // Schnittstelle für Operationen, die abstrakte Produkte konstruieren
virtual Button* CreateButton()=0;
virtual Scrollbar* CreateScrollbar()=0;
};
class MSWindowsFactory: public GUIFactory { // concrete factory
public: // implementiert die Konstruktion konkreter Produkte
Button* CreateButton()
{return new MSWindowsButton; }
Scrollbar* CreateScrollbar() {return new MSWindowsScrollbar; }
};
class XWindowsFactory: public GUIFactory { // concrete factory
public: // implementiert die Konstruktion konkreter Produkte
Button* CreateButton()
{ return new XWindowsButton; }
Scrollbar* CreateScrollbar() { return new XWindowsScrollbar; }
};
void CreateAppWindow(GUIFactory* guiFactory)
{ // verwendet nur abstract factory und abstrakte Produkte
Button* b=guiFactory->CreateButton();
b->OnClick();
Scrollbar* s=guiFactory->CreateScrollbar();
}
void test()
{
CreateAppWindow(new MSWindowsFactory);
CreateAppWindow(new XWindowsFactory);
// GUIFactory* guiFactory=new MSWindowsFactory();
// guiFactory=new XWindowsFactory();
}
} // end of namespace N_Gamma_Abstract_Windows_Factory
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
361
void __fastcall TForm1::Aufg8_4_12Click(TObject *Sender)
{
N_Gamma_Abstract_Windows_Factory ::test();
}
//----- Aufgabe 8.4.14 ----------------------------------------------void display(AnsiString s)
{
Form1->Memo1->Lines->Add(s);
}
class CBasisklasse{
public:
virtual void anzeigen()=0;
virtual void drucken()=0;
virtual void aendern()=0;
};
class CBestellung
public:
void anzeigen()
{
display("Best
};
void drucken()
{
display("Best
};
void aendern()
{
display("Best
};
};
: public CBasisklasse{
anzeigen");
drucken");
aendern");
class CLagerbestand : public CBasisklasse{
public:
void anzeigen()
{
display("LB anzeigen");
};
void drucken()
{
display("LB drucken");
};
void aendern()
{
display("LB aendern");
};
};
enum Aktion {anzeigen,drucken,aendern};
void dispatch1(Aktion a, CBasisklasse* p)
{
if (a==anzeigen)
p->anzeigen();
else if (a==drucken) p->drucken();
else if (a==aendern) p->aendern();
}
void dispatch(Aktion a, CBasisklasse* p)
{
typedef void (CBasisklasse::* ZEFB)();
ZEFB Aktionen[]={&CBasisklasse::anzeigen, &CBasisklasse::drucken,
&CBasisklasse::aendern};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
362
12 Lösungen
(p->*Aktionen[a])();
}
void __fastcall TForm1::ZeigerEltFktClick(TObject *Sender)
{
CBasisklasse* p;
CBestellung best;
p=&best;
dispatch(anzeigen,p);
dispatch(drucken,p);
dispatch(aendern,p);
CLagerbestand lag;
p=&lag;
dispatch(anzeigen,p);
dispatch(drucken,p);
dispatch(aendern,p);
}
12.7.10 Aufgabe 8.4.3.3
// Datei: d:\Loesungen\kap-8\8.4\immounit.h
#ifndef ImmoUnitH
#define ImmoUnitH
class CImmobilie {
protected: // Nur zur Vereinfachung - private wäre besser
AnsiString Anschrift;
double Kaufpreis;
public:
CImmobilie(char* Anschrift_, double Kaufpreis_);
virtual ~CImmobilie(); // virtuellen Destruktor nicht vergessen
virtual AnsiString toStr() const;
};
#endif
// Datei: d:\Loesungen\kap-8\8.4\immounit.cpp
#include "ImmoUnit.h"
CImmobilie::CImmobilie(char* Anschrift_, double
Kaufpreis_):Anschrift(Anschrift_),Kaufpreis(Kaufpreis_)
{
}
CImmobilie::~CImmobilie() { }
AnsiString CImmobilie::toStr() const
{
return " in "+AnsiString(Anschrift)+ " KP: "+FloatToStr(Kaufpreis) + " DM ";
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.7 Lösungen Kapitel 8
363
12.7.11 Aufgabe 8.5
// Datei: d:\Loesungen\kap-8\8.5\RTTIU.cpp
#include "RTTIU.h"
//---------- Aufgabe 1 ----------------------------------------------#include <typeinfo>
using namespace std;
void Aufgabe_1()
{
class C {
virtual // für Aufgabe b) auskommentieren
void f(){}; // damit C1 polymorph ist
} c;
class D1:public C {
} d1;
class D2:public C {
} d2;
// a)
c=d1;
bool b1= typeid(c)==typeid(d1);
c=d2;
bool b2= typeid(c)==typeid(d2);
// false
// false
// false
// false
D1* pd1=&d1;
D2* pd2=&d2;
C* pc=pd1;
bool b3= typeid(*pc)==typeid(d1); // true
bool b4= typeid(pc)==typeid(pd1); // false
pc=&c;
bool b5= typeid(*pc)==typeid(d1); // false
C& rc=c;
bool b6= typeid(rc)==typeid(d1);
bool b7= typeid(c)==typeid(d1);
rc=d1;
bool b8= typeid(rc)==typeid(d1);
C& rc1=d1;
bool b9= typeid(rc1)==typeid(d1);
b)
// false
// false
// false
// false
// false
// false
// false
// false
// false
// true
// false
int Breakpoint=1;
{
// c)
class E1:public D2 {
} e1;
class E2:public D2 {
} e2;
// /* für Aufgabe b) auskommentieren
D1* pd1=&d1;
D2* pd2=&d2;
E1* pe1=&e1;
E2* pe2=&e2;
C* pc=&c;
D2* p1= dynamic_cast<D2*>(pc); // p1=0
pc=pd1;
D2* p2= dynamic_cast<D2*>(pc); // p2=0
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
364
pc=pe1;
D2* p3= dynamic_cast<D2*>(pc); // p2!=0
pc=pe2;
// Fehler bei private Ableitung
D2* p4= dynamic_cast<D2*>(pc); // p2!=0
// d) Welche Ergebnisse würde man in Aufgabe c) erhalten,
//
wenn E2 private und nicht public abgeleitete wäre ?
C& rc=c;
C& rd1=d1;
C& rd2=d2;
C& re1=e1;
C& re2=e2;
// D2 x1= dynamic_cast<D2&>(c); // Exception bad_cast
// D2 x2= dynamic_cast<D2&>(rd1); // Exception bad_cast
D2 x3= dynamic_cast<D2&>(rd2); // keine Exception
D2 x4= dynamic_cast<D2&>(re1); // keine Exception
D2 x5= dynamic_cast<D2&>(re2); // keine Exception
int Breakpoint=0;
// */ Ende für Aufgabe b) auskommentieren
}
}
void __fastcall TForm1::Aufg_1Click(TObject *Sender)
{
Aufgabe_1();
}
//---------- Aufgabe 2 ----------------------------------------------class C {
public:
virtual void f()
{
Form1->Memo1->Lines->Add(typeid(*this).name());
};
C() { f(); }
~C() { f(); }
};
class D:public C {
public:
D() { f(); }
~D() { f(); }
};
void __fastcall TForm1::Aufg_2Click(TObject *Sender)
{
D d; // Lösung: C D D C
}
12.8 Lösungen Kapitel 9
12.8.1 Aufgabe 9.2
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.8 Lösungen Kapitel 9
// Datei: d:\Loesungen\kap-9\9.2\PropU.cpp
#include "PropU.h"
//-------------------------------------------------------------------class C {
int fx;
void setx(int x_){fx=x_*x_;};
int getx(){return fx;};
public:
__property int x = {read=getx, write=setx};
};
void __fastcall TForm1::Aufg_2_2Click(TObject *Sender)
{
C p;
p.x=17;
int y=p.x;
Memo1->Lines->Add(y);
}
//--------------------------------------------------------------------
12.8.2 Aufgabe 9.3
// Datei: d:\Loesungen\kap-9\9.3\ResizeUnit.h
#ifndef ResizeUnitH
#define ResizeUnitH
class TResize{
int fsw,fsh,
faw,fah;
bool initialized,
PositionAndSize; // true: adjust position and size
// false: adjust only position
int n_Controls; // müßte meist reichen
TForm* Form;
struct TWinCoord {
int Top,
Left,
Width,
Height;
};
TWinCoord* sc;
int xt(int x)
{ // transformiert x-Koordinaten
return x*faw/fsw;
};
int yt(int y)
{ // transformiert y-Koordinaten
return y*fah/fsh;
};
// Da diese Klasse Zeiger enthält, führt der vom Compiler erzeugte
// Copy-Konstruktor zu flachen Kopien. Da der aber nie benötigt wird,
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
365
366
// wird sein Aufruf durch eine private Deklaration unterbunden.
TResize(TResize&); // Copy-Konstruktor private
TResize& operator=(TResize&);
void Init(TForm* Form_);
public:
void Resize(TForm* Form_);
TResize(bool PositionAndSize_=false):initialized(false),
PositionAndSize(PositionAndSize_) { };
virtual ~TResize() {delete[] sc;}
};
#endif
// Datei: d:\Loesungen\kap-9\9.3\ResizeUnit.cpp
#include <vcl.h> // Mit Datei|Neu Unit erzeugen
#pragma hdrstop //
#include "ResizeUnit.h"
void TResize::Init(TForm* F)
{
Form=F;
sc=new TWinCoord[F->ComponentCount];
fsw = Form->Width; // Breite des Formulars beim Start
fsh = Form->Height;
for (int i=0; i<Form->ComponentCount;i++ )
{ // Größe der Komponenten beim Start
sc[i].Top
= ((TControl*)Form->Components[i])->Top;
sc[i].Left
= ((TControl*)Form->Components[i])->Left;
// Die Konversion ist auch mit dynamic_cast möglich:
sc[i].Height = dynamic_cast<TControl*>(Form->Components[i])->Height;
sc[i].Width = dynamic_cast<TControl*>(Form->Components[i])->Width;
}
initialized = true; // Wird bei Create automatisch auf
};
// false gesetzt
void TResize::Resize(TForm* F)
{
if (!initialized) Init(F);
faw = Form->Width; // Breite des aktuellen Formulars
fah = Form->Height;
for (int i=0; i<Form->ComponentCount; i++)
if (PositionAndSize) // Position und Größe anpassen
((TWinControl*)Form->Components[i])->SetBounds(xt(sc[i].Left),
yt(sc[i].Top),xt(sc[i].Width), yt(sc[i].Height));
else // nur die Position anpassen
((TWinControl*)Form->Components[i])->SetBounds(xt(sc[i].Left),
yt(sc[i].Top), sc[i].Width, sc[i].Height);
};
// Datei: d:\Loesungen\kap-9\9.3\ResizU.cpp
#include "ResizU.h"
//-------------------------------------------------------------------#include "ResizeUnit.h"
// Noch "ResizeUnit.cpp" mit Projekt|Dem Projekt hinzufügen
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.8 Lösungen Kapitel 9
TResize r;
// Passt nur die Größe und nicht die Position an
// CResize r(true); // Passt die Größe und die Position an
void __fastcall TForm1::FormResize(TObject *Sender)
{
r.Resize(Form1);
}
//--------------------------------------------------------------------
12.8.3 Aufgabe 9.4
// Datei: d:\Loesungen\kap-9\9.4\EigKompU.cpp
#include "EigKompU.h"
// ------- Aufgabe 1 -----------------------------------// ClickAction ist im Klassenheader deklariert
void __fastcall TForm1::ClickAktion1(TObject *Sender)
{
Button3->Caption="Aktion 1";
}
void __fastcall TForm1::ClickAktion2(TObject *Sender)
{
Button3->Caption="Aktion 2";
}
void __fastcall TForm1::ClickAktion3(TObject *Sender)
{
Button3->Caption="Aktion 3";
}
void __fastcall TForm1::RadioButton1Click(TObject *Sender)
{
Button3->OnClick=ClickAktion1;
}
void __fastcall TForm1::RadioButton2Click(TObject *Sender)
{
Button3->OnClick=ClickAktion2;
}
void __fastcall TForm1::RadioButton3Click(TObject *Sender)
{
Button3->OnClick=ClickAktion3;
}
// ------- Aufgabe 2 -----------------------------------TEdit* MakeEdit(TForm* F, int l,int t,int w,int h)
{
TEdit* E = new TEdit(F);
E->Parent = F;
E->SetBounds(l, t, w, h);
return E;
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
367
368
12 Lösungen
TLabel* MakeLabel(TForm* F, int l,int t,int w,int h, AnsiString Caption)
{
TLabel* L = new TLabel(F);
L->Parent = F;
L->SetBounds(l, t, w, h);
L->Caption=Caption;
return L;
};
TButton* MakeButton(TForm* F,AnsiString Caption, int l,int t,int w,int
h,TNotifyEvent E)
{
TButton* B = new TButton(F);
B->Parent = F;
B->SetBounds(l, t, w, h);
B->Caption=Caption;
B->OnClick=E;
return B;
};
class MyForm : public TForm
{
__published: // Von der IDE verwaltete Komponenten
private: // Anwender-Deklarationen
public: // Anwender-Deklarationen
__fastcall MyForm(TForm* AOwner);
void __fastcall EndeClick(TObject *Sender);
void __fastcall LoeschenClick(TObject *Sender);
void __fastcall SpeichernClick(TObject *Sender);
TLabel* LVorname;
TEdit* EVorname;
TLabel* LNachname;
TEdit* ENachname;
TLabel* LBussgeld;
TEdit* EBussgeld;
TLabel* LOrdnungswidrigkeit;
TEdit* EOrdnungswidrigkeit;
TButton* BSpeichern;
TButton* BLoeschen;
TButton* BEnde;
};
__fastcall MyForm::MyForm(TForm* AOwner):TForm(AOwner,0)
{
Caption="Das städtische Haushaltssanierungsprogramm";
int LabelLeft=10, EditLeft=150, Top=10, TopInc=40, w=100,h=20;
LVorname=MakeLabel(this,LabelLeft,Top,w,h,"Vorname");
EVorname=MakeEdit (this,EditLeft,Top,w,h);
LNachname=MakeLabel(this,LabelLeft,Top+TopInc,w,h,"Nachname");
ENachname=MakeEdit (this,EditLeft,Top+TopInc,w,h);
LBussgeld=MakeLabel(this,LabelLeft,Top+2*TopInc,w,h,"Bussgeld");
EBussgeld=MakeEdit (this,EditLeft,Top+2*TopInc,w,h);
LOrdnungswidrigkeit=MakeLabel(this,LabelLeft,Top+3*TopInc,w,h,"Ordnungswidrigkeit
");
EOrdnungswidrigkeit=MakeEdit (this,EditLeft,Top+3*TopInc,w,h);
TButton* BSpeichern=MakeButton(this,"Daten
speichern",10,180,100,h,SpeichernClick);
TButton* BLoeschen=MakeButton(this,"Eingabe
löschen",120,180,100,h,LoeschenClick);
TButton* BEnde=MakeButton(this,"Programm beenden",230,180,100,h,EndeClick);
Show();
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.8 Lösungen Kapitel 9
SetBounds(0,0,400,250);
};
void __fastcall MyForm::SpeichernClick(TObject *Sender)
{
AnsiString v=EVorname->Text;
AnsiString n=ENachname->Text;
AnsiString b=EBussgeld->Text;
AnsiString o=EOrdnungswidrigkeit->Text;
}
void __fastcall MyForm::LoeschenClick(TObject *Sender)
{
EVorname->Clear();
ENachname->Clear();
EBussgeld->Clear();
EOrdnungswidrigkeit->Clear();
}
void __fastcall MyForm::EndeClick(TObject *Sender)
{
Close();
}
void __fastcall TForm1::CreateFormClick(TObject *Sender)
{
MyForm* MyForm1=new MyForm(Form1);
}
12.8.4 Aufgabe 9.5
// Datei: d:\Loesungen\kap-9\9.5\MDIImgCU.cpp
#include "MDIImgCU.h"
TMDIChildForm *MDIChildForm;
__fastcall TMDIChildForm::TMDIChildForm(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TMDIChildForm::FormCreate(TObject *Sender)
{
Image1->Align=alClient;
FormStyle=fsMDIChild;
}
void __fastcall TMDIChildForm::FormClose(TObject *Sender,
TCloseAction &Action)
{
Action=caFree;
}
12.8.5 Aufgabe 9.5
// Datei: d:\Loesungen\kap-9\9.5\MDIImgPU.cpp
#include "MDIImgPU.h"
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
369
370
12 Lösungen
//-------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
FormStyle = fsMDIForm;
}
//-------------------------------------------------------------------#include "MDIImgCU.h"
void __fastcall TForm1::ffnen1Click(TObject *Sender)
{
if (OpenPictureDialog1->Execute())
{
MDIChildForm=new TMDIChildForm(this);
MDIChildForm->Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName);
}
}
//--------------------------------------------------------------------
12.8.6 Aufgabe 9.5
// Datei: d:\Loesungen\kap-9\9.5\mdiapp.cpp
USEFORM("Main.cpp", MainForm);
USEFORM("ChildWin.cpp", MDIChild);
USERES("mdiapp.res");
USEFORM("about.cpp", AboutBox);
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
Application->Initialize();
Application->CreateForm(__classid(TMainForm), &MainForm);
Application->CreateForm(__classid(TAboutBox), &AboutBox);
Application->Run();
return 0;
}
12.8.7 Aufgabe 9.6
// Datei: d:\Loesungen\kap-9\9.6\KlassRefU.cpp
#include "KlassRefU.h"
//-------------------------------------------------------------------void ShowBaseNames(TMetaClass* M)
{
for (TMetaClass* C=M;C!=0; C=C->ClassParent())
Form1->Memo1->Lines->Add(C->ClassName());
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ShowBaseNames(__classid(TEdit));
}
//--------------------------------------------------------------------
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.8 Lösungen Kapitel 9
12.8.8 Aufgabe 9.7
// Datei: d:\Loesungen\kap-9\9.7\BotschU.cpp
#include "BotschU.h"
//------ Aufgabe 1 --------------------------------------------------void __fastcall TForm1::ButtonCopyClick(TObject *Sender)
{
SendMessage(Memo1->Handle, WM_COPY, 0, 0);
// dasselbe Ergebnis erzielt man mit
// Memo1->Perform(WM_COPY,0,0);
}
void __fastcall TForm1::ButtonCutClick(TObject *Sender)
{
SendMessage(Memo1->Handle, WM_CUT, 0, 0);
// dasselbe Ergebnis erzielt man mit
// Memo1->Perform(WM_CUT,0,0);
}
void __fastcall TForm1::ButtonPasteClick(TObject *Sender)
{
SendMessage(Memo1->Handle, WM_PASTE, 0, 0);
// dasselbe Ergebnis erzielt man mit
// Memo1->Perform(WM_PASTE,0,0);
}
//------ Aufgabe 2 --------------------------------------------------void TForm1::EditNext()
{
if (RadioButton1->Checked)
RadioButton1->Caption="Mit Enter in Edit1/Edit2 nicht weiter";
else
RadioButton1->Caption="Mit Enter in Edit1/Edit2 weiter";
if (RadioButton2->Checked)
RadioButton2->Caption="Form KeyPreview true";
else
RadioButton2->Caption="Form KeyPreview false";
Form1->KeyPreview=RadioButton2->Checked;
if (RadioButton3->Checked)
{
RadioButton3->Caption="Edit1/Edit1->OnKeyPress=Edit1->OnKeyPress";
Edit1->OnKeyPress=Edit1KeyPress;
Edit2->OnKeyPress=Edit1KeyPress;
}
else
{
RadioButton3->Caption="Edit1/Edit1->OnKeyPress=0";
Edit1->OnKeyPress=0;
Edit2->OnKeyPress=0;
}
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
RadioButton1->Checked=true;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
371
372
12 Lösungen
void __fastcall TForm1::RadioButton2Click(TObject *Sender)
{
EditNext();
}
void __fastcall TForm1::RadioButton3Click(TObject *Sender)
{
EditNext();
}
void __fastcall TForm1::RadioButton1Click(TObject *Sender)
{
EditNext();
}
void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
if (Key == 13) // falls KeyPreview auf true gesetzt ist
Form1->Perform(WM_NEXTDLGCTL,0,0);
// dasselbe Ergebnis erzielt man mit
// SendMessage(Form1->Handle,WM_NEXTDLGCTL,0,0); }
}
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (Key == 13)
Form1->Perform(WM_NEXTDLGCTL,0,0);
}
#include "KomponU.h"
#include "KomponU.cpp"
//------- Aufgabe 3: TTabEdit ---------------------------------------void __fastcall TForm1::TabEditClick(TObject *Sender)
{
static int Left=GroupBox3->Left+TabEdit->Width+10;
static int Top=GroupBox3->Top+10;
Left=Left+20;
Top=Top+20;
TTabEdit* te=new TTabEdit(Form1,Form1,Left,Top,60,20);
te->Text="TabEdit "+IntToStr(Left);
}
//------- Aufgabe 4: TFocusColorEdit --------------------------------void __fastcall TForm1::FocusColorEditClick(TObject *Sender)
{
TFocusColorEdit* fce=new TFocusColorEdit(Form1,
GroupBox4->Left+FocusColorEdit->Width+20,
GroupBox4->Top+FocusColorEdit->Top,
100,
30);
fce->Parent=Form1;
}
//------- Aufgabe 5: TResizableMemo ---------------------------------void __fastcall TForm1::ResizMemoClick(TObject *Sender)
{
TResizableMemo* tr=new TResizableMemo(Form1,GroupBox6->Left+ResizMemo->Width+20,
GroupBox6->Top+ResizMemo->Top,//+ColBordLabel->Top,
100, 100);
tr->Text="Der rechte Rand kann mit der Maustaste verzogen werden";
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
373
}
//------- Aufgabe 6: TColBorderLabel --------------------------------void __fastcall TForm1::ColBordLabelClick(TObject *Sender)
{
TColBorderLabel* tc=new TColBorderLabel(Form1,ColBordLabel->Left+ColBordLabel>Width+20,
ColBordLabel->Top, 100, 20);
tc->BlinkInterval=500;
tc->BorderColor=clRed;
tc->Color=clYellow;
}
//------- Aufgabe 7: TRubberShape -----------------------------------void __fastcall TForm1::RubberShapeClick(TObject *Sender)
{
TRubberShape* tr=new TRubberShape(Form1,RubberShape->Left+RubberShape->Width+20,
RubberShape->Top,
150,
150);
tr->Canvas->TextOut(0,0,"");
}
12.9 Lösungen Kapitel 10
12.9.1 Aufgabe 10.1
// Datei: d:\Loesungen\kap-10\10.1\FktTemplU.cpp
#include "FktTemplU.h"
//----- Aufgabe 1. -----------------------------------------------template <class T>void vertausche(T& a, T& b)
{
T h = a;
a = b;
b = h;
}
template <typename T> bool kleiner(T x,T y)
{
return x<y;
}
#include <typeinfo>
// Wenn man die folgende Spezialisierung auskommentiert, wird ein
// Array mit Zeigern nicht richtig sortiert:
template <typename T> bool kleiner(T* x,T* y)
{
// Form1->Memo1->Lines->Add(typeid(x).name());
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
374
12 Lösungen
return *x<*y;
}
// Wenn man die folgende Spezialisierung auskommentiert, wird ein
// Array mit char* nicht richtig sortiert:
// template <> bool kleiner(char* x,char* y) // In BCB5 mit kleiner<T> mehrdeutig
!!!
// template <> bool kleiner(const char* x,const char* y) // wird in BCB5 mit
kleiner anstelle kleiner<T> nicht generiert !!!
// template <> bool kleiner<const char*>(const char* x,const char* y) // wird in
BCB5 mit kleiner anstelle kleiner<T> nicht generiert !!!
template <> bool kleiner<const char*>(const char* x,const char* y) //
funktioniert in GNU
{
return strcmp(x,y)<0;
}
template <typename T> void AuswahlSort(T A[],int n)
{
// Form1->Memo1->Lines->Add(typeid(T).name());
for (int i=0;i<n-1;i++)
{
int iMin = i;
for (int j=iMin+1;j<n;j++)
if (kleiner<T>(A[j],A[iMin])) iMin=j;
//
if (kleiner(A[j],A[iMin])) iMin=j;
vertausche(A[iMin],A[i]);
}
}
void test_Aufg1()
{
int a[5]={5,4,6,2,1};
AuswahlSort(a,5);
int* p[5]={new int(5),new int(4),new int(6),new int(2),new int(1)};
AuswahlSort<int*>(p,5);
for (int i=0;i<5; i++)
Form1->Memo1->Lines->Add(IntToStr(*p[i]));
const char* n[5]={"15","14","16","02","01"};
AuswahlSort<const char*>(n,5);
for (int i=0;i<5; i++)
Form1->Memo1->Lines->Add(n[i]);
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
test_Aufg1();
}
//----- Aufgabe 2. -----------------------------------------------#include <sstream>
#include <string>
using namespace std;
template<typename T> string ToStr(T x)
{
ostringstream os;
os<<x;
return os.str();
}
template<typename T> T StrTo(string s)
{
T d;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
istringstream is(s);
is.exceptions(ios::failbit|ios::badbit);
// Löst Exception aus, falls keine Umwandlung möglich ist
is>>d;
return d;
}
template<typename T> T StrTo(char* s)
{ // Für char*-Argumente, da abgeleitete Parameter nicht konvertiert werden
return StrTo<T>(string(s));
}
struct CBruch {
int z,n; // z: Zähler, n: Nenner
};
ostream& operator<<(ostream& f, const CBruch& b)
{
return f<<b.z<<"|"<<b.n;
}
istream& operator>>(istream& f, CBruch& b)
{
char Bruchstrich;
f>>b.z>>Bruchstrich>>b.n;
return f;
}
void test_Aufg2()
{
CBruch b1={1,2}, b2={3,4};
string s1=ToStr(b1);
string s2=ToStr(b2);
string s3=ToStr(1.2345);
string s4=ToStr(17);
CBruch b5=StrTo<CBruch>(string("1/2"));
CBruch b6=StrTo<CBruch>("3/4"); // erfordert Parameter char*
// CBruch b7=StrTo<CBruch>("einhalb"); // löst Exception aus
int i=StrTo<int>("1");
double d=StrTo<double>("2.3");
}
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
test_Aufg2();
}
//----- Aufgabe 3. -----------------------------------------------inline bool operator==(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n==q2.z*q1.n;
}
inline bool operator<(const CBruch& q1, const CBruch& q2)
{
return q1.z*q2.n < q2.z*q1.n;
}
// Diese Funktionen sind in "CBuilder\include\utility.h" genau so definiert:
template <class T>
inline bool operator!=(const T& x, const T& y)
{
return !(x == y);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
375
376
12 Lösungen
template <class T>
inline bool operator>(const T& x, const T& y)
{
return y < x;
}
template <class T>
inline bool operator<=(const T& x, const T& y)
{
return !(y < x);
}
template <class T>
inline bool operator>=(const T& x, const T& y)
{
return !(x < y);
}
void test_Aufg3()
{
CBruch b1={1,2}, b2={3,4};
bool r1=b1<b2;
bool r2=b1<=b2;
bool r3=b1>b2;
bool r4=b1>=b2;
bool r5=b1==b2;
bool r6=b1!=b2;
}
void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
test_Aufg3();
}
//----- Aufgabe 4. -----------------------------------------------// a)
template<typename T> T Abs(T x)
{
if (x<0) return -x;
else return x;
}
// b)
void Aufg4b()
{
long L=1;
unsigned int u=1;
int i1=Abs('c');
int i3=Abs(L);
int i5=Abs(u+L);
int i7=Abs(1.5L);
}
int i2=Abs(1);
int i4=Abs(u);
// int i6=Abs(1.5); // siehe c)
// c)
int Abs(int x)
{
// Form1->Memo1->Lines->Add("2: int");
if (x<0) return -x;
else return x;
}
// das wird nur dann übersetzt, wenn i6=... in b) auskommentiert wird:
template<> double Abs<double>(double x)
{
// Form1->Memo1->Lines->Add("3: double");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
377
if (x<0) return -x;
else return x;
}
void Aufg4c()
{
long L=1;
unsigned int u=1;
int i1=Abs('c');
int i3=Abs(L);
int i5=Abs(u+L);
int i7=Abs(1.5L);
}
int i2=Abs(1);
int i4=Abs(u);
int i6=Abs(1.5);
void __fastcall TForm1::Aufg4Click(TObject *Sender)
{
Aufg4b();
Aufg4c();
}
12.9.2 Aufgabe 10.2
// Datei: d:\Loesungen\kap-10\10.2\KlTemplU.cpp
#include "KlTemplU.h"
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
//----- Aufgabe 1----------------------------------------------------template <typename T>
class MeineListe {
// typedef int T;
// T: der Datentyp der "Nutzdaten"
struct list_node {// Knoten der verketteten Liste
T data;
list_node* next;
};
list_node *first,*last;
public:
MeineListe():first(0),last(0) {};
void insert(T data) // Erzeugt einen neuen Knoten
{ // der Liste und fügt diesen nach last ein.
// last zeigt anschließend auf das letzte und
// first auf das erste Element der Liste.
list_node* tmp=new list_node;
tmp->data = data;
tmp->next = 0;
if (last == 0) first = tmp;
else last->next = tmp;
last = tmp;
}
void show()
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
378
{
for (list_node* tmp=first; tmp!=0; tmp=tmp->next)
Form1->Memo1->Lines->Add(tmp->data);
}
~MeineListe()
{
while (first!=0)
{
list_node* tmp = first;
first = first->next;
delete tmp;
}
// first=0
last=0;
};
};
void test_MeineListe(int n)
{
Form1->Memo1->Lines->Add("test n="+IntToStr(n));
MeineListe<double> L;
for (int i=0; i<n; i++)
L.insert(i);
L.show();
}
AnsiString bytes_allocated_on_heap()
{//Projektoptionen Linker:Ohne "Dynamische RTL" notwendig
static bool first_call=true;
static THeapStatus f;
THeapStatus akt=GetHeapStatus();
if (first_call)
{
f=akt;
first_call=false;
}
return "Bytes allocated on heap: "+
IntToStr(akt.TotalAllocated-f.TotalAllocated);
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
Form1->Memo1->Lines->Add("test n="+bytes_allocated_on_heap());
test_MeineListe(100);
Form1->Memo1->Lines->Add("test n="+bytes_allocated_on_heap());
}
//----- Aufgabe 2----------------------------------------------------#include <sstream>
using namespace std;
template<class T, int Max> class Array {
T a[Max];
public:
Array(){};
T& operator[](int i)
{
if (i<0 || i>= Max)
{
ostringstream message;
message<<"Index in Array "<<i<<" > "<<(Max-1);
throw std::out_of_range(message.str());
}
return a[i];
};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.9 Lösungen Kapitel 10
};
void test_Array()
{
const int n=100;
Array<int, n> a;
for (int i=0;i<n;i++) a[i]=i;
int s=0;
for (int i=0;i<n;i++) s=s+a[i];
Form1->Memo1->Lines->Add("s="+IntToStr(s));
}
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
test_Array();
}
//----- Aufgabe 3 ---------------------------------------------------// In der Standardbibliothek von C++ ist das Funktions-Template make_pair
// in <utility> folgendermaßen definiert:
template <class T1, class T2>
pair<T1, T2> make_pair(const T1& x, const T2& y)
{
return pair<T1, T2>(x, y);
}
#include <map>
using namespace std;
void test_make_pair()
{
map<string,string> ac;
ac.insert(pair<string,string>("Daniel","13.10.89"));
ac.insert(make_pair("Daniel","13.10.89"));
}
void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
test_make_pair();
}
//----- Aufgabe 4 ---------------------------------------------------#include <fstream>
using namespace std;
template<typename T> class binary_fstream: public fstream {
public:
// Konstruktor: fstream();
binary_fstream():fstream() {}
// Konstruktor: fstream(const char* s,ios_base::openmode
mode=ios_base::in|ios_base::out);
binary_fstream(const char* s,ios_base::openmode
mode=ios_base::in|ios_base::out):
fstream(s,mode|ios::binary) {}
void write(T t)
{ // fstream: write(const char_type* s, streamsize n);
fstream::write((char*)&t,sizeof(T));
}
void read(T& t)
{ // fstream: read(char_type* s, streamsize n);
fstream::read((char*)&t,sizeof(T));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
379
380
12 Lösungen
}
};
void test_binary_fstream()
{
binary_fstream<int> f("c:\\test\\bin");
for (int i=0; i<10; i++)
f.write(i);
f.close();
f.open("c:\\test\\bin");
int x;
f.read(x);
while (!f.eof())
{
Form1->Memo1->Lines->Add(IntToStr(x));
f.read(x);
}
}
void __fastcall TForm1::Aufg4Click(TObject *Sender)
{
test_binary_fstream();
}
//----- Aufgabe 5 ---------------------------------------------------template<class T> class MeinAutoPtr
{
T* ptr;
public:
MeinAutoPtr (T* p) throw() : ptr(p) { }
MeinAutoPtr(MeinAutoPtr<T>& a)
{
T* tmp=a.ptr;
a.ptr=0;
ptr=tmp;
}
throw() // Copy-Konstruktor
MeinAutoPtr<T>& operator= (MeinAutoPtr<T>& rhs)
{
if (ptr != rhs.ptr)
{
T* tmp = rhs.ptr;
rhs.ptr = 0;
delete ptr;
ptr = tmp;
}
return *this;
}
throw() // operator=
~MeinAutoPtr() throw() { delete ptr; }
// members
T& operator* ()
T* operator-> ()
T* get
()
};
const throw() { return *ptr;
const throw() { return ptr;
const throw() { return ptr;
}
}
}
class C {
public:
C(){
Form1->Memo1->Lines->Add("Konstr");
}
~C(){
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
Form1->Memo1->Lines->Add("Destr");
}
};
void test_MeinAutoPtr()
{
C* pc1=new C;
C* pc2=new C;
MeinAutoPtr<C> p(pc1);
MeinAutoPtr<C> q(pc2);
p=q;
MeinAutoPtr<C> r(p);
C* pc;
// p=pc;
}
void __fastcall TForm1::Aufg5Click(TObject *Sender)
{
test_MeinAutoPtr();
}
12.9.3 Aufgabe 10.3
// Datei: d:\Loesungen\kap-10\10.3\FktObjU.cpp
#include "FktObjU.h"
//----- Aufgabe 1. -----------------------------------------------#include <algorithm>
#include <function>
using namespace std;
template<typename T, typename Iterator>
bool is_sorted(Iterator first, Iterator last)
{
return adjacent_find(first,last,greater<T>())==last;
}
#include <vector>
void Aufg1()
{
int a1[5]={2,1,2,5,7};
bool b1=is_sorted<int>(a1, a1+5); // false
int a2[5]={1,1,2,5,7};
bool b2=is_sorted<int>(a2, a2+5); // true
int a3[5]={1,1,2,7,5};
bool b3=is_sorted<int>(a3, a3+5); // false
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Aufg1();
}
//----- Aufgabe 2. -----------------------------------------------// a)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
381
382
bool AnsiCompStr(string s1,string s2)
{ // lstrcmp verwendet den Windows-Zeichensatz
return lstrcmp(s1.c_str(),s2.c_str())<=0;
}
// d)
void print(string s)
{
Form1->Memo1->Lines->Add(s.c_str());
}
void Aufg2()
{
// b)
const int n=20; // Anzahl der strings in a
char* a[]={".","-","a","b","c","o","u","s","t","ä",
"ü","Ä","Ö","Ü","ß","A","O","U","S","T"};
vector<string> v1(a,a+n);
sort(v1.begin(), v1.end(),AnsiCompStr);
// c)
vector<string> v2(v1.begin(),v1.end());
sort(v2.begin(), v2.end());
vector<string> v3(v1.size());
// d)
transform(v1.begin(),v1.end(),v2.begin(),v3.begin(),plus<string>());
for_each(v3.begin(),v3.end(),print);
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Aufg2();
}
//----- Aufgabe 3. -----------------------------------------------class C {
public:
virtual void f()
{
Form1->Memo1->Lines->Add("C");
}
virtual void g(int i)
{
Form1->Memo1->Lines->Add("C-g"+IntToStr(i));
}
};
class D : public C{
public:
void f()
{
Form1->Memo1->Lines->Add("D");
}
void g(int i)
{
Form1->Memo1->Lines->Add("D-g"+IntToStr(i));
}
};
class E : public D{
public:
void f()
{
Form1->Memo1->Lines->Add("E");
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.9 Lösungen Kapitel 10
void g(int i)
{
Form1->Memo1->Lines->Add("E-g"+IntToStr(i));
}
};
void Aufg3()
{
C c;
D d;
E e;
vector<C*> v;
v.push_back(&c);
v.push_back(&d);
v.push_back(&e);
// for_each(v.begin(),v.end(),C::f); // das geht nicht
// a)
for_each(v.begin(),v.end(),mem_fun(&C::f));
// b)
for_each(v.begin(),v.end(),bind2nd(mem_fun(&C::g),17));
// c)
{
vector<C> w;
w.push_back(c);
w.push_back(d);
w.push_back(e);
for_each(w.begin(),w.end(),mem_fun_ref(&C::f));
}
}
void __fastcall TForm1::Button3Click(TObject *Sender)
{
Aufg3();
}
//----- Aufgabe 4. -----------------------------------------------struct CName {
string Vorname;
string Nachname;
CName(string v, string n):Vorname(v),Nachname(n) {}
};
typedef vector<CName> Container;
typedef Container::iterator Iterator;
// a)
bool Name_gleich(const CName n, const string Name)
{
// (const CName& n, const string& Name) // geht nicht
return n.Nachname==Name;
}
void Aufg4a(Container v,string gesucht)
{
Iterator p=find_if(v.begin(),v.end(),bind2nd(ptr_fun(Name_gleich),gesucht));
while (p!=v.end())
{
Form1->Memo1->Lines->Add(p->Nachname.c_str() );
p=find_if(p+1,v.end(),bind2nd(ptr_fun(Name_gleich),gesucht));
}
}
// b)
#include <functional>
class find_Name: public unary_function<CName,bool> {
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
383
384
string gesucht;
public:
find_Name(const string& gesucht_):gesucht(gesucht_) {}
bool operator ()(const CName& n)
{
return n.Nachname==gesucht;
}
};
class groesser:public unary_function<int,int>{
int value;
public:
groesser(int y):value(y) {};
bool operator()(int x) { return x>value; };
};
void Aufg4b(Container& v,string gesucht)
{
Iterator p=find_if(v.begin(),v.end(),find_Name(gesucht));
while (p!=v.end())
{
Form1->Memo1->Lines->Add(p->Nachname.c_str() );
p=find_if(p+1,v.end(),find_Name(gesucht));
}
}
void Aufg4()
{
vector<CName> v;
v.push_back( CName("Johann Sebastian","Mozart") );
v.push_back( CName("Johann Wolfgang","Bach") );
v.push_back( CName("Wolfgang Amadeus","Goethe") );
Aufg4a(v,"Mozart");
Aufg4b(v,"Mozart");
}
void __fastcall TForm1::Button4Click(TObject *Sender)
{
Aufg4();
}
12.9.4 Aufgabe 10.4
// Datei: d:\Loesungen\kap-10\10.4\IteratU.cpp
#include "IteratU.h"
//----- Aufgabe 1. -----------------------------------------------#include <fstream>
#include <algorithm>
using namespace std;
// a)
template<typename T,typename Iterator>
void WriteToFile(Iterator first, Iterator last, char* fn)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.9 Lösungen Kapitel 10
ofstream f(fn);
copy(first, last, ostream_iterator<T>(f,"\n"));
}
// b)
template<typename T> void CopyFile(char* fn1,char* fn2)
{
ifstream f1(fn1);
ofstream f2(fn2);
copy(istream_iterator<T>(f1),
istream_iterator<T>(),ostream_iterator<T>(f2,"\n"));
}
// c)
#include <iterator>
#include <vector>
template<typename T> void SortFile(char* fn1,char* fn2)
{
ifstream f1(fn1);
vector<T> v;
copy(istream_iterator<T>(f1),
istream_iterator<T>(),back_inserter(v));
sort(v.begin(),v.end());
ofstream f2(fn2);
copy(v.begin(), v.end(), ostream_iterator<T>(f2,"\n"));
}
// d)
#include <function>
template<typename T, typename Iterator>
bool is_sorted(Iterator first, Iterator last)
{
return adjacent_find(first,last,greater<T>())==last;
}
template<typename T>
bool FileIsSorted(char* fn)
{
ifstream f(fn);
return is_sorted<T>(istream_iterator<T>(f),istream_iterator<T>());
}
// e)
template<typename T> void print(T x)
{
Form1->Memo1->Lines->Add(x);
}
template<typename T> void ShowFile(char* fn)
{
print<char*>(fn);
ifstream f(fn);
for_each(istream_iterator<T>(f),istream_iterator<T>(),print<T>);
}
// g)
struct CBruch {
CBruch():z(0),n(1) {}
CBruch(int z_,int n_):z(z_),n(n_) {}
int z,n; // z: Zähler, n: Nenner
};
ostream& operator<<(ostream& f, const CBruch& b)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
385
386
12 Lösungen
{
return f<<b.z<<"|"<<b.n;
}
istream& operator>>(istream& f, CBruch& b)
{
char Bruchstrich;
f>>b.z>>Bruchstrich>>b.n;
return f;
}
void Aufg1()
{
// f)
int as[3]={1,2,3};
// sortiert
WriteToFile<int>(as,as+3,"s.txt");
int au[3]={1,3,2};
// unsortiert
WriteToFile<int>(au,au+3,"u.txt");
int a0[1];
WriteToFile<int>(a0,a0,"0.txt");
// 0 Elemente
int a1[1]={1};
WriteToFile<int>(a1,a1+1,"1.txt"); // 1 Element
bool
bool
bool
bool
b1=FileIsSorted<int>("s.txt");
b2=FileIsSorted<int>("u.txt");
b3=FileIsSorted<int>("0.txt");
b4=FileIsSorted<int>("1.txt");
CopyFile<int>("s.txt",
CopyFile<int>("u.txt",
CopyFile<int>("0.txt",
CopyFile<int>("1.txt",
"sc.txt");
"uc.txt");
"0c.txt");
"1c.txt");
SortFile<int>("s.txt",
SortFile<int>("u.txt",
SortFile<int>("0.txt",
SortFile<int>("1.txt",
"ss.txt");
"us.txt");
"0s.txt");
"1s.txt");
bool
bool
bool
bool
b1s=FileIsSorted<int>("ss.txt");
b2s=FileIsSorted<int>("us.txt");
b3s=FileIsSorted<int>("0s.txt");
b4s=FileIsSorted<int>("1s.txt");
ShowFile<int>("s.txt");
ShowFile<int>("u.txt");
ShowFile<int>("0.txt");
ShowFile<int>("1.txt");
ShowFile<int>("ss.txt");
ShowFile<int>("us.txt");
ShowFile<int>("0s.txt");
ShowFile<int>("1s.txt");
// g)
vector<CBruch> vb;
vb.push_back(CBruch(1,2));
vb.push_back(CBruch(1,3));
vb.push_back(CBruch(1,4));
WriteToFile<CBruch>(vb.begin(),vb.end(),"b.txt");
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
::Aufg1();
}
//----- Aufgabe 2. -----------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
template<> void print<CBruch>(CBruch x)
{
Form1->Memo1->Lines->Add(AnsiString(x.z)+"|"+AnsiString(x.n));
}
void Aufg2()
{
ifstream f("s.txt");
istream_iterator<int> beg1(f);
istream_iterator<int> end1;
vector<int> v1(beg1,end1);
for_each(v1.begin(),v1.end(),print<int>);
ifstream fb("b.txt");
istream_iterator<CBruch> beg2(fb);
istream_iterator<CBruch> end2;
vector<CBruch> vb(beg2,end2);
for_each(vb.begin(),vb.end(),print<CBruch>);
}
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
::Aufg2();
}
12.9.5 Aufgabe 10.5
// Datei: d:\Loesungen\kap-10\10.5\AlgoU.cpp
#include "AlgoU.h"
#include <vector>
#include <fstream>
using namespace std;
//------ Aufgabe 10.5.5 ------------------------------------------//----- Aufgabe 1. -----------------------------------------------// a)
template<typename Container1, typename Container2>
bool Equal(Container1 c1, Container2 c2)
{
return (c1.size()==c2.size()) && equal(c1.begin(),c1.end(),c2.begin());
}
// b)
template<typename Container>
int Count(Container c, typename Container::value_type x)
{
return count(c.begin(),c.end(),x);
};
// c)
template<typename T>
int CountFileElements(const char* fn, T y)
{ // Anzahl der Dateielemente mit dem Wert y
ifstream fi(fn);
return count(istream_iterator<T>(fi),istream_iterator<T>(),y);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
387
388
#include <set>
void Aufg1()
{
int a[5]={1,2,3,4,5};
vector<int> v(a,a+5);
set<char> s(a,a+5);
bool b1=Equal(s,v);
int i1=Count(v,1);
int i2=CountFileElements("c:\\test\\u.txt",1); // u.txt von Aufgabe 10.4
}
void __fastcall TForm1::Aufg1Click(TObject *Sender)
{
::Aufg1();
}
//----- Aufgabe 2. -----------------------------------------------// a)
template<typename T> T MinValue(char* fn)
{
ifstream fi(fn);
return *min_element(istream_iterator<T>(fi),istream_iterator<T>());
}
// b)
template<typename C, typename T> T MinValue(const C& a)
{
return *min_element(a.begin(),a.end());
}
// c)
template<typename T> T MinValue(T* a, int n)
{
return *min_element(a,a+n);
}
void Aufg2()
{
int i=MinValue<int>("c:\\test\\u.txt"); // u.txt von Aufgabe 10.4
int a[3]={3,2,5};
int ai=MinValue(a,3);
vector<int> v(a,a+3);
int vi=MinValue<vector<int>,int >(v);
}
void __fastcall TForm1::Aufg2Click(TObject *Sender)
{
::Aufg2();
}
//----- Aufgabe 3. -----------------------------------------------/* Zur Orientierung die Algorithmen aus stl.zip
template <class InputIterator, class Function>
Function for_each (InputIterator first,
InputIterator last, Function f)
{
while (first != last) f(*first++);
return f;
}
template <class InputIterator, class Predicate>
InputIterator find_if(InputIterator first, InputIterator
last, Predicate pred)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.9 Lösungen Kapitel 10
{
while (first != last && !pred(*first)) ++first;
return first;
}
string s="123 und 123";
string::iterator p=find(s.begin(),s.end(),'2');
while (p!=s.end())
{
Form1->Memo1->Lines->Add(*p);
p=find(p+1,s.end(),'2');
}
*/
// a)
template<typename Container, typename Predicate, typename Function>
void for_each_if(Container c, Predicate pred, Function f)
{
Container::iterator i=find_if(c.begin(),c.end(),pred);
while (i!=c.end())
{
f(*i);
i=find_if(++i,c.end(),pred);
}
}
// b)
template<typename T, typename Predicate, typename Function>
void for_each_if(T* a, int n, Predicate pred, Function f)
{
T* i=find_if(a,a+n,pred);
while (i!=a+n)
{
f(*i);
i=find_if(++i,a+n,pred);
}
}
// c)
template<typename T, typename Predicate, typename Function>
void for_each_if(char* fn, Predicate pred, Function f)
{
ifstream fi(fn);
const istream_iterator<T> end=istream_iterator<T>();
istream_iterator<T> i=find_if(istream_iterator<T>(fi),end,pred);
while (i!=end)
{
f(*i);
i=find_if(++i,end,pred);
}
}
bool all(int i)
{
return true;
}
bool odd(int i)
{
return i%2==1;
}
void print(int i)
{
Form1->Memo1->Lines->Add(i);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
389
390
bool first_odd(pair<int,int> i)
{
return i.first%2==1;
}
void print_pair(pair<int,int> i)
{
Form1->Memo1->Lines->Add(IntToStr(i.first)+": "+IntToStr(i.second));
}
// Auch diese Lösungsvarianten sind möglich:
template<typename Container, typename Predicate, typename Function>
void for_each_if_1(Container c, Predicate pred, Function f)
{
for (Container::iterator i=c.begin();i!=c.end();i++)
if (pred(*i)) f(*i);
}
template<typename T, typename Predicate, typename Function>
void for_each_if_1(T* a, int n, Predicate pred, Function f)
{
for (int i=0;i<n;i++)
if (pred(*(a+i))) f(*(a+i));
}
template<typename T, typename Predicate, typename Function>
void for_each_if_1(char* fn, Predicate pred, Function f)
{
ifstream fi(fn);
T i;
fi>>i;
while (!fi.eof())
{
if (pred(i)) f(i);
fi>>i;
}
}
#include <map>
void Aufg3()
{
int a[5]={1,2,3,4,5};
vector<int> vi(a,a+5);
Form1->Memo1->Lines->Add("1-5:");
// for_each_if(vi,all,print);
// for_each_if_1(vi,all,print);
// for_each_if(a, 5, all, print);
// for_each_if_1(a, 5, all, print);
Form1->Memo1->Lines->Add("u:");
// for_each_if<int>("c:\\test\\u.txt",all,print);
// for_each_if_1<int>("c:\\test\\u.txt",all,print);
Form1->Memo1->Lines->Add("odd:");
// for_each_if(vi,odd,print);
// for_each_if_1(vi,odd,print);
for_each_if(a, 5, odd, print);
for_each_if_1(a, 5, odd, print);
// return;
Form1->Memo1->Lines->Add("odd-2:");
for_each_if<int>("c:\\test\\u.txt",odd,print);
for_each_if_1(vi,all,print);
map<int,int> m;
m.insert(make_pair(1,1));
m.insert(make_pair(2,4));
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.9 Lösungen Kapitel 10
m.insert(make_pair(3,9));
for_each_if(m,first_odd,print_pair);
}
void __fastcall TForm1::Aufg3Click(TObject *Sender)
{
::Aufg3();
}
//----- Aufgabe 4. -----------------------------------------------/*
template<class ForwardIterator, class BinaryPredicate>
// Version 2
ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last,
BinaryPredicate pred);
...
if (first == last) return last;
ForwardIterator next = first;
while(++next != last) {
if (binary_pred(*first, *next)) return first;
first = next;
}
*/
void Action1(int i)
{
Form1->Memo1->Lines->Add(i);
}
void Level0_Start()
{
Form1->Memo1->Lines->Add("Level0_Start");
}
void Level0_End()
{
Form1->Memo1->Lines->Add("Level0_End");
}
void Level1_Start()
{
Form1->Memo1->Lines->Add("Level1_Start");
}
void Level1_End()
{
Form1->Memo1->Lines->Add("Level1_End");
}
template<typename Container, typename predicate>
void GW1(Container c, predicate pred)
{
Container::iterator first=c.begin(), last=first;
Level0_Start();
while (last!=c.end())
{
last=adjacent_find(first, c.end(),pred);
Level1_Start();
for_each(first,last!=c.end()?last+1:last,Action1);
Level1_End();
first=last+1;
}
Level0_End();
}
/*
template<typename T, typename Container, typename predicate>
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
391
392
void GW1_1(Container<T> c, predicate pred)
{
Container::iterator first=c.begin();
Level0_Start();
while (first!=c.end())
{
Level1_Start();
Action1(*first);
first++;
while (first!=c.end()&& !pred(*first,*(first-1)))
{
Action1(*first);
first++;
}
Level1_End();
}
Level0_End();
}
*/
bool mod10(int i, int j)
{ // Bedingung für einen Gruppenwechsel
return (i/10!=j/10);
}
#include <set>
void __fastcall TForm1::Aufg4Click(TObject *Sender)
{
// int a[3]={1}; vector<int> v(a,a+0); // leerer Container
// int a[1]={1}; vector<int> v(a,a+1); // ein Element
// int a[3]={1,3,9}; vector<int> v(a,a+3); // eine Gruppe
// int a[4]={1,3,9,11}; vector<int> v(a,a+4); // ein GW und ein Element
int a[5]={1,3,9,11,12}; vector<int> v(a,a+5);
GW1(v,mod10);
// GW1_1<int>(v,mod10);
/*
set<int> s(v.begin(),v.end());
GW1<int>(s,mod10);
*/
}
//----- Aufgabe 5. -----------------------------------------------/*
In algo.h aus "ftp://butler.hpl.hp.com/stl/stl.zip" sind
nur ältere Versionen von count und count_if enthalten, die
nicht mehr zum aktuellen C++-Standard gehören.
In der STL von SGI (http://www.sgi.com/tech/stl/download.html)
sind diese Algorithmen folgendermaßen implementiert:
*/
template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last, const T& value)
{
typename iterator_traits<InputIterator>::difference_type n = 0;
for ( ; first != last; ++first)
if (*first == value)
++n;
return n;
}
template <class InputIterator, class Predicate>
typename iterator_traits<InputIterator>::difference_type
count_if(InputIterator first, InputIterator last, Predicate pred)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.9 Lösungen Kapitel 10
{
typename iterator_traits<InputIterator>::difference_type n = 0;
for ( ; first != last; ++first)
if (pred(*first))
++n;
return n;
}
/* **** alte Versionen (nicht mehr im C++-Standard, aber aus STL.zip
template <class InputIterator, class T, class Size>
void count(InputIterator first, InputIterator last, const T& value,
Size& n) {
while (first != last)
if (*first++ == value) ++n;
}
template <class InputIterator, class Predicate, class Size>
void count_if(InputIterator first, InputIterator last, Predicate pred,
Size& n) {
while (first != last)
if (pred(*first++)) ++n;
}
*/
// In algo.h aus "ftp://butler.hpl.hp.com/stl/stl.zip" sind
// die Algorithmen min_element usw. folgendermaßen implementiert:
template <class ForwardIterator>
ForwardIterator min_element(ForwardIterator first, ForwardIterator last)
{
if (first == last) return first;
ForwardIterator result = first;
while (++first != last)
if (*first < *result) result = first;
return result;
}
template <class ForwardIterator, class Compare>
ForwardIterator min_element(ForwardIterator first, ForwardIterator last,
Compare comp)
{
if (first == last) return first;
ForwardIterator result = first;
while (++first != last)
if (comp(*first, *result)) result = first;
return result;
}
template <class ForwardIterator>
ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last)
{
if (first == last) return last;
ForwardIterator next = first;
while(++next != last) {
if (*first == *next) return first;
first = next;
}
return last;
}
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last,
BinaryPredicate binary_pred)
{
if (first == last) return last;
ForwardIterator next = first;
while(++next != last) {
if (binary_pred(*first, *next)) return first;
first = next;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
393
394
12 Lösungen
}
return last;
}
//-------------------------------------------------------------------template<typename Container, typename Function>
void for_each(Container c, Function f)
{
for_each(c.begin(),c.end(),f);
}
template<typename T, typename Function>
void for_each(char* fn, Function f)
{
ifstream fi(fn);
for_each(istream_iterator<T>(fi),istream_iterator<T>(),f);
}
template<typename T, typename Function>
void for_each(T* a, int n, Function f)
{
for_each(a, a+n, f);
}
void __fastcall TForm1::Aufg5Click(TObject *Sender)
{
int a[5]={1,5,2,4,4};
vector<int> v(a,a+5);
set<double> s(a,a+5);
for_each(v,print);
for_each(s,print);
for_each<int>("c:\\test\\u.txt",print);
for_each(a,5,print);
int i=count(v.begin(),v.end(),1);
int j=count_if(v.begin(),v.end(),odd);
int k=*min_element(v.begin(),v.end());
bool found=adjacent_find(v.begin(),v.end())!=v.end();
}
//------ Aufgabe 10.5.19 -------------------------------------------//----- Aufgabe 1. -----------------------------------------------template <typename Container>
typename Container::value_type Median(Container c)
{
nth_element(c.begin(),c.begin()+c.size()/2,c.end());
return *(c.begin()+c.size()/2);
}
void test_Median()
{
vector<double> v;
double md=Median(v);
}
void __fastcall TForm1::Aufg_1Click(TObject *Sender)
{
test_Median();
}
//----- Aufgabe 2. -----------------------------------------------template <typename Container1, typename Container2>
void MaxElements(Container1 src, int n, Container2& dest)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.9 Lösungen Kapitel 10
{
partial_sort(src.begin(),src.begin()+n,src.end(),
greater<typename Container1::value_type>());
copy(src.begin(),src.begin()+n,back_inserter(dest));
}
template <typename T, typename Container>
void MaxElements(char* fn, int n, Container& dest)
{
ifstream f(fn);
vector<T> v;
copy(istream_iterator<T>(f),istream_iterator<T>(),back_inserter(v));
MaxElements(v,n,dest);
}
template <typename T, typename Container2>
void MaxElements(T* src, int n, int m, Container2& dest)
{
partial_sort(src,src+n,src+m,greater<T>());
copy(src,src+n,back_inserter(dest));
}
void test_MaxElements()
{
int a[6]={1,2,3,4,5,6};
vector<int> v(a,a+6);
vector<int> s;
//MaxElements(v,3,s);
MaxElements(a,2,6,s);
for_each(s,print);
return;
MaxElements<int>("c:\\test\\u.txt",2,s);
// for_each(s,10,print);
for_each(s,print);
}
void __fastcall TForm1::Aufg_2Click(TObject *Sender)
{
test_MaxElements();
}
//----- Aufgabe 3. -----------------------------------------------template <typename Container>
void Remove(Container& c, typename Container::value_type x)
{
c.erase(remove(c.begin(), c.end(),x),c.end());
}
void test_Remove()
{
int a[4]={1,2,3,4};
vector<int> v(a,a+4);
Remove (v,2);
for_each(v,print);
}
void __fastcall TForm1::Aufg_3Click(TObject *Sender)
{
test_Remove();
}
//----- Aufgabe 4. -----------------------------------------------template <typename T>
void merge_files(char* in1fn, char* in2fn, char* outfn)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
395
396
12 Lösungen
ifstream in1(in1fn);
ifstream in2(in2fn);
ofstream out(outfn);
merge(istream_iterator<T>(in1),
istream_iterator<T>(),
istream_iterator<T>(in2),
istream_iterator<T>(),
ostream_iterator<T>(out,"\n"));
}
void make_file(char* fn, int n)
{
ofstream f(fn);
for (int i=0; i<n; i++)
f<<i<<endl;
}
void test_merge_files()
{
string s1="125", s2="126", s;
bool b1=binary_search(s1.begin(),s1.end(),'2'); // b=true
bool b2=binary_search(s1.begin(),s1.end(),'3'); // b=false
merge(s1.begin(),s1.end(),s2.begin(),s2.end(),back_inserter(s));
// s="112256"
char* in1fn="int1.dat";
char* in2fn="int2.dat";
char* outfn="int.dat";
make_file(in1fn, 100);
make_file(in2fn, 100);
merge_files<int>(in1fn,in2fn, outfn);
}
void __fastcall TForm1::Aufg_4Click(TObject *Sender)
{
test_merge_files();
}
//----- Aufgabe 5. -----------------------------------------------void write_call(vector<int> p, ofstream& f)
{
f<<"f(";
for (vector<int>::iterator i=p.begin();i!=p.end()-1;i++)
f<<*i<<",";
f<<*(p.end()-1)<<");"<<endl;
}
void make_calls()
{
ofstream f("calls.cpp");
int args[4]={1,2,3,4};
vector<int> p(args,args+4);
write_call(p,f);
while (next_permutation(p.begin(),p.end()))
write_call(p,f);
}
void __fastcall TForm1::Aufg_5Click(TObject *Sender)
{
make_calls();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
12.9.6 Aufgabe 10.5
// Datei: d:\Loesungen\kap-10\10.5\calls.cpp
f(1,2,3,4);
f(1,2,4,3);
f(1,3,2,4);
f(1,3,4,2);
f(1,4,2,3);
f(1,4,3,2);
f(2,1,3,4);
f(2,1,4,3);
f(2,3,1,4);
f(2,3,4,1);
f(2,4,1,3);
f(2,4,3,1);
f(3,1,2,4);
f(3,1,4,2);
f(3,2,1,4);
f(3,2,4,1);
f(3,4,1,2);
f(3,4,2,1);
f(4,1,2,3);
f(4,1,3,2);
f(4,2,1,3);
f(4,2,3,1);
f(4,3,1,2);
f(4,3,2,1);
12.10 Lösungen Kapitel 11
12.10.1 Aufgabe 11.1
// Datei: d:\Loesungen\kap-11\11.1\InitOGL.cpp
HDC InitOpenGL(HWND Handle,bool UseDoubleBuffer)
{
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd,0,sizeof(pfd)); // setze alle Felder auf 0
pfd.nSize
= sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion
= 1; // Versionsnummer, 1 notwendig
pfd.dwFlags
=
// Eigenschaften des Pixelpuffers
PFD_DRAW_TO_WINDOW | // Zeichne in ein Fenster
PFD_SUPPORT_OPENGL; // OpenGL anstelle von GDI
if (UseDoubleBuffer)// Doppelbuffer verwenden
pfd.dwFlags |= PFD_DOUBLEBUFFER; // flag setzen
pfd.iPixelType = PFD_TYPE_RGBA; // RGBA Pixel
pfd.cColorBits = 24; // Farbtiefe 24 Bit
pfd.cDepthBits = 32; // Tiefenpuffer 32 Bits
pfd.iLayerType = PFD_MAIN_PLANE; // Einzige Möglichkeit
HDC dc = GetDC(Handle);
int iPF = ChoosePixelFormat(dc, &pfd);
if (iPF==0) ShowMessage("Fehler bei ChoosePixelFormat");
SetPixelFormat(dc, iPF, &pfd );
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
397
398
12 Lösungen
GLContext= wglCreateContext(dc);
if (GLContext) wglMakeCurrent(dc, GLContext);
else ShowMessage("wglMakeCurrent nicht erfolgreich");
return dc;
}
12.10.2 Aufgabe 11.1
// Datei: d:\Loesungen\kap-11\11.1\Lights.cpp
#include "Lights.h"
TForm2 *Form2;
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
#include <gl\gl.h>
void DrawScene();
void SetLightPosition(GLdouble x,GLdouble y,GLdouble z)
{
GLfloat position[] = {50-x, 50-y, 50-z, 1}; // w!=0: position, w==0: direction
glLightfv(GL_LIGHT0, GL_POSITION, position);
DrawScene();
Form2->LabAmbLightXPosValue->Caption=FloatToStr(position[0]);
Form2->LabAmbLightYPosValue->Caption=FloatToStr(position[1]);
Form2->LabAmbLightZPosValue->Caption=FloatToStr(position[2]);
}
void __fastcall TForm2::SBPositionXChange(TObject *Sender)
{
SetLightPosition(SBPositionX->Position,SBPositionY->Position,SBPositionZ>Position);
}
void __fastcall TForm2::SBPositionYChange(TObject *Sender)
{
SBPositionXChange(Sender); // same effect as
// SetLightPosition(SBPositionX->Position,SBPositionY->Position,SBPositionZ>Position);
}
void __fastcall TForm2::SBPositionZChange(TObject *Sender)
{
SBPositionXChange(Sender);
}
void SetAmbientLight(GLdouble r,GLdouble g,GLdouble b)
{
GLfloat light[] = {r/100,g/100,b/100, 1};
glLightfv(GL_LIGHT0, GL_AMBIENT, light);
DrawScene();
Form2->LabAmbLightRedValue->Caption=FloatToStr(light[0]);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
399
Form2->LabAmbLightGreenValue->Caption=FloatToStr(light[1]);
Form2->LabAmbLightBlueValue->Caption=FloatToStr(light[2]);
}
void __fastcall TForm2::SBAmbLightRedChange(TObject *Sender)
{
SetAmbientLight(SBAmbLightRed->Position,SBAmbLightGreen->Position,SBAmbLightBlue>Position);
}
void __fastcall TForm2::SBAmbLightGreenChange(TObject *Sender)
{
SBAmbLightRedChange(Sender);
}
void __fastcall TForm2::SBAmbLightBlueChange(TObject *Sender)
{
SBAmbLightRedChange(Sender);
}
void SetDiffuseAndSpecularLight(GLdouble r,GLdouble g,GLdouble b)
{
GLfloat light[] = {r/100,g/100,b/100, 1};
// Woo p. 182: For realistic effects, set specular
// and diffuse light to the same values.
glLightfv(GL_LIGHT0, GL_DIFFUSE, light);
glLightfv(GL_LIGHT0, GL_SPECULAR, light);
DrawScene();
Form2->LabDiffSpecLightRedValue->Caption=FloatToStr(light[0]);
Form2->LabDiffSpecLightGreenValue->Caption=FloatToStr(light[1]);
Form2->LabDiffSpecLightBlueValue->Caption=FloatToStr(light[2]);
}
void __fastcall TForm2::SBDiffSpecLightRedChange(TObject *Sender)
{
SetDiffuseAndSpecularLight(SBDiffSpecLightRed->Position,SBDiffSpecLightGreen>Position,SBDiffSpecLightBlue->Position);
}
void __fastcall TForm2::SBDiffSpecLightGreenChange(TObject *Sender)
{
SBDiffSpecLightRedChange(Sender);
}
void __fastcall TForm2::SBDiffSpecLightBlueChange(TObject *Sender)
{
SBDiffSpecLightRedChange(Sender);
}
void SetAmbAndDiffuseMaterial(GLdouble r,GLdouble g,GLdouble b)
{
GLfloat mat[] = {r/100,g/100,b/100, 1};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat);
DrawScene();
Form2->LabMatRed->Caption=FloatToStr(mat[0]);
Form2->LabMatGreen->Caption=FloatToStr(mat[1]);
Form2->LabMatBlue->Caption=FloatToStr(mat[2]);
}
void __fastcall TForm2::SBAmbDiffMatRedChange(TObject *Sender)
{
SetAmbAndDiffuseMaterial(SBAmbDiffMatRed->Position,SBAmbDiffMatGreen>Position,SBAmbDiffMatBlue->Position);
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
400
void __fastcall TForm2::SBAmbDiffMatGreenChange(TObject *Sender)
{
SBAmbDiffMatRedChange(Sender);
}
void __fastcall TForm2::SBAmbDiffMatBlueChange(TObject *Sender)
{
SBAmbDiffMatRedChange(Sender);
}
void SetSpecularMaterial(GLdouble r,GLdouble g,GLdouble b)
{
GLfloat mat[] = {r/100, g/100, b/100, 1};
glMaterialfv(GL_FRONT, GL_SPECULAR, mat);
DrawScene();
Form2->LabMatSpecRed->Caption=FloatToStr(mat[0]);
Form2->LabMatSpecGreen->Caption=FloatToStr(mat[1]);
Form2->LabMatSpecBlue->Caption=FloatToStr(mat[2]);
}
void __fastcall TForm2::SBSpecMatRedChange(TObject *Sender)
{
SetSpecularMaterial(SBSpecMatRed->Position,SBSpecMatGreen>Position,SBSpecMatBlue->Position);
}
void __fastcall TForm2::SBSpecMatGreenChange(TObject *Sender)
{
SBSpecMatRedChange(Sender);
}
void __fastcall TForm2::SBSpecMatBlueChange(TObject *Sender)
{
SBSpecMatRedChange(Sender);
}
void __fastcall TForm2::SBMatShininessChange(TObject *Sender)
{
GLfloat mat_shininess[] = {SBMatShininess->Position};
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
DrawScene();
Form2->LabMatShininess->Caption=FloatToStr(mat_shininess[0]);
}
12.10.3 Aufgabe 11.1
// Datei: d:\Loesungen\kap-11\11.1\OpenGLU.cpp
#include "OpenGLU.h"
#include <float.h> // for _control87
//-------------------------------------------------------------------void DrawScene();
HDC dc;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.10 Lösungen Kapitel 11
401
HGLRC GLContext;
#include "InitOGL.cpp";
const bool UseDoubleBuffer = true; // with false images may flicker
#include <gl/gl.h>
#include <gl/glu.h>
void __fastcall TForm1::FormCreate(TObject *Sender)
{
dc=InitOpenGL(Form1->Handle,UseDoubleBuffer);
glEnable(GL_DEPTH_TEST); // necessary for the depthbuffer
glClearColor(1, 1, 1, 1); // white background
// damit ein Betrachter im Ursprung das O
glMatrixMode(GL_MODELVIEW);
// verschiebt den Ursprung nach hinten,
glTranslated(0,0,-2);
// die folgenden Anweisungen sind nur für die Bitmaps
StatusBar1->Visible=false;
StatusBar1->SimplePanel=false;
StatusBar1->SimpleText="R: Rotate, T: Translate, up, down, left, right, shift-up,
shift-down";
Scene->Visible =true;
Lights->Visible =true;
glLineWidth(3);
}
//-------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender)
{
ReleaseDC(Handle,dc);
wglMakeCurrent(dc, NULL);
wglDeleteContext(GLContext);
}
void __fastcall TForm1::FormResize(TObject *Sender)
{ // FormResize is called after FormCreate
glViewport(0, 0, ClientWidth, ClientHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLdouble aspect =(double)ClientWidth/ClientHeight;
// viewing angle 60°, aspect=Width/Height, für z<1 und z>10 ausblenden:
gluPerspective(60, aspect, 0.1, 100);
// make the modelview matrix the actual matrix
glMatrixMode(GL_MODELVIEW);
DrawScene();
}
void __fastcall TForm1::FormPaint(TObject *Sender)
{
FormResize(Sender);
}
//-------------------------------------------------------------------GLfloat colRed[]
= {1, 0, 0},
colGreen[]
= {0, 1, 0},
colBlue[]
= {0, 0, 1},
colYellow[]
= {1, 1, 0},
colDarkGray[] = {0.2, 0.2, 0.2},
colGray[]
= {0.5, 0.5, 0.5},
colLightGray[] = {0.8, 0.8, 0.8},
// colCyan[]
= {0, 1, 1},
// colMagenta[]
= {1, 0, 1},
colBlack[]
= {0, 0, 0};
//-------------------------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
402
12 Lösungen
void CoordinateSystem(GLdouble x, GLdouble y, GLdouble z)
{
glBegin(GL_LINES);
glColor3fv(colRed);
glVertex3d(-x, 0, 0);
glVertex3d(x, 0, 0);
glColor3fv(colGreen);
glVertex3d(0, -y, 0);
glVertex3d(0, y, 0);
glColor3fv(colBlue);
glVertex3d(0, 0, z);
glVertex3d(0, 0, -z);
glEnd();
double h=0.05;
glBegin(GL_TRIANGLES); // Arrow at the end of the x-Axis
glColor3fv(colRed);
glVertex3d(x-h, h, 0);
glVertex3d(x, 0, 0);
glVertex3d(x-h, -h, 0);
glEnd();
glBegin(GL_TRIANGLES);
glColor3fv(colGreen);
glVertex3d(+h,y-h, 0);
glVertex3d(0, y, 0);
glVertex3d(-h,y-h, 0);
glEnd();
glBegin(GL_TRIANGLES);
glColor3fv(colBlue);
glVertex3d(0,+h, z-h);
glVertex3d(0, 0, z);
glVertex3d(0,-h, z-h);
glEnd();
// glScaled(x,y,z);
}
void Quad(GLdouble n[], GLdouble v0[], GLdouble v1[],
GLdouble v2[],GLdouble v3[], GLenum mode)
{ // Viereck mit den Eckpunkten v0, v1, v2 und v3
glBegin(mode);
glNormal3dv(n); // Normale
glVertex3dv(v0);
glVertex3dv(v1);
glVertex3dv(v2);
glVertex3dv(v3);
glEnd();
}
/*
void Quader(GLdouble x0, GLdouble y0, GLdouble z0,
GLdouble x1, GLdouble y1, GLdouble z1, GLenum mode)
{
GLdouble tmp;
// sicherstellen, daß x0<=x1, y0<=y1 und z0<=z1 gilt
if (x0 > x1) {tmp = x0; x0 = x1; x1 = tmp;}
if (y0 > y1) {tmp = y0; y0 = y1; y1 = tmp;}
if (z0 > z1) {tmp = z0; z0 = z1; z1 = tmp;}
GLdouble v[8][3] =
{{x0,y0,z1},
{x1,y0,z1},
{x1,y1,z1},
{x0,y1,z1},
{x0,y0,z0},
{x1,y0,z0},
{x1,y1,z0},
// Eckpunkte
//=v[0]
//=v[1]
//=v[2]
//=v[3]
//=v[4]
//=v[5]
//=v[6]
v[7] ---- v[6]
/|
/|
v[3] ---- v[2]|
| |
| |
| |
| |
| |
| |
| v[4] ---| v[5]
|/
|/
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
403
{x0,y1,z0}};//=v[7]
GLdouble Normale_vorn[] =
Normale_hinten[]=
Normale_rechts[]=
Normale_links[] =
Normale_oben[] =
Normale_unten[] =
v[0] ---- v[1]
{ 0, 0, 1},
{ 0, 0,-1},
{ 1, 0, 0},
{-1, 0, 0},
{ 0, 1, 0},
{ 0,-1, 0};
Viereck(Normale_vorn, v[0],v[1],v[2],v[3],mode); // vorne
Viereck(Normale_rechts,v[1],v[5],v[6],v[2],mode); // rechts
Viereck(Normale_hinten,v[5],v[4],v[7],v[6],mode); // hinten
Viereck(Normale_links, v[4],v[0],v[3],v[7],mode); // links
Viereck(Normale_oben, v[3],v[2],v[6],v[7],mode); // oben
Viereck(Normale_unten, v[1],v[0],v[4],v[5],mode); // unten
// von außen betrachtet alle im Gegen-Uhrzeigersinn, OpenGL Prog Guide S. 82
}
*/
void ColoredCube(GLdouble x0, GLdouble y0, GLdouble z0,
GLdouble x1, GLdouble y1, GLdouble z1, GLenum mode)
{ // wire cube: mode=GL_LINE_LOOP, full sides: mode=GL_QUADS
// to ensure x0<=x1, y0<=y1 und z0<=z1:
if (x0 > x1) {GLdouble tmp = x0; x0 = x1; x1 = tmp;}
if (y0 > y1) {GLdouble tmp = y0; y0 = y1; y1 = tmp;}
if (z0 > z1) {GLdouble tmp = z0; z0 = z1; z1 = tmp;}
GLdouble v[8][3] =
// corners
{{x0,y0,z1}, //=v[0]
{x1,y0,z1}, //=v[1]
{x1,y1,z1}, //=v[2]
{x0,y1,z1}, //=v[3]
{x0,y0,z0}, //=v[4]
{x1,y0,z0}, //=v[5]
{x1,y1,z0}, //=v[6]
{x0,y1,z0}};//=v[7]
v[7] ---- v[6]
/|
/|
v[3] ---- v[2]|
| |
| |
| |
| |
| |
| |
| v[4] ---| v[5]
|/
|/
v[0] ---- v[1]
GLdouble FrontNormal[]={0,0,1}, BackNormal[]={ 0, 0,-1},
RightNormal[]={1,0,0}, LeftNormal[]={-1, 0, 0},
UpNormal[]
={0,1,0}, DownNormal[]={ 0,-1, 0};
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,colBlue);
glColor3fv(colRed);
Quad(FrontNormal, v[0],v[1],v[2],v[3],mode); // front
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,colGreen);
glColor3fv(colGreen);
Quad(RightNormal,v[1],v[5],v[6],v[2],mode); // right
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,colBlue);
glColor3fv(colBlue);
Quad(BackNormal,v[5],v[4],v[7],v[6],mode); // back
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,colYellow);
glColor3fv(colYellow);
Quad(LeftNormal, v[4],v[0],v[3],v[7],mode); // left
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,colLightGray);
glColor3fv(colLightGray);
Quad(UpNormal, v[3],v[2],v[6],v[7],mode); // up
// glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,colDarkGray);
glColor3fv(colDarkGray);
Quad(DownNormal, v[1],v[0],v[4],v[5],mode); // down
}
void City()
{
srand(1); // always the same random numbers
const int n = 10;
const double w=2.0/n;
for (int x=-n/2; x<n/2; x++)
for (int z=-n/2; z<n/2; z++)
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
404
12 Lösungen
ColoredCube((x-w)/n, 0,
(z-w)/n,
(x+w)/n, (1+rand()%8)/10.0, (z+w)/n, GL_QUADS);
}
/*
void glutWireSphere(GLdouble radius, GLint slices, GLint stacks)
{ // from glut-3.7\lib\glut\glut_shapes.c
QUAD_OBJ_INIT();
gluQuadricDrawStyle(quadObj, GLU_LINE);
gluQuadricNormals(quadObj, GLU_SMOOTH);
// If we ever changed/used the texture or orientation state
// of quadObj, we'd need to change it to the defaults here
// with gluQuadricTexture and/or gluQuadricOrientation.
gluSphere(quadObj, radius, slices, stacks);
}
void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
{ // from glut-3.7\lib\glut\glut_shapes.c
QUAD_OBJ_INIT();
gluQuadricDrawStyle(quadObj, GLU_FILL);
gluQuadricNormals(quadObj, GLU_SMOOTH);
// If we ever changed/used the texture or orientation state
// of quadObj, we'd need to change it to the defaults here
// with gluQuadricTexture and/or gluQuadricOrientation.
gluSphere(quadObj, radius, slices, stacks);
}
*/
void Sphere(GLdouble radius, GLint slices, GLenum style, GLfloat* color)
{ // ähnlich wie in glut-3.7\lib\glut\glut_shapes.c
static GLUquadricObj *quadObj=gluNewQuadric();
gluQuadricDrawStyle(quadObj, style);
gluQuadricNormals(quadObj, GLU_SMOOTH);
glColor3fv(color);
gluSphere(quadObj, radius, slices, slices/*stacks*/);
}
void Cylinder(GLdouble baseRadius, GLdouble topRadius, GLdouble height, GLenum
style, GLfloat* color)
{ // ähnlich wie in glut-3.7\lib\glut\glut_shapes.c
static GLUquadricObj *quadObj=gluNewQuadric();
gluQuadricDrawStyle (quadObj, style);
gluQuadricNormals(quadObj, GLU_SMOOTH);
GLint slices=30, stacks=10;
glColor3fv(color);
gluCylinder(quadObj, baseRadius, topRadius, height, slices, stacks);
}
void Speichenrad(int n)
{
glPushMatrix();
Sphere(0.2,20,GLU_FILL,colRed); // Kugel im Mittelpunkt
for (int i=0; i<n; i++)
{
glRotated(360.0/n,0,0,1); // 360/n Grad weiter drehen
glPushMatrix();
glRotated(-90,1,0,0); // in die x-z-Ebene drehen
Cylinder(0.02, 0.02, 1,GLU_FILL, colGray);
glTranslated(0,0,1);
Sphere(0.1,20,GLU_FILL,colBlue);
glPopMatrix();
}
glPopMatrix();
}
void IndependentMotions()
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
static int angle=0;
angle=angle+5;
glPushMatrix();
glTranslated(1,0,0);
glRotated(angle-10,0,0,1);
Speichenrad(10);
glPopMatrix();
glPushMatrix();
glTranslated(-1,0,0);
glRotated(-angle,0,0,1);
Speichenrad(10);
glPopMatrix();
}
// für RotateTranslate:
void InitScene()
{ // Initialisiere die Szene so, dass die Modelle
// beim Aufruf von RotateTranslate ins Bild passen
glLoadIdentity();
glTranslated(-0.8,-0.1,-2.5);
}
void Modell()
{
CoordinateSystem(1,1,1);
ColoredCube(-0.1, -0.1, -0.1, 0.1, 0.1, 0.1, GL_QUADS);
}
void RotateTranslate(bool b)
{ // b==true: first translate, then rotate
// b==false: first rotate, then translate
InitScene();
Modell();
if (b) glTranslated(1.5,0,0);
glRotated(20,0,0,1);
if (!b) glTranslated(1.5,0,0);
Modell();
}
void InitLights()
{
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
GLfloat light_position[] = {1, 5, 5, 0};
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
GLfloat mat[] = { 0, 0, 1, 1};
glMaterialfv(GL_FRONT, GL_AMBIENT, mat);
GLfloat light[] = { 0, 0, 0.7, 1.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, light);
}
//----- Aufgabe 1. a) -----------------------------------------------void Arrow()
{
glBegin(GL_LINES);
glVertex3d(-1, 0, 0);
glVertex3d(1, 0, 0);
glEnd();
double h=0.05;
glBegin(GL_TRIANGLES);
glVertex3d(1-h, h, 0);
glVertex3d(1, 0, 0);
glVertex3d(1-h, -h, 0);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
405
406
glEnd();
}
void CoordinateSystem()
{
glPushMatrix();
glColor3fv(colRed);
Arrow();
glPushMatrix();
glRotated(90,0,0,1);
glColor3fv(colGreen);
Arrow();
glPopMatrix();
glPushMatrix();
glRotated(-90,0,1,0);
glColor3fv(colBlue);
Arrow();
glPopMatrix();
glPopMatrix();
}
//----- Aufgabe 1. b) -----------------------------------------------void H2O(double radH=0.2, double radO=0.1, double dist=0.8)
{ // simple model of a water molecule
glPushMatrix();
Sphere(radH,20,GLU_FILL,colRed); // H
for (int i=0; i<2; i++)
// 2O
{
glRotated(120,0,0,1);
glPushMatrix();
glRotated(-90,1,0,0); // in die x-z-Ebene drehen
Cylinder(0.02, 0.02, dist,GLU_FILL, colYellow);
glTranslated(0,0,dist);
Sphere(radO,20,GLU_FILL,colBlue);
glPopMatrix();
}
glPopMatrix();
}
//----- Aufgabe 1. c) -----------------------------------------------void RotatingCubeAndCylinder()
{
static int angle=0;
angle=angle+5;
glPushMatrix();
glTranslated(1,0,0);
glRotated(angle-10,0,1,1);
double w=0.2;
ColoredCube(-w, -w, -w, w, w, w, GL_QUADS);
glPopMatrix();
glPushMatrix();
glTranslated(-1,0,0);
glRotated(-angle,1,0,1);
Cylinder(0.2, 0, 1 ,GLU_FILL, colYellow);
glPopMatrix();
}
//----- Aufgabe 1. d) -----------------------------------------------void City2(const int n)
{
srand(1); // Jedesmal dieselbe Folge von Zufallszahlen
const double w=2.0/n;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.10 Lösungen Kapitel 11
for (int x=-n/2; x<n/2; x++)
for (int z=-n/2; z<n/2; z++)
{
glPushMatrix();
glTranslated((x-w)/n,0,(z-w)/n);
ColoredCube(0, 0, 0,
2*w/n, (1+rand()%8)/10.0, 2*w/n, GL_QUADS);
glPopMatrix();
}
}
//----- Aufgabe 1. e) -----------------------------------------------void Robot()
{
static int i=0, angle;
i++;
if (i==36) i=0;
if (i<18) angle=5*i;
else angle=180-5*i;
glPushMatrix();
glRotatef (-90, 1, 0, 0);
Cylinder(0.2, 0.2, 1, GLU_FILL, colGreen); // body
glPopMatrix();
glPushMatrix();
glTranslatef (0, 1.3, 0);
Sphere(0.3,20,GLU_FILL,colBlue); // head
glPopMatrix();
glPushMatrix();
glRotatef (-90, 1, 0, 0);
glTranslatef (0, 0.2, 0.8);
glRotatef (90+angle, 0, 1, 0);
Cylinder(0.1, 0.1, 0.6, GLU_FILL, colRed); // arm
glPopMatrix();
glPushMatrix();
glRotatef (-90, 1, 0, 0);
glTranslatef (0, -0.2, 0.8);
glRotatef (90+angle, 0, 1, 0);
Cylinder(0.1, 0.1, 0.6, GLU_FILL, colRed); // arm
glPopMatrix();
}
//----- Aufgabe 1. f) -----------------------------------------------void Solarsystem(bool LocalTransf, GLenum style, bool TexturedEarth, bool
TwoMoons)
{
void TexturedSphere(GLdouble radius); // in textures.cpp
if (LocalTransf) glPushMatrix(); // siehe Abschnitt 11.1.5
else glLoadIdentity();
static int time = 0, day = 0;
const int incr = 25; // in hours
time = time+incr;
day = day+time/24;
time = time%24;
day = day %365;
// look from above:
glTranslated(0, 0, -10.0);
glRotated(10, 1, 0, 0);
Sphere(1,50,GLU_FILL,colYellow); // yellow sun
// The earth rotates in 365 days round the sun:
glRotated(360.0*day/365, 0, 1, 0); // rotate around the y-axis
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
407
408
12 Lösungen
glTranslated(4, 0, 0); // distance earth - moon
// The earth rotates in 24 hours around her axis:
glRotatef(360.0*time/24.0, 0, 1, 0);
if (LocalTransf)
{
glPushMatrix();
glRotatef(90,1,0,0); // north pole up
if (TexturedEarth) TexturedSphere(0.5);
// With style=GLU_LINE and slices=5 one sees the
// rotatation of the earth around her axis:
else Sphere(0.5,5,style,colBlue); // blue earth
glPopMatrix();
}
else if (TexturedEarth) TexturedSphere(0.5);
else Sphere(0.5,5,style,colBlue); // blue earth
// The moon rotates 12.5 times each year around the earth. He doesn't
// rotate around an axis, but shows always the same side to the earth:
if (TwoMoons) glPushMatrix();
glRotated(360.0*12.5*day/365, 0, 1, 0);
glTranslated(2, 0, 0); // distance earth - moon
Sphere(0.2,50,GLU_FILL,colRed);// red moon
if (TwoMoons)
{
glPopMatrix();
glRotated(360.0*9*day/365, 0, 1, 0);
glTranslated(1.5, 0, 0); // distance earth - moon
Sphere(0.2,50,GLU_FILL,colGreen);// green moon
}
if (LocalTransf) glPopMatrix();
}
// Text:
void Solarsystem(bool LocalTransf, GLenum style)
{
if (LocalTransf) glPushMatrix(); // siehe Abschnitt 11.1.5
else glLoadIdentity();
static int time = 0, day = 0;
const int incr = 25; // in hours
time = time+incr;
day = day+time/24;
time = time%24;
day = day %365;
// look from above:
glTranslated(0, 0, -10.0);
glRotated(10, 1, 0, 0);
Sphere(1,50,GLU_FILL,colYellow); // yellow sun
// The earth rotates in 365 days round the sun:
glRotated(360.0*day/365, 0, 1, 0); // rotate around the y-axis
glTranslated(4, 0, 0); // distance earth - sun
// The earth rotates in 24 hours around her axis:
glRotatef(360.0*time/24.0, 0, 1, 0);
Sphere(0.5,5,style,colBlue);
// blue earth
// The moon rotates 12.5 times each year around the earth. He doesn't
// rotate around an axis, but shows always the same side to the earth:
glRotated((360*12.5*day)/365, 0, 1, 0);
glTranslated(2, 0, 0); // distance earth - moon
Sphere(0.2,50,GLU_FILL,colRed);// red moon
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
if (LocalTransf) glPopMatrix();
}
//----- Aufgabe 2. -----------------------------------------------enum {trRotate, trTranslate} Transformation=trTranslate;
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if (Key=='R') Transformation=trRotate;
else if (Key=='T') Transformation=trTranslate;
bool ShiftPressed=Shift.Contains(ssShift);
if (Transformation==trTranslate)
{ //VK_UP, VK_DOWN: Pfeiltasten hoch, tief usw.
if
(Key==VK_UP
&& !ShiftPressed) glTranslated(0,0, 0.1);
else if (Key==VK_DOWN && !ShiftPressed) glTranslated(0,0,-0.1);
else if (Key==VK_UP)
glTranslated(0, 0.1, 0);
else if (Key==VK_DOWN) glTranslated(0,-0.1, 0);
else if (Key==VK_LEFT) glTranslated(-0.1, 0, 0);
else if (Key==VK_RIGHT) glTranslated( 0.1, 0, 0);
}
else if (Transformation==trRotate)
{
if(Key==VK_UP && !ShiftPressed)
glRotated(-5, 1, 0, 0);
else if(Key == VK_DOWN && !ShiftPressed)
glRotated(5, 1, 0, 0);
else if(Key == VK_UP)
glRotated(-5, 0, 0, 1);
else if(Key == VK_DOWN) glRotated(5, 0, 0, 1);
else if(Key == VK_LEFT) glRotated(-5, 0, 1, 0);
else if(Key == VK_RIGHT) glRotated(5, 0, 1, 0);
}
DrawScene();
}
//----- Aufgabe 3. -----------------------------------------------#include "Lights.h"
void __fastcall TForm1::LightsOnClick(TObject *Sender)
{
InitLights();
Form2->Show();
Form1->LightsOn->Checked=true;
Form1->LightsOff->Checked=false;
}
void __fastcall TForm1::LightsOffClick(TObject *Sender)
{
Form1->LightsOn->Checked=false;
Form1->LightsOff->Checked=true;
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
}
enum {modCity, modSolarsystemOneMoon, modSolarsystemTwoMoons,
modIndependentMotions, modRobot, modTree , modOnePaperplane,
modSomeAnimatedPaperplanes, modSimpleTexturedFigures,
modRotatingCubeAndCylinder, modH2O, modSolarsystemTexturedEarth,
modSimpleTexturedRoom} Model=modCity;
bool ShowCoordinateSystem = true;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
409
410
void __fastcall TForm1::CoordinateSystemClick(TObject *Sender)
{
ShowCoordinateSystem =! ShowCoordinateSystem;
CoordinateSystem->Checked =ShowCoordinateSystem;
}
void __fastcall TForm1::rotatingcubeandcylinder1Click(TObject *Sender)
{
Model=modRotatingCubeAndCylinder;
}
void __fastcall TForm1::City1Click(TObject *Sender)
{
Model=modCity;
}
void __fastcall TForm1::H201Click(TObject *Sender)
{
Model=modH2O;
}
void __fastcall TForm1::RobotClick(TObject *Sender)
{
Model=modRobot;
}
void __fastcall TForm1::Solarsystem1Click(TObject *Sender)
{
Model=modSolarsystemOneMoon;
}
void __fastcall TForm1::SolarsystemTwoMoonsClick(TObject *Sender)
{
Model=modSolarsystemTwoMoons;
}
void __fastcall TForm1::independantmotions1Click(TObject *Sender)
{
Model=modIndependentMotions;
}
void __fastcall TForm1::recursiveTree1Click(TObject *Sender)
{
Model=modTree;
}
void __fastcall TForm1::onepaperplane1Click(TObject *Sender)
{
Model=modOnePaperplane;
}
void __fastcall TForm1::someanimatedpaperplanes1Click(TObject *Sender)
{
Model=modSomeAnimatedPaperplanes;
}
void __fastcall TForm1::texturedearth1Click(TObject *Sender)
{
Model=modSolarsystemTexturedEarth;
}
//----- Aufgabe 4. -----------------------------------------------#include "Textures.cpp"
void __fastcall TForm1::LoadBitmapClick(TObject *Sender)
{
if (OpenPictureDialog1->Execute())
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.10 Lösungen Kapitel 11
411
LoadWindowsBitmap(OpenPictureDialog1->FileName.c_str());
InitTexture();
DrawScene();
}
}
//----- Aufgabe 4. a) -----------------------------------------------void __fastcall TForm1::SimpleTextureClick(TObject *Sender)
{
MakeTexture();
InitTexture();
DrawScene();
}
void __fastcall TForm1::SimpleFigures1Click(TObject *Sender)
{
Model=modSimpleTexturedFigures;
DrawScene();
}
//----- Aufgabe 4. a) -----------------------------------------------bool inSimpleTexturedRoom=false;
void __fastcall TForm1::simpleroom1Click(TObject *Sender)
{
inSimpleTexturedRoom=!inSimpleTexturedRoom;
}
#include "RecTree.cpp"
#include "paperplane.cpp"
void DrawScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
double w=5;
if (inSimpleTexturedRoom)
TexturedCube(-w, -w, -2*w, w, w, 2*w, GL_QUADS);
// Model =-1;
// RotateTranslate(false);
// Solarsystem(false, GLU_LINE);
if (ShowCoordinateSystem)
CoordinateSystem();
if (Model==modH2O) H2O();
else if (Model==modRotatingCubeAndCylinder) RotatingCubeAndCylinder();
else if (Model==modCity) City2(10);
else if (Model==modRobot) Robot();
// void Solarsystem(bool PushPop, GLenum style, bool TexturedEarth, bool
TwoMoons)
else if (Model==modSolarsystemTwoMoons) Solarsystem(true,GLU_FILL,false,true);
else if (Model==modSolarsystemOneMoon) Solarsystem(false,GLU_LINE,false,false);
else if (Model==modIndependentMotions) IndependentMotions();
else if (Model==modOnePaperplane) Paperplane(true);
else if (Model==modSomeAnimatedPaperplanes) AnimatedPaperplanes(3,false);
else if (Model==modSolarsystemTexturedEarth)
Solarsystem(true,GLU_LINE,true,false);
else if (Model==modSimpleTexturedRoom) TexturedCube(-2, -2, -10, 2, 2, 10,
GL_QUADS);
else if (Model==modSimpleTexturedFigures) TexturedFigures();
else if (Model==modTree)
{
bool LightsOn=glIsEnabled(GL_LIGHTING);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
412
12 Lösungen
if (!LightsOn)
{
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}
RecursiveTree(6);
if (!LightsOn)
{
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
}
}
if (UseDoubleBuffer) SwapBuffers(dc);
else glFlush();
}
12.10.4 Aufgabe 11.1
// Datei: d:\Loesungen\kap-11\11.1\paperplane.cpp
#include <math.h>
void Normal3P(const double v1[3], const double v2[3], const double v3[3])//,
double n[3])
{ // calculates normal to the points v1, v2, v3
double v[3]={v1[0]-v2[0],v1[1]-v2[1],v1[2]-v2[2]},//v1-v2
w[3]={v2[0]-v3[0],v2[1]-v3[1],v2[2]-v3[2]},//v2-v3
c[3]={v[1]*w[2]-v[2]*w[1],v[2]*w[0]-v[0]*w[2],
v[0]*w[1] - v[1]*w[0]},
d=sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]);
if (d==0) d=1; // nicht schön, aber auch nicht schlimm
double n[3]={c[0]/d,c[1]/d,c[2]/d};
glNormal3dv(n);
}
void Paperplane(bool colored)
{
//glShadeModel(GL_FLAT); // Damit erhält das gesamte Primitiv dieselbe Farbe.
// So sieht man, wie sich der Papierflieger aus Dreiecken zusammensetzt.
// Bei der Voreinstellung glShadeModel(GL_SMOOTH) werden die Farben
// zwischen den Ecken interpoliert.
glBegin(GL_TRIANGLE_STRIP);
if (colored) glMaterialfv(GL_FRONT, GL_AMBIENT, colRed);
glColor3fv(colRed);
GLdouble p0[]={-0.7, 0, -0.1}; // linker Flügel
GLdouble p1[]={-0.1, 0, 0};
// linker Flügel
GLdouble p2[]={-0.1, 0.7, 0}; // linker Flügel
Normal3P(p0,p1,p2);
glVertex3dv(p0);
glVertex3dv(p1);
glVertex3dv(p2);
glColor3fv(colGreen);
if (colored) glMaterialfv(GL_FRONT, GL_AMBIENT, colGreen);
GLdouble p3[]={0, 0, -0.3}; // linke Seite
Normal3P(p1,p3,p2);
glVertex3dv(p3);
glColor3fv(colBlue);
if (colored) glMaterialfv(GL_FRONT, GL_AMBIENT, colBlue);
GLdouble p4[]={0, 0.8, -0.3}; // linke Seite
Normal3P(p3,p4,p2);
glVertex3dv(p4);
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
glColor3fv(colYellow);
if (colored) glMaterialfv(GL_FRONT, GL_AMBIENT, colYellow);
GLdouble p5[]={0.1, 0, 0}; // rechte Seite
Normal3P(p3,p5,p4);
glVertex3dv(p5);
glColor3fv(colGreen);
if (colored) glMaterialfv(GL_FRONT, GL_AMBIENT, colGreen);
GLdouble p6[]={0.1, 0.7, 0}; // rechte Seite
Normal3P(p3,p5,p6);
glVertex3dv(p6);
glColor3fv(colRed);
if (colored) glMaterialfv(GL_FRONT, GL_AMBIENT, colRed);
GLdouble p7[]={0.7, 0, -0.1}; // Spitze rechter Flügel
Normal3P(p5,p7,p6);
glVertex3dv(p7);
glEnd();
//glShadeModel(GL_SMOOTH); // Voreinstellung wieder herstellen.
}
//----- Aufgabe 1. g) -----------------------------------------------#include <math.h>
double Speed(int i)
{
double d=(i+4)/200.0;
if (i&1) d=-d;
return d;
}
void AnimatedPaperplanes(int n, bool colored)
{
static int calls=0;
calls++;
for (int i = 0; i < n; i++)
{
double t = (calls+10)*Speed(i);
double x = 2*sin(2*t);
double y = sin(t/3.4) - 0.5;
double z = cos(t)-1.5;
//
double angle = 180*((atan(2.0) + M_PI_2) * sin(t) - M_PI_2)/M_PI;
// double xx=atan(2.0) + M_PI_2;
// 2.678
double angle = 180*(2.678*sin(t)-1.57)/3.14;
//
double angle = 180*sin(t)/3.14;
if (Speed(i) < 0) angle += 180;
glPushMatrix();
glTranslated(x, y, z);
glRotated(290, 1, 0, 0);
glRotated(angle, 0, 0, 1);
Paperplane(colored);
glPopMatrix();
}
}
12.10.5 Aufgabe 11.1
// Datei: d:\Loesungen\kap-11\11.1\RecTree.cpp
// rekursiver Baum:
void Stem()
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
413
414
12 Lösungen
GLfloat tree_mat[] = { 0.5, 0.3, 0.1, 1.0 };
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, tree_mat);
//GLUquadricObj *cylquad = gluNewQuadric();
glPushMatrix();
glRotatef(-90, 1, 0, 0);
// gluCylinder(cylquad, 0.1, 0.08, 1, 10, 2 );
Cylinder(0.1, 0.08, 1, GLU_FILL, colGray);
glPopMatrix();
}
void Leaf()
{
GLfloat leaf_mat[] = { 0.0, 0.7, 0.0, 1.0 };
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, leaf_mat);
glBegin(GL_TRIANGLES);
glNormal3f(-0.1, 0, 0.25);
glVertex3f(0, 0, 0);
glVertex3f(0.3, 0.3, 0.1);
glVertex3f(0.3, -0.3, 0);
glEnd();
}
// nicht normalisiert
void StemAndLeaves0()
{
glPushMatrix();
Stem();
for(int i = 0; i < 3; i++)
{
glTranslatef(0, 0.333, 0);
glRotatef(90, 0, 1, 0);
glPushMatrix();
glRotatef(0, 0, 1, 0);
glRotatef(50, 1, 0, 0);
Leaf();
glPopMatrix();
glPushMatrix();
glRotatef(180, 0, 1, 0);
glRotatef(60, 1, 0, 0);
Leaf();
glPopMatrix();
}
//glPopAttrib();
glPopMatrix();
}
void StemWithLeaves()
{
glPushMatrix();
Stem();
for(int i = 0; i < 6; i++)
{
glTranslatef(0, 1.0/6, 0);
glRotatef(120, 0, 1, 0);
glPushMatrix();
glRotatef(50, 1, 0, 0);
Leaf();
glPopMatrix();
}
glPopMatrix();
}
void RecursiveTree(int level)
{
if (level <= 0)
StemWithLeaves();
else
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
{
Stem();
glPushMatrix();
glTranslatef(0, 1, 0);
glScalef(0.7, 0.7, 0.7);
for (int i=0; i<3; i++)
{
double x=(i+1)/4.0;
glPushMatrix();
glRotatef(i*120 + x*40, 0, 1, 0);
glRotatef(30 + x*20, 0, 0, 1);
RecursiveTree(level - 1);
glPopMatrix();
}
glPopMatrix();
}
}
12.10.6 Aufgabe 11.1
// Datei: d:\Loesungen\kap-11\11.1\Textures.cpp
void MakeTexture()
{
const int Width=64,Height=64; // muss Zweierpotenz sein
GLubyte Image[Width][Height][3]; // RGB
for (int i = 0; i < Width; i++)
for (int j = 0; j < Height; j++)
{
int c = (((i&0x8)==0)^((j&0x8)==0)); // 0 oder 1
Image[i][j][0] = c*255; // schwarz oder weiss
Image[i][j][1] = c*255;
Image[i][j][2] = c*255;
}
glTexImage2D(GL_TEXTURE_2D, 0, 3, Width, Height,
0, GL_RGB, GL_UNSIGNED_BYTE, Image);
}
void LoadWindowsBitmap(char* fn)
{
Graphics::TBitmap* b=new Graphics::TBitmap();
b->LoadFromFile(fn);
GLubyte *bmp =new GLubyte[b->Width*b->Height*3];
for (int i = 0; i < b->Height; i++)
for (int j = 0; j < b->Width; j++)
{
int Pixel=b->Canvas->Pixels[j][i];
bmp[3*(i*b->Width+j)+0] = GetRValue(Pixel);
bmp[3*(i*b->Width+j)+1] = GetGValue(Pixel);
bmp[3*(i*b->Width+j)+2] = GetBValue(Pixel);
}
// Anstelle von glTexImage2D wird hier gluBuild2DMipmaps aufgerufen.
// Bei dieser Funktion müssen die Arraygrenzen keine Zweierpotenzen sein.
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, b->Width, b->Height,
GL_RGB, GL_UNSIGNED_BYTE, bmp);
delete b;
delete[] bmp;
}
void InitTexture()
{
// MakeTexture(); // oder LoadWindowsBitmap("...");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
415
416
12 Lösungen
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
//
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
}
void TexturedQuad(GLdouble v0[], GLdouble v1[],
GLdouble v2[], GLdouble v3[])
{ // Viereck mit den Eckpunkten v0, v1, v2 und v3
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glTexCoord2d(0,0); glVertex3dv(v0);
glTexCoord2d(0,1); glVertex3dv(v1);
glTexCoord2d(1,1); glVertex3dv(v2);
glTexCoord2d(1,0); glVertex3dv(v3);
glEnd();
glDisable(GL_TEXTURE_2D);
}
void TexturedCube(GLdouble x0, GLdouble y0, GLdouble z0,
GLdouble x1, GLdouble y1, GLdouble z1, GLenum mode)
{
// to ensure x0<=x1, y0<=y1 und z0<=z1:
if (x0 > x1) {GLdouble tmp = x0; x0 = x1; x1 = tmp;}
if (y0 > y1) {GLdouble tmp = y0; y0 = y1; y1 = tmp;}
if (z0 > z1) {GLdouble tmp = z0; z0 = z1; z1 = tmp;}
GLdouble v[8][3] =
// Eckpunkte
{{x0,y0,z1}, //=v[0]
{x1,y0,z1}, //=v[1]
{x1,y1,z1}, //=v[2]
{x0,y1,z1}, //=v[3]
{x0,y0,z0}, //=v[4]
{x1,y0,z0}, //=v[5]
{x1,y1,z0}, //=v[6]
{x0,y1,z0}};//=v[7]
v[7] ---- v[6]
/|
/|
v[3] ---- v[2]|
| |
| |
| |
| |
| |
| |
| v[4] ---| v[5]
|/
|/
v[0] ---- v[1]
TexturedQuad(v[0],v[1],v[2],v[3]);
TexturedQuad(v[1],v[5],v[6],v[2]);
TexturedQuad(v[5],v[4],v[7],v[6]);
TexturedQuad(v[4],v[0],v[3],v[7]);
TexturedQuad(v[3],v[2],v[6],v[7]);
TexturedQuad(v[1],v[0],v[4],v[5]);
front
right
back
left
up
down
//
//
//
//
//
//
}
void TexturedSphere(GLdouble radius)
{ // as in glut-3.7\lib\glut\glut_shapes.c
static GLUquadricObj *quadObj=gluNewQuadric();
glEnable(GL_TEXTURE_2D);
gluQuadricDrawStyle(quadObj, GLU_FILL);
gluQuadricNormals(quadObj, GLU_SMOOTH);
gluQuadricTexture(quadObj, GL_TRUE);
GLint slices=30, stacks=30;
gluSphere(quadObj, radius, slices, stacks);
glDisable(GL_TEXTURE_2D);
}
//----- Aufgabe 4. a) -----------------------------------------------void TexturedFigures()
{
glPushMatrix();
GLdouble v0[]={-1.2, 0, -1}, v1[]={-1.2, 1, -1},
v2[]={-0.2, 1, -1}, v3[]={-0.2, 0, -1};
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
TexturedQuad(v0,v1,v2,v3);
glTranslated(1, 0.5, -3.0);
TexturedSphere(1);
glPopMatrix();
}
12.10.7 Aufgabe 11.2
// Datei: d:\Loesungen\kap-11\11.2\W32FileU.cpp
#include "W32FileU.h"
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
}
//-------------------------------------------------------------------void ShowLastError(AnsiString where)
{
MessageBox(NULL,SysErrorMessage(GetLastError()).c_str(),
where.c_str(),MB_OK|MB_ICONERROR);
/* Ohne VCL:
LPTSTR lpMsgBuf; // char*
FormatMessage( // siehe Online-Hilfe zu Win32
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),//Default language
(LPTSTR)&lpMsgBuf, //Adresse des nullterminierten Textes
0,
NULL);
MessageBox(NULL,lpMsgBuf,where.c_str(),
MB_OK|MB_ICONERROR);
LocalFree( lpMsgBuf ); // Free the buffer.
*/
}
//----- Aufgabe 11.2.1 ----------------------------------------------HANDLE h1;
void __fastcall TForm1::Open1Click(TObject *Sender)
{
char* fn="c:\\test\\test.dat";
h1=CreateFile(
fn, // Dateiname
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
0, // 0: keine SECURITY_ATTRIBUTES
OPEN_ALWAYS,
// immer neu anlegen
FILE_ATTRIBUTE_NORMAL, // Dateiattribut
0); // kein TemplateFile
if (h1==INVALID_HANDLE_VALUE)
ShowLastError("CreateFile 1");
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
417
418
12 Lösungen
typedef int DataRec;
const int RecSize=sizeof(DataRec);
int RecNr;
void __fastcall TForm1::Write1Click(TObject *Sender)
{
DWORD lpNumberOfBytesWritten;
for (int i=0; i<10; i++)
{
DataRec x=i;
BOOL b=WriteFile(
h1, // Handle der Datei
&x, // Adresse der Daten
RecSize,// Anzahl der zu schreibenden Bytes
&lpNumberOfBytesWritten, // Adresse für die Anzahl der geschriebenen Bytes
0);
// für overlapped I/O , sonst 0
if (!b)
ShowLastError("CreateFile 1");
}
}
void __fastcall TForm1::Read1Click(TObject *Sender)
{
DWORD sfp=SetFilePointer(
h1,// Handle der Datei
0, // Anzahl der Bytes, um die der File-Pointer
// relativ zum Startpunkt bewegt werden soll
0, // Für Bewegungen > 232-2 Bytes
FILE_BEGIN);
// Startpunkt
if (sfp== 0xFFFFFFFF)
ShowMessage("Fehler bei SetFilePointer");
DWORD NumberOfBytesRead;
DataRec x;
bool success = true, eof=false;
while (success && !eof)
{
success=ReadFile(h1,&x,sizeof(x),&NumberOfBytesRead,0);
if (!success) ShowLastError("ReadFile");
eof=NumberOfBytesRead<sizeof(x);
if (!eof) // Datensatz kann bearbeitet werden
Form1->Memo1->Lines->Add(IntToStr(x));
}
}
void __fastcall TForm1::Close1Click(TObject *Sender)
{
CloseHandle(h1);
}
//----- Aufgabe 11.2.2 ----------------------------------------------HANDLE OpenFile(char* fn)
{
HANDLE h=CreateFile(fn,GENERIC_READ,
0,0,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
if (h==INVALID_HANDLE_VALUE)
ShowLastError("CreateFile");
return h;
}
void __fastcall TForm1::FehlermeldungClick(TObject *Sender)
{
OpenFile(":::"); // Unzulässiger Dateiname
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
HANDLE h=OpenFile("c:\\test.dat");
DWORD x,NumberOfBytesRead;
bool success=WriteFile(h,&x,sizeof(x),&NumberOfBytesRead,0); // Schreiben
unzulässig
if (!success) ShowLastError("ReadFile");
success=WriteFile(0,&x,sizeof(x),&NumberOfBytesRead,0); // Unzulässiges Handle
if (!success) ShowLastError("ReadFile");
OpenFile("c:\\test.dat"); // Ein zweites Mal öffnen
}
//----- Aufgabe 11.2.3 ----------------------------------------------HANDLE lfh;
void __fastcall TForm1::OpenSharedClick(TObject *Sender)
{
char* fn="c:\\test\\test.dat";
lfh= CreateFile(
fn, // Dateiname
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
0, // 0: keine SECURITY_ATTRIBUTES
OPEN_ALWAYS,
// immer neu anlegen
FILE_ATTRIBUTE_NORMAL, // Dateiattribut
0); // kein TemplateFile
if (lfh==INVALID_HANDLE_VALUE)
ShowLastError("CreateFile 1");
}
void __fastcall TForm1::CloseClick(TObject *Sender)
{
CloseHandle(lfh);
}
void __fastcall TForm1::LockClick(TObject *Sender)
{
if (LockFile(lfh,RecNr*RecSize,0, RecSize,0))
{
// ShowMessage("locked");
}
else
{
ShowMessage("Already locked");
}
}
void __fastcall TForm1::UnlockClick(TObject *Sender)
{
if (UnlockFile(lfh,RecNr*RecSize,0, RecSize,0))
{
// ShowMessage("Unlocked");
}
else
{
ShowMessage("Unlock not possible");
}
}
void __fastcall TForm1::UpDown1Click(TObject *Sender, TUDBtnType Button)
{
RecNr=UpDown1->Position;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
419
420
12 Lösungen
void __fastcall TForm1::ReadClick(TObject *Sender)
{
DWORD sfp=SetFilePointer(
lfh, // Handle der Datei
RecNr*RecSize, // Anzahl der Bytes, um die der File-Pointer
// relativ zum Startpunkt bewegt werden soll
0, // Für Bewegungen > 2^32-2 Bytes
FILE_BEGIN);
// Startpunkt
if (sfp== 0xFFFFFFFF)
ShowMessage("Fehler bei SetFilePointer");
DWORD NumberOfBytesRead;
DataRec x;
bool success=ReadFile(lfh,&x,sizeof(x),&NumberOfBytesRead,0);
if (!success) ShowLastError("ReadFile");
bool eof=NumberOfBytesRead<sizeof(x);
if (eof)
ShowMessage("eof bei ReadFile");
Edit2->Text=IntToStr(x);
}
//-------------------------------------------------------------------void __fastcall TForm1::WriteClick(TObject *Sender)
{
DWORD sfp=SetFilePointer(
lfh, // Handle der Datei
RecNr*RecSize, // Anzahl der Bytes, um die der File-Pointer
// relativ zum Startpunkt bewegt werden soll
0, // Für Bewegungen > 2^32-2 Bytes
FILE_BEGIN);
// Startpunkt
if (sfp== 0xFFFFFFFF)
ShowMessage("Fehler bei SetFilePointer");
DataRec x=StrToInt(Edit2->Text);
DWORD NumberOfBytesWritten;
BOOL b=WriteFile(
lfh, // Handle der Datei
&x, // Adresse der Daten
RecSize,// Anzahl der zu schreibenden Bytes
&NumberOfBytesWritten, // Adresse für die Anzahl der geschriebenen Bytes
0);
// für overlapped I/O , sonst 0
if (!b)
ShowLastError("WriteFile");
}
12.10.8 Aufgabe 11.3
// Datei: d:\Loesungen\kap-11\11.3\SerCommU.cpp
#include "SerCommU.h"
// #pragma link "ComPort"
void ShowLastError(AnsiString where)
{ // In der VCL einfach mit SysErrorMessage:
MessageBox(NULL,SysErrorMessage(GetLastError()).c_str(),where.c_str(),MB_OK|MB_IC
ONERROR);
/* Ohne SysErrorMessage:
LPTSTR lpMsgBuf; // char*
FormatMessage( // siehe Online-Hilfe zu Win32
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf, // Adresse des nullterminierten Meldungstextes
0,
NULL);
MessageBox( NULL, lpMsgBuf,where.c_str(),MB_OK|MB_ICONERROR);
LocalFree( lpMsgBuf ); // Free the buffer.
*/
}
void ShowTextAndValue(TMemo* t,AnsiString s,int Value)
{
t->Lines->Add(s+IntToStr(Value));
}
void showDCB(TMemo* M,HANDLE hCom)
{
DCB dcb; // Device Control Block
BOOL fSuccess = GetCommState(hCom, &dcb); // DCB lesen
if (!fSuccess) ShowLastError("GetCommState");
//
DWORD DCBlength;
// sizeof(DCB)
//
DWORD BaudRate;
// current baud rate
ShowTextAndValue(M,"baud rate: ",dcb.BaudRate);
ShowTextAndValue(M,"parity: ",dcb.fParity);
ShowTextAndValue(M,"CTS output flow control: ",dcb.fOutxCtsFlow);
ShowTextAndValue(M,"DSR output flow control: ",dcb.fOutxDsrFlow);
ShowTextAndValue(M,"DTR flow control type: ",dcb.fDtrControl);
ShowTextAndValue(M,"DSR sensitivity: ",dcb.fDsrSensitivity);
ShowTextAndValue(M,"RTS flow control: ",dcb.fRtsControl);
}
HANDLE OpenComm(char* Port)
{ // Öffnet den Port und gibt sein Handle zurück
HANDLE hCom = CreateFile(Port,// z.B. "COM1",
GENERIC_READ | GENERIC_WRITE, // Zum Senden und Empfangen
0, // Für comm devices exclusive-access notwendig
0, // Keine security attributes
OPEN_EXISTING, // Für comm devices OPEN_EXISTING notwendig
0, // In den folgenden Beispielen kein overlapped I/O
0); // Für comm devices muß hTemplate NULL sein
if (hCom == INVALID_HANDLE_VALUE)
ShowLastError("CreateFile: "+AnsiString(Port));
return hCom;
}
void SetDCB(HANDLE hCom)
{
DCB dcb; // Device Control Block
BOOL fSuccess = GetCommState(hCom, &dcb); // DCB lesen
if (!fSuccess) ShowLastError("GetCommState");
// Setze die Baudrate=9600, 8 Datenbits, keine Parity, 1 Stopbit:
dcb.BaudRate = 9600; // Baudrate=9600
dcb.ByteSize = 8;
// 8 Datenbits
dcb.Parity
= NOPARITY;
// keine Parity
dcb.StopBits = ONESTOPBIT; // 1 Stopbit
bool NoFlowControl=false,
HardwareFlowControl=false,
SoftwareFlowControl=true;
// Oft spricht man auch von Hardware- bzw. Software-Handshake
// Die folgenden Ausführungen nach dem MSDN-Artikel
if (NoFlowControl)
{
// kein Hardware Flowcontrol:
dcb.fOutxCtsFlow=false;
dcb.fOutxDsrFlow=false;
// kein Software Flowcontrol:
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
421
422
dcb.fInX=false; // für Empfänger
dcb.fOutX=false; // für Sender
dcb.fDsrSensitivity=false;
}
else
{
if (HardwareFlowControl)
{
dcb.fOutxCtsFlow=true;
dcb.fOutxDsrFlow=true;
}
// Hier kein else: Software- und HardwareFlowControl sind
// gleichzeitig möglich, auch wenn das nicht üblich ist
if (SoftwareFlowControl)
{
dcb.fInX=true; // für Empfänger
dcb.fOutX=true; // für Sender
// Die folgenden Elemente steuern SoftwareFlowControl,
// Sie müssen aber nicht gesetzt werden. Oft reichen die
// Voreinstellungen.
// dcb.fTXContinueOnXoff=false;
// dcb.XonLim = ...;
// dcb.XoffLim = ...;
// dcb.XoffChar = ...;
// dcb.XonChar = ...;
}
}
// SetCommState konfiguriert die serielle Schnittstelle
fSuccess = SetCommState(hCom, &dcb);
if (!fSuccess) ShowLastError("SetCommState");
showDCB(Form1->Memo1,hCom);
}
void SetReadTimeouts(HANDLE hCom)
{ // Der Aufruf von SetCommTimeouts ist notwendig,
// da ohne diesen die Ergebnisse von Readfile undefiniert sind
COMMTIMEOUTS t;
// Alle Wert in Millisekunden
// Werte für ReadFile:
t.ReadIntervalTimeout=100; // Timeout zwischen zwei Zeichen
t.ReadTotalTimeoutMultiplier=10; // pro Zeichen
t.ReadTotalTimeoutConstant=1;
//
// Wenn mit ReadFile n Zeichen gelesen werden sollen, tritt ein Timeout
// nach ReadTotalTimeoutMultiplier*n+ReadTotalTimeoutConstant ms ein.
// Werte für WriteFile: wenn beide 0 sind,
// kein Timeout beim Schreiben
t.WriteTotalTimeoutMultiplier=0;
t.WriteTotalTimeoutConstant=0;
if (!SetCommTimeouts(hCom,&t))
ShowLastError("SetCommTimeouts");
}
void showCommProp(TMemo* M, HANDLE hCom)
{
/*typedef struct _COMMPROP { // cmmp
WORD wPacketLength;
// packet size, in bytes
WORD wPacketVersion;
// packet version
DWORD dwServiceMask;
// services implemented
DWORD dwReserved1;
// reserved
DWORD dwMaxTxQueue;
// max Tx bufsize, in bytes
DWORD dwMaxRxQueue;
// max Rx bufsize, in bytes
DWORD dwMaxBaud;
// max baud rate, in bps
DWORD dwProvSubType;
// specific provider type
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.10 Lösungen Kapitel 11
DWORD
DWORD
DWORD
WORD
WORD
DWORD
DWORD
DWORD
DWORD
dwProvCapabilities;
dwSettableParams;
dwSettableBaud;
wSettableData;
wSettableStopParity;
dwCurrentTxQueue;
dwCurrentRxQueue;
dwProvSpec1;
dwProvSpec2;
423
//
//
//
//
//
//
//
//
//
capabilities supported
changable parameters
allowable baud rates
allowable byte sizes
stop bits/parity allowed
Tx buffer size, in bytes
Rx buffer size, in bytes
provider-specific data
provider-specific data
WCHAR wcProvChar[1];
// provider-specific data
} COMMPROP;
*/
_COMMPROP CP;
GetCommProperties(hCom,&CP);
ShowTextAndValue(M,"max Tx bufsize, in bytes: ",CP.dwMaxTxQueue);
ShowTextAndValue(M,"max Rx bufsize, in bytes: ",CP.dwMaxRxQueue);
ShowTextAndValue(M,"Tx buffer size, in bytes: ",CP.dwCurrentTxQueue);
ShowTextAndValue(M,"Rx buffer size, in bytes: ",CP.dwCurrentRxQueue);
}
HANDLE hComSend,hComReceive;
void __fastcall TForm1::FormCreate(TObject *Sender)
{
BEmpfangClick(Sender);
hComSend=OpenComm("COM2");
hComReceive=OpenComm("COM1");
SetDCB(hComSend);
SetDCB(hComReceive);
SetReadTimeouts(hComReceive);
showCommProp(Form1->Memo1,hComSend);
showCommProp(Form1->Memo1,hComReceive);
}
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
CloseHandle(hComSend);
CloseHandle(hComReceive);
}
int SendData(char Data[],int n)
{
DWORD NumberOfBytesWritten; // Anzahl der gesendeten Bytes
bool b=WriteFile(hComSend,
Data,
n, // Anzahl der zu sendenden Bytes
&NumberOfBytesWritten, // Adresse, an die der Wert geschrieben wird
0); // kein overlapped I/O
if (!b)
ShowLastError("SendData");
return NumberOfBytesWritten;
}
void __fastcall TForm1::BSendenClick(TObject *Sender)
{
// int n=SendData("abcdefghijklmno",10);
int n=SendData(Edit1->Text.c_str(),Edit1->Text.Length());
Memo1->Lines->Add("Written: "+IntToStr(n));
}
DWORD ReceiveData(char* Data,int n)
{
DWORD NumberOfBytesRead;
// Anzahl der gelesenen Bytes
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
424
12 Lösungen
bool b=ReadFile(hComReceive, // handle des Com-Ports
Data, // Adresse des Datenpuffers
n,
// Anzahl der zu lesenden Bytes
&NumberOfBytesRead, // Adresse, an die der Wert geschrieben wird
0);
// kein overlapped I/O
if (!b)
ShowLastError("ReceiveData");
return NumberOfBytesRead;
}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
const int BufSize = 3;
char Buffer[BufSize]; // address of buffer that receives data
DWORD NumberOfBytesRead=ReceiveData(Buffer,BufSize);
if (NumberOfBytesRead > 0)
{
AnsiString s;
for (DWORD i=0;i<NumberOfBytesRead;i++)
s=s+Buffer[i];
Memo1->Lines->Add("Gelesen n="+IntToStr(NumberOfBytesRead)+" ch="+s);
}
else
{
Memo1->Lines->Add("Nichts empfangen");
}
}
void __fastcall TForm1::BEmpfangClick(TObject *Sender)
{
if (Timer1->Enabled)
{
Timer1->Enabled =false;
BEmpfang->Caption="Read-Timer disabled";
}
else
{
Timer1->Enabled=true;
BEmpfang->Caption="Read-Timer aktiv";
};
}
12.10.9 Aufgabe 11.4
// Datei: d:\Loesungen\kap-11\11.4\DBU.cpp
#include "DBU.h"
//-------------------------------------------------------------------void InitTable1()
{
Form1->Table1->DatabaseName = "c:\\test";
Form1->Table1->TableName = "KB";
}
void InitTable2()
{
Form1->Table2->DatabaseName = "c:\\test";
Form1->Table2->TableName = "KS";
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
425
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Erzeuge das Verzeichnis, falls es nicht existiert
AnsiString dir="c:\\test";
if (CreateDirectory(dir.c_str(),0))
ShowMessage("directory '"+dir+"' created");
InitTable1();
InitTable2();
}
const
const
const
const
const
AnsiString
AnsiString
AnsiString
AnsiString
AnsiString
fnUnique="Eindeutig";
fnKontoNr="KontoNr";
fnDatum="Datum";
fnBewart="Bewart";
fnBetrag="Betrag";
TFieldDef* NewField(TTable* t, AnsiString Name, TFieldType DataType)
{
TFieldDef* pNewField = t->FieldDefs->AddFieldDef();
pNewField->Name = Name;
pNewField->DataType = DataType;
return pNewField;
}
void DefinePrimaryIndex(TTable* Table)
{ // der Primärindex muss das erste Feld eines Datensatzes sein
Table->IndexDefs->Clear();
TIndexDef* pi=Table->IndexDefs->AddIndexDef();
pi->Name = "PrimärIndex";
pi->Fields=fnKontoNr+";"+fnUnique;
pi->Options<<ixPrimary;
}
const bool unique=false; // siehe Abschnitt 11.4.3
void CreateKBDB(TTable* t)
{
t->Active = false; // zum Anlegen notwendig
t->TableType = ttParadox; // oder ttDBase usw.
t->FieldDefs->Clear();
NewField(t, fnKontoNr, ftInteger);
if (!unique)
NewField(t, fnUnique, ftAutoInc);
NewField(t, fnDatum, ftDate);
NewField(t, fnBewart, ftString);
NewField(t, fnBetrag, ftCurrency);
if (!unique)
DefinePrimaryIndex(t);
t->CreateTable(); // erzeugt die Tabelle
}
const AnsiString fnName="Name";
const AnsiString fnKontostand="Kontostand";
void CreateKSDB(TTable* t)
{
t->Active = false; // zum Anlegen notwendig
t->TableType = ttParadox; // oder ttDBase usw.
t->FieldDefs->Clear();
NewField(t, fnKontoNr, ftInteger);
NewField(t, fnName, ftString);
NewField(t, fnKontostand, ftCurrency);
t->CreateTable();
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
426
void __fastcall TForm1::CreateDBClick(TObject *Sender)
{
CreateKBDB(Table1);
CreateKSDB(Table2);
}
//-------------------------------------------------------------------const int MaxKonten=50;
void CreateKBData(TTable* t, int n)
{
t->Active = true;
for (int i=0;i<n;i++)
{
t->Append();
t->FieldByName(fnKontoNr)->AsInteger=1000+i%MaxKonten;
t->FieldByName(fnDatum)->AsDateTime=Date()+i;
AnsiString PlusMinus[2]={"+","-"};
t->FieldValues[fnBewart]=PlusMinus[i%2];
t->FieldValues[fnBetrag]=1000-i;
t->Post();
}
}
void CreateKSData(TTable* t)
{
AnsiString NN[10] = {"Duestrip, ",
"Sander, ", "König, ",
"Blond, ",
"Schluck, ", "Parker, ", "Kannt, ",
"Pascal, ", "Coldrain, ","Prince, "};
AnsiString VN[10] = {"Daniel",
"Alek",
"Q.",
"James",
"Donald",
"Karl", "Imanuel",
"Nikolausi", "John", "Charlie"};
t->Active = true;
for (int i=0;i<MaxKonten;i++)
{
t->Append();
t->FieldByName(fnKontoNr)->AsInteger=1000+i;
t->FieldValues[fnName]=NN[(i/10)%10]+VN[i%10];
t->FieldValues[fnKontostand]=0;
t->Post();
}
}
void __fastcall TForm1::CreateDataClick(TObject *Sender)
{
if (unique)
::CreateKBData(Form1->Table1, MaxKonten);
else
::CreateKBData(Form1->Table1, 4*MaxKonten);
::CreateKSData(Form1->Table2);
}
//-------------------------------------------------------------------//-------------------------------------------------------------------Currency Sum(TTable* t)
{
Currency s=0;
t->First();
while (!t->Eof)
{
s=s+t->FieldByName("Betrag")->AsFloat;
t->Next();
}
return s;
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.10 Lösungen Kapitel 11
427
void __fastcall TForm1::SummeClick(TObject *Sender)
{
Table1->Active=true;
Edit1->Text=CurrToStr(Sum(Form1->Table1));
}
//-------------------------------------------------------------------void __fastcall TForm1::InitGrid1Click(TObject *Sender)
{
InitTable1();
DataSource1->DataSet = Table1;
// wird im OI angeboten
DBGrid1->DataSource = DataSource1; // wird im OI angeboten
Table1->Active=true;
DBNavigator1->DataSource=DataSource1;
}
//-------------------------------------------------------------------void DefineSecondaryIndex(TTable* Table)
{
Table->Active=false;
Table->Exclusive=true;
Table->AddIndex("IndexDatum", fnDatum, TIndexOptions()<<ixCaseInsensitive,"");
Table->AddIndex("IndexKontoNrDatum", fnKontoNr+";"+fnDatum, TIndexOptions(),"");
Table->Exclusive=false;
Table->Active=true;
}
void __fastcall TForm1::DefSecInxClick(TObject *Sender)
{
DefineSecondaryIndex(Table1);
Table1->IndexName="IndexDatum";
}
void __fastcall TForm1::GotoNearestClick(TObject *Sender)
{
Table1->IndexName ="IndexKontoNrDatum";
Table1->SetKey();
Table1->FieldByName(fnKontoNr)->Value = 1017;
Table1->GotoNearest();
}
void __fastcall TForm1::SetRangeClick(TObject *Sender)
{
Table1->IndexName ="IndexKontoNrDatum";
Table1->SetRangeStart();
Table1->FieldByName(fnKontoNr)->Value = 1010;
Table1->SetRangeEnd();
Table1->FieldByName(fnKontoNr)->Value = 1020;
Table1->ApplyRange();
}
//-------------------------------------------------------------------void __fastcall TForm1::ShowIndexClick(TObject *Sender)
{
Table1->GetIndexNames(Memo1->Lines);
// Memo1->Lines=Table1->IndexDefs->Items[0];
Table1->IndexName="PrimärIndex";
}
//-------------------------------------------------------------------void LoadFilter(TTable* t,char* fn)
{
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
428
12 Lösungen
Form1->Memo1->Lines->LoadFromFile(fn);
t->Filter=Form1->Memo1->Text;
}
void __fastcall TForm1::FiltrClick(TObject *Sender)
{
//LoadFilter(Table1,"c:\\test\\BestCustomers.txt");
Table1->Filtered=true;
Table1->Filter=Edit2->Text; // "KontoNr<>1003"; // "<>" bedeutet "!="
return;
//Table1->Filter="KontoNr!=5"; // Exception: Ungültiges Zeichen!
Table1->Filter="KontoNr<>1003"; // "<>" bedeutet "!="
return;
Table1->Filter="KontoNr<>1003 and Bewart='-' "; //
Table1->Filter="(KontoNr<1003) and (not(Betrag=1000))";
}
//-------------------------------------------------------------------void __fastcall TForm1::Table1FilterRecord(TDataSet *DataSet, bool &Accept)
{
// Accept = Edit1->Text==Table1->FieldByName(fnKontoNr)->AsString;
}
//-------------------------------------------------------------------void __fastcall TForm1::InitGrid2Click(TObject *Sender)
{
InitTable2();
DataSource2->DataSet = Table2;
// wird im OI angeboten
DBGrid2->DataSource = DataSource2; // wird im OI angeboten
Table2->Active=true;
DBNavigator2->DataSource=DataSource2;
}
//-------------------------------------------------------------------void __fastcall TForm1::ConnectClick(TObject *Sender)
{
Table1->MasterSource = DataSource2;
Table1->MasterFields = fnKontoNr; // muss ein Index sein
}
//-------------------------------------------------------------------void __fastcall TForm1::SumAllClick(TObject *Sender)
{
// vorher immer Connect aufrufen!
Currency Summe;
Table2->First();
while (!Table2->Eof)
{
Summe = 0;
Table1->First();
while (!Table1->Eof)
{
if (Table1->FieldByName(fnBewart)->AsString == "+")
Summe = Summe + Table1->FieldByName(fnBetrag)->AsFloat;
else if (Table1->FieldByName(fnBewart)->AsString == "-")
Summe = Summe - Table1->FieldByName(fnBetrag)->AsFloat;
else ShowMessage("Falsche Bewegungsart");
Table1->Next();
}
Table2->Edit();
Table2->FieldByName(fnKontostand)->AsFloat = Summe;
Table2->Post();
Table2->Next();
}
}
//-------------------------------------------------------------------Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12.10 Lösungen Kapitel 11
429
const bool UseDBN=true;
void __fastcall TForm1::OpenSQLClick(TObject *Sender)
{
if (UseDBN) Form1->Query1->DatabaseName = "c:\\test";
// Hier ist keine Zuweisung an TableName möglich und notwendig
DataSource1->DataSet = Query1; // wird im OI angeboten
DBGrid1->DataSource = DataSource1; // wird im OI angeboten
DBNavigator1->DataSource = DataSource1; // wird im OI ang.
//Query1->Active=true;
}
//-------------------------------------------------------------------void __fastcall TForm1::SimpleQueryClick(TObject *Sender)
{
Query1->Close(); // beim ersten Aufruf unnötig, aber kein Fehler
Query1->SQL->Clear();
int i=3;
if (UseDBN)
if (i==1)
Query1->SQL->Add("SELECT * FROM KB ");
else if (i==2)
Query1->SQL->Add("SELECT KontoNr,Betrag FROM KB");
else if (i==3)
{
Query1->SQL->Add("SELECT * FROM KB WHERE KontoNr < 1020 and Betrag < 900");
Query1->SQL->Add("ORDER BY KB.KontoNr,KB.Datum");
}
else
Query1->SQL->Add("SELECT * FROM 'c:\\test\\KB.db\' ");
Query1->Open();
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Query1->Close();
Query1->SQL->Clear();
Query1->SQL->Add("SELECT KB.KontoNr,Name, Datum, Betrag");
Query1->SQL->Add("FROM KS,KB WHERE KB.KontoNr=KS.KontoNr");
Query1->SQL->Add("ORDER BY KB.KontoNr,KB.Datum");
Query1->Open();
}
//-------------------------------------------------------------------void __fastcall TForm1::ConnectSQLClick(TObject *Sender)
{
Query2->DatabaseName = "c:\\test";
DataSource2->DataSet = Query2;
DBGrid2->DataSource = DataSource2;
DBNavigator2->DataSource = DataSource2;
Query2->Close();
Query2->SQL->Clear();
Query2->SQL->Add("SELECT * FROM KS ");
Query2->Open();
Query1->DataSource = DataSource2;
}
//-------------------------------------------------------------------//-------------------------------------------------------------------void __fastcall TForm1::Button4Click(TObject *Sender)
{
Query1->Close();
Query1->SQL->Clear();
Query1->SQL->Add("SELECT KB.KontoNr,Name, Datum, Betrag");
Query1->SQL->Add("FROM KS,KB WHERE KB.KontoNr=KS.KontoNr");
Query1->SQL->Add("ORDER BY KB.KontoNr,KB.Datum");
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
430
Query1->Open();
}
//-------------------------------------------------------------------void __fastcall TForm1::StartTrClick(TObject *Sender)
{
Table1->Database->TransIsolation = tiDirtyRead;
// tiDirtyRead für Paradox- und dBASE-Tabellen notwendig
Table1->Database->StartTransaction();
}
void __fastcall TForm1::CommitTrClick(TObject *Sender)
{
Table1->Database->Commit();
}
void __fastcall TForm1::RollbTrClick(TObject *Sender)
{
Table1->Database->Rollback();
}
void __fastcall TForm1::RefreshDataClick(TObject *Sender)
{ // Aktualisiert die Anzeige
Table1->Refresh();
}
//-------------------------------------------------------------------#include "QRDBU.h"
void __fastcall TForm1::QuickReportClick(TObject *Sender)
{
Form2->Show();
Form2->QuickRep1->Preview();
}
12.10.10 Aufgabe 11.4
// Datei: d:\Loesungen\kap-11\11.4\QRDBU.cpp
#include "QRDBU.h"
TForm2 *Form2;
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
void InitTable1();
#include "DBU.h";
void __fastcall TForm2::FormCreate(TObject *Sender)
{
InitTable1();
Form1->Table1->Active = true;
// Verbinde den Quickreport mit der TTable:
QuickRep1->DataSet = Form1->Table1;
// Drucke dieses Band für jede Zeile der Tabelle:
QRBand1->BandType =rbDetail;
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
12 Lösungen
12.10 Lösungen Kapitel 11
// Verbinde die Textfelder mit der TTable:
QRDBText1->DataSet = Form1->Table1;
QRDBText1->DataField = "KontoNr";
QRDBText2->DataSet = Form1->Table1;
QRDBText2->DataField = "Datum";
QRDBText3->DataSet = Form1->Table1;
QRDBText3->DataField = "Betrag";
QRDBText3->Alignment = taRightJustify;
QRDBText3->AutoSize = false;
QRDBText3->Mask = "0.00";
}
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“
431
Aufgaben und Lösungen zu „R. Kaiser: C++ mit dem Borland C++Builder, Springer-Verlag, ISBN 3-540-62994-7“