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: ä: ä ö: ö ü: ü ß: ß Ä: Ä Ö: Ö Ü: Ü Beispiel: „In München steht ein Hofbrä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,"ä"); else if (s[i]=='ö') s.replace(i,1,"ö"); else if (s[i]=='ü') s.replace(i,1,"ü"); else if (s[i]=='ß') s.replace(i,1,"ß"); else if (s[i]=='Ä') s.replace(i,1,"Ä"); else if (s[i]=='Ö') s.replace(i,1,"ö"); else if (s[i]=='Ü') s.replace(i,1,"Ü"); 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“