Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden!...

100
Entwicklungsumgebung, Compiler, alle Beispiele und mehr auf www.cppbuch.de ulrich BREYMANN DER C++ PROGRAMMIERER // C++ LERNEN // PROFESSIONELL ANWENDEN // LÖSUNGEN NUTZEN 4. Auflage Inklusive »C++-Rezeptbuch« mit mehr als 150 praktischen Lösungen AKTUELL ZU C++14

Transcript of Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden!...

Page 1: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Entwicklungsumgebung, Compiler, alle Beispiele und mehr auf www.cppbuch.de

ulrich BREYMANN

DER C++PROGRAMMIERER

// C++ LERNEN // PROFESSIONELL ANWENDEN // LÖSUNGEN NUTZEN

4. A

ufl a

ge

Inklusive »C++-Rezeptbuch« mit mehr als 150 praktischen Lösungen

AKTUELL

ZU

C++14

Page 2: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Breymann

Der C++-Programmierer

Bleiben Sie auf dem Laufenden!

Unser Computerbuch-Newsletter informiertSie monatlich über neue Bücher und Termine.Profitieren Sie auch von Gewinnspielen undexklusiven Leseproben. Gleich anmelden unter

www.hanser-fachbuch.de/newsletter

Hanser Update ist der IT-Blog des Hanser Verlagsmit Beiträgen und Praxistipps von unseren Autorenrund um die Themen Online Marketing, Webent-wicklung, Programmierung, Softwareentwicklungsowie IT- und Projektmanagement. Lesen Sie mitund abonnieren Sie unsere News unter

www.hanser-fachbuch.de/update

Page 3: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.
Page 4: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Ulrich Breymann

Der C++-Programmierer

C++ lernen –professionell anwenden –Lösungen nutzen

4., überarbeitete und erweiterte Auflage

Page 5: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Prof. Dr. Ulrich Breymann lehrte Informatik an der Fakultät Elektrotechnik und Informatikder Hochschule Bremen.Kontakt: [email protected]

Alle in diesem Buch enthaltenen Informationen, Verfahren und Darstellungen wurden nachbestem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz aus-zuschließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mitkeiner Verpflichtung oder Garantie irgendeiner Art verbunden. Autor und Verlag übernehmeninfolgedessen keine juristische Verantwortung und werden keine daraus folgende oder sonstigeHaftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen – oderTeilen davon – entsteht.

Ebenso übernehmen Autor und Verlag keine Gewähr dafür, dass beschriebene Verfahren usw.frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Wa-renbezeichnungen usw. in diesem Buch berechtigt deshalb auch ohne besondere Kennzeichnungnicht 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 Information der Deutschen Nationalbibliothek:

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbiblio-grafie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

Dieses Werk ist urheberrechtlich geschützt.Alle Rechte, auch die der Übersetzung, des Nachdruckes und der Vervielfältigung des Buches,oder Teilen daraus, vorbehalten. Kein Teil des Werkes darf ohne schriftliche Genehmigung desVerlages in irgendeiner Form (Fotokopie, Mikrofilm oder ein anderes Verfahren) – auch nicht fürZwecke der Unterrichtsgestaltung – reproduziert oder unter Verwendung elektronischer Systemeverarbeitet, vervielfältigt oder verbreitet werden.

© 2015 Carl Hanser Verlag München, www.hanser-fachbuch.deLektorat: Brigitte Bauer-SchiewekHerstellung: Irene WeilhartUmschlagdesign: Marc Müller-Bremer, www.rebranding.de, MünchenUmschlagrealisation: Stephan RönigkDatenbelichtung, Druck und Bindung: Kösel, KrugzellAusstattung patentrechtlich geschützt. Kösel FD 351, Patent-Nr. 0748702Printed in Germany

Print-ISBN: 978-3-446-44346-4

E-Book-ISBN: 978-3-446-44404-1

Page 6: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

für Bruni

Page 7: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.
Page 8: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt

Vorwort .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

Teil I: Einführung in C++ .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

1 Es geht los! .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

1.1 Historisches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

1.2 Objektorientierte Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

1.3 Compiler. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

1.4 Das erste Programm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

1.4.1 Namenskonventionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

1.5 Integrierte Entwicklungsumgebung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

1.6 Einfache Datentypen und Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

1.6.1 Ausdruck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

1.6.2 Ganze Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

1.6.3 Reelle Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

1.6.4 Konstante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

1.6.5 Zeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

1.6.6 Logischer Datentyp bool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

1.6.7 Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

1.6.8 Regeln zum Bilden von Ausdrücken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

1.6.9 Standard-Typumwandlungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

1.7 Gültigkeitsbereich und Sichtbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

1.7.1 Namespace std . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

Page 9: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

8 Inhalt

1.8 Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

1.8.1 Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

1.8.2 Sequenz (Reihung) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

1.8.3 Auswahl (Selektion, Verzweigung) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

1.8.4 Fallunterscheidungen mit switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

1.8.5 Wiederholungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

1.8.6 Kontrolle mit break und continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

1.9 Benutzerdefinierte und zusammengesetzte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

1.9.1 Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

1.9.2 Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

1.9.3 Der C++-Standardtyp vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

1.9.4 Zeichenketten: Der C++-Standardtyp string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

1.9.5 Container und Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

1.9.6 Typermittlung mit auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

1.9.7 Unions und Bitfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

1.10 Einfache Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

1.10.1 Standardein- und -ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

1.10.2 Ein- und Ausgabe mit Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

2 Programmstrukturierung ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

2.1 Funktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

2.1.1 Aufbau und Prototypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

2.1.2 Gültigkeitsbereiche und Sichtbarkeit in Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . 110

2.1.3 Lokale static-Variable: Funktion mit Gedächtnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

2.2 Schnittstellen zum Datentransfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

2.2.1 Übergabe per Wert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

2.2.2 Übergabe per Referenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

2.2.3 Gefahren bei der Rückgabe von Referenzen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

2.2.4 Vorgegebene Parameterwerte und unterschiedliche Parameterzahl . . . . . . . . . 119

2.2.5 Überladen von Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

2.2.6 Funktion main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

2.2.7 Beispiel Taschenrechnersimulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

2.2.8 Spezifikation von Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

2.2.9 Alternative Funktions-Syntax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

2.3 Modulare Programmgestaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

2.3.1 Steuerung der Übersetzung nur mit #include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

2.3.2 Einbinden vorübersetzter Programmteile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

Page 10: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 9

2.3.3 Dateiübergreifende Gültigkeit und Sichtbarkeit. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

2.3.4 Übersetzungseinheit, Deklaration, Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

2.3.5 Präprozessordirektiven und Makros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

2.4 Funktions-Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

2.4.1 Spezialisierung von Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

2.4.2 Einbinden von Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

2.5 inline-Funktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

2.6 constexpr-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

2.7 Namensräume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

2.8 C++-Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

2.8.1 Einbinden von C-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

3 Objektorientierung 1 .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

3.1 Abstrakte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

3.2 Klassen und Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

3.2.1 const-Objekte und Methoden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

3.2.2 inline-Elementfunktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

3.3 Initialisierung und Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

3.3.1 Standardkonstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

3.3.2 Direkte Initialisierung der Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

3.3.3 Allgemeine Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

3.3.4 Kopierkonstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

3.3.5 Typumwandlungskonstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

3.3.6 Konstruktor und mehr vorgeben oder verbieten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

3.3.7 Einheitliche Initialisierung und Sequenzkonstruktor. . . . . . . . . . . . . . . . . . . . . . . . . . 170

3.3.8 Delegierender Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172

3.3.9 constexpr-Konstruktor und -Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

3.4 Beispiel: Rationale Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

3.4.1 Aufgabenstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

3.4.2 Entwurf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

3.4.3 Implementation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

3.5 Destruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186

3.6 Wie kommt man zu Klassen und Objekten? Ein Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188

3.7 Gegenseitige Abhängigkeit von Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

4 Intermezzo: Zeiger .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195

4.1 Zeiger und Adressen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

4.2 C-Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199

Page 11: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

10 Inhalt

4.2.1 C-Array und sizeof. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

4.2.2 Initialisierung von C-Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

4.2.3 Zeigerarithmetik. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

4.2.4 Indexoperator bei C-Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

4.2.5 C-Array mit begin() und end() durchlaufen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

4.3 C-Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

4.4 Dynamische Datenobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211

4.4.1 Freigeben dynamischer Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213

4.5 Zeiger und Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216

4.5.1 Parameterübergabe mit Zeigern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216

4.5.2 Parameter des main-Programms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218

4.5.3 Gefahren bei der Rückgabe von Zeigern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219

4.6 this-Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

4.7 Mehrdimensionale C-Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

4.7.1 Statische mehrdimensionale C-Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

4.7.2 Array als Funktionsparameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221

4.7.3 Dynamisch erzeugte mehrdimensionale Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

4.7.4 Klasse für dynamisches zweidimensionales Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227

4.8 Binäre Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

4.9 Zeiger auf Funktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236

4.10 Standard-Typumwandlungen für Zeiger. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

4.11 Zeiger auf Elementfunktionen und -daten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

4.11.1 Zeiger auf Elementfunktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

4.11.2 Zeiger auf Elementdaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242

4.12 Komplexe Deklarationen lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242

4.12.1 Lesbarkeit mit typedef und using verbessern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243

5 Objektorientierung 2 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .245

5.1 Eine String-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245

5.1.1 friend-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251

5.2 Klassenspezifische Daten und Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252

5.2.1 Klassenspezifische Konstante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256

5.3 Klassen-Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258

5.3.1 Ein Stack-Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258

5.3.2 Stack mit statisch festgelegter Größe. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260

5.4 Template-Metaprogrammierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262

5.5 Variadic Templates: Templates mit variabler Parameterzahl . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265

Page 12: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 11

5.5.1 Klassen-Templates mit variabler Stelligkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268

5.6 Typbestimmung mit decltype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269

5.6.1 declval . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272

6 Vererbung ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .273

6.1 Vererbung und Initialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279

6.2 Zugriffsschutz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280

6.3 Typbeziehung zwischen Ober- und Unterklasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282

6.4 Code-Wiederverwendung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

6.4.1 Konstruktor erben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284

6.5 Überschreiben von Funktionen in abgeleiteten Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

6.5.1 Virtuelle Funktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287

6.5.2 Abstrakte Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292

6.5.3 Virtueller Destruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

6.5.4 Private virtuelle Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

6.6 Probleme der Modellierung mit Vererbung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303

6.7 Mehrfachvererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305

6.7.1 Namenskonflikte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308

6.7.2 Virtuelle Basisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309

6.8 Standard-Typumwandlungsoperatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312

6.9 Typinformationen zur Laufzeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315

6.10 Using-Deklaration für protected-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317

6.11 Private- und Protected-Vererbung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317

7 Fehlerbehandlung ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321

7.1 Ausnahmebehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323

7.1.1 Exception-Spezifikation in Deklarationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326

7.1.2 Exception-Hierarchie in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327

7.1.3 Besondere Fehlerbehandlungsfunktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328

7.1.4 Erkennen logischer Fehler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330

7.1.5 Arithmetische Fehler / Division durch 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332

7.2 Speicherbeschaffung mit new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333

7.3 Exception-Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335

8 Überladen von Operatoren ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .337

8.1 Rationale Zahlen — noch einmal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

8.1.1 Arithmetische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

8.1.2 Ausgabeoperator << . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342

Page 13: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

12 Inhalt

8.2 Eine Klasse für Vektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343

8.2.1 Index-Operator [ ] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346

8.2.2 Zuweisungsoperator = . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349

8.2.3 Mathematische Vektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351

8.2.4 Multiplikationsoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353

8.3 Inkrement-Operator ++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354

8.4 Typumwandlungsoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358

8.5 Smart Pointer: Operatoren -> und * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359

8.5.1 Smart Pointer und die C++-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364

8.6 Objekt als Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364

8.7 new und delete überladen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366

8.7.1 Speichermanagement mit malloc und free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370

8.7.2 Unterscheidung zwischen Heap- und Stack-Objekten . . . . . . . . . . . . . . . . . . . . . . . . 371

8.7.3 Fehlende delete-Anweisung entdecken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373

8.7.4 Eigene Speicherverwaltung für einen bestimmten Typ . . . . . . . . . . . . . . . . . . . . . . . 374

8.7.5 Empfehlungen im Umgang mit new und delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378

8.8 Operatoren für Literale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379

8.8.1 Stringliterale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379

8.8.2 Benutzerdefinierte Literale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380

8.9 Mehrdimensionale Matrizen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383

8.9.1 Zweidimensionale Matrix als Vektor von Vektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . 384

8.9.2 Dreidimensionale Matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386

8.10 Zuweisung bei Vererbung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389

9 Dateien und Ströme... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .399

9.1 Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

9.1.1 Formatierung der Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

9.2 Eingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404

9.3 Manipulatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407

9.3.1 Eigene Manipulatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412

9.4 Fehlerbehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414

9.5 Typumwandlung von Dateiobjekten nach bool. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415

9.6 Arbeit mit Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416

9.6.1 Positionierung in Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417

9.6.2 Lesen und Schreiben in derselben Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418

9.7 Umleitung auf Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419

9.8 Tabelle formatiert ausgeben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421

Page 14: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 13

9.9 Formatierte Daten lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422

9.9.1 Eingabe benutzerdefinierter Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422

9.10 Blockweise lesen oder schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423

9.11 Ergänzungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426

10 Einführung in die Standard Template Library (STL) .. . . . . . . . . . . . . . . . . . . . . . . . . . . .427

10.1 Container, Iteratoren, Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428

10.2 Iteratoren im Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433

10.3 Beispiel verkettete Liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434

Teil II: Fortgeschrittene Themen .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .439

11 Lambda-Funktionen ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441

11.1 Eigenschaften. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442

11.1.1 Äquivalenz zum Funktionszeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443

11.1.2 Lambda-Funktion und Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444

11.2 Generische Lambda-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444

11.3 Parametererfassung mit [] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446

12 Performance, Wert- und Referenzsemantik .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .447

12.1 Performanceproblem Wertsemantik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449

12.1.1 Auslassen der Kopie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449

12.1.2 Temporäre Objekte bei der Zuweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450

12.2 Referenzsemantik für R-Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451

12.3 Optimierung durch Referenzsemantik für R-Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453

12.3.1 Bewegender Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456

12.3.2 Bewegender Zuweisungsoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456

12.4 Die move()-Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457

12.4.1 Regel zur Template-Auswertung von &&-Parametern . . . . . . . . . . . . . . . . . . . . . . . . 459

12.5 Ein effizienter binärer Plusoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460

12.5.1 Return Value Optimization (RVO) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460

12.5.2 Kopien temporärer Objekte eliminieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461

12.5.3 Verbesserung durch verzögerte Auswertung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462

13 Reguläre Ausdrücke ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .465

13.1 Elemente regulärer Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466

13.1.1 Greedy oder lazy? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468

13.2 Interaktive Auswertung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469

Page 15: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

14 Inhalt

13.3 Auszug des regex-APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472

13.4 Anwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474

14 Threads ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .475

14.1 Zeit und Dauer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476

14.2 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477

14.3 Die Klasse thread. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480

14.3.1 Thread-Group. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483

14.4 Synchronisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485

14.5 Thread-Steuerung: pausieren, fortsetzen, beenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488

14.5.1 Data Race . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492

14.6 Interrupt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493

14.7 Warten auf Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495

14.8 Reader/Writer-Problem. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500

14.8.1 Wenn Threads verhungern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505

14.8.2 Reader/Writer-Varianten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507

14.9 Thread-Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507

15 Grafische Benutzungsschnittstellen ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .509

15.1 Ereignisgesteuerte Programmierung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510

15.2 GUI-Programmierung mit Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511

15.2.1 Installation und Einsatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511

15.2.2 Meta-Objektsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512

15.2.3 Der Programmablauf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513

15.2.4 Ereignis abfragen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514

15.3 Signale, Slots und Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515

15.4 Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524

15.5 Qt oder Boost?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527

15.5.1 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528

15.5.2 Verzeichnisbaum durchwandern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529

16 Internet-Anbindung ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531

16.1 Protokolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532

16.2 Adressen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532

16.3 Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536

16.3.1 Bidirektionale Kommunikation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539

16.3.2 UDP-Sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541

16.3.3 Atomuhr mit UDP abfragen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543

Page 16: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 15

16.4 HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545

16.4.1 Verbindung mit GET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547

16.4.2 Verbindung mit POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551

16.5 Mini-Webserver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552

17 Datenbankanbindung ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561

17.1 C++-Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562

17.2 Anwendungsbeispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566

Teil III: Praktische Methoden und Werkzeugeder Softwareentwicklung .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .573

18 Effiziente Programmerzeugung mit make ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .575

18.1 Quellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576

18.2 Wirkungsweise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577

18.3 Variablen und Muster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579

18.4 Universelles Makefile für einfache Projekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580

18.5 Automatische Ermittlung von Abhängigkeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582

18.5.1 Getrennte Verzeichnisse: src, obj, bin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583

18.6 Makefile für Verzeichnisbäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585

18.6.1 Rekursive Make-Aufrufe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586

18.6.2 Ein Makefile für alles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588

18.7 Automatische Erzeugung von Makefiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589

18.7.1 Makefile für rekursive Aufrufe erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590

18.8 Erzeugen von Bibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591

18.8.1 Statische Bibliotheksmodule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592

18.8.2 Dynamische Bibliotheksmodule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593

18.9 GNU Autotools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596

18.10 CMake . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599

18.11 Code Bloat bei der Instanziierung von Templates vermeiden . . . . . . . . . . . . . . . . . . . . . . . . . . . 599

18.11.1 extern-Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600

18.11.2 Aufspaltung in Schnittstelle und Implementation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602

19 Unit-Test .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .603

19.1 Werkzeuge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604

19.2 Test Driven Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605

19.3 Boost Unit Test Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606

19.3.1 Beispiel: Testgetriebene Entwicklung einer Operatorfunktion . . . . . . . . . . . . . . . 608

Page 17: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

16 Inhalt

19.3.2 Fixture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612

19.3.3 Testprotokoll und Log-Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613

19.3.4 Prüf-Makros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614

19.3.5 Kommandozeilen-Optionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617

20 Werkzeuge zur Verwaltung von Projekten ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619

20.1 Dokumentation und Strukturanalyse mit doxygen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619

20.1.1 Strukturanalyse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623

20.2 Versionskontrolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624

20.2.1 Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625

20.2.2 Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626

20.3 Projektverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627

Teil IV: Das C++-Rezeptbuch:Tipps und Lösungen für typische Aufgaben .. . . . . . . . . . . . . . . . . . . . . .629

21 Sichere Programmentwicklung ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631

21.1 Regeln zum Design von Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631

21.2 Defensive Programmierung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634

21.2.1 double- und float-Werte richtig vergleichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635

21.2.2 const und constexpr verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635

21.2.3 Anweisungen nach for/if/while einklammern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635

21.2.4 int und unsigned/size_t nicht mischen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636

21.2.5 size_t oder auto statt unsigned int verwenden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636

21.2.6 Postfix++ mit Präfix++ implementieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637

21.2.7 Ein Destruktor darf keine Exception werfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637

21.2.8 explicit-Typumwandlungsoperator bevorzugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638

21.2.9 explicit-Konstruktor für eine Typumwandlung bevorzugen . . . . . . . . . . . . . . . . . 638

21.2.10 Leere Standardkonstruktoren vermeiden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638

21.2.11 Mit override Schreibfehler reduzieren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638

21.2.12 Kopieren und Zuweisung verbieten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639

21.2.13 Vererbung verbieten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640

21.2.14 Überschreiben einer virtuellen Methode verhindern . . . . . . . . . . . . . . . . . . . . . . . . . . 640

21.2.15 The Big Three – oder Big Five? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641

21.2.16 One Definition Rule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642

21.2.17 Defensiv Objekte löschen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642

21.2.18 Speicherbeschaffung und -freigabe kapseln. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642

Page 18: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 17

21.2.19 Programmierrichtlinien einhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642

21.3 Exception-sichere Beschaffung von Ressourcen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642

21.3.1 Sichere Verwendung von unique_ptr und shared_ptr. . . . . . . . . . . . . . . . . . . . . . . . . 643

21.3.2 So vermeiden Sie new und delete!. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643

21.3.3 So vermeiden Sie new[] und delete[]! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644

21.3.4 shared_ptr für Arrays korrekt verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644

21.3.5 unique_ptr für Arrays korrekt verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645

21.3.6 Exception-sichere Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645

21.3.7 Exception-sicherer Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646

21.3.8 Exception-sichere Zuweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649

21.4 Empfehlungen zur Thread-Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650

21.4.1 Warten auf die Freigabe von Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650

21.4.2 Deadlock-Vermeidung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651

21.4.3 notify_all oder notify_one?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651

21.4.4 Performance mit Threads verbessern?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652

22 Von der UML nach C++ ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .653

22.1 Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654

22.2 Interface anbieten und nutzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654

22.3 Assoziation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656

22.3.1 Aggregation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659

22.3.2 Komposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659

23 Algorithmen für verschiedene Aufgaben ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661

23.1 Algorithmen mit Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662

23.1.1 String splitten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662

23.1.2 String in Zahl umwandeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663

23.1.3 Zahl in String umwandeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667

23.1.4 Strings sprachlich richtig sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667

23.1.5 Umwandlung in Klein- bzw. Großschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669

23.1.6 Strings sprachlich richtig vergleichen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671

23.1.7 Von der Groß-/Kleinschreibung unabhängiger Zeichenvergleich . . . . . . . . . . . 672

23.1.8 Von der Groß-/Kleinschreibung unabhängige Suche . . . . . . . . . . . . . . . . . . . . . . . . . 673

23.2 Textverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675

23.2.1 Datei durchsuchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675

23.2.2 Ersetzungen in einer Datei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676

23.2.3 Code-Formatierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678

23.2.4 Lines of Code (LOC) ermitteln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679

Page 19: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

18 Inhalt

23.2.5 Zeilen, Wörter und Zeichen einer Datei zählen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681

23.2.6 CSV-Datei lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681

23.2.7 Kreuzreferenzliste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683

23.3 Operationen auf Folgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686

23.3.1 Container anzeigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686

23.3.2 Folge mit gleichen Werten initialisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686

23.3.3 Folge mit Werten eines Generators initialisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687

23.3.4 Folge mit fortlaufenden Werten initialisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687

23.3.5 Summe und Produkt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688

23.3.6 Mittelwert und Standardabweichung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689

23.3.7 Skalarprodukt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689

23.3.8 Folge der Teilsummen oder -produkte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690

23.3.9 Folge der Differenzen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691

23.3.10 Kleinstes und größtes Element finden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692

23.3.11 Elemente rotieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694

23.3.12 Elemente verwürfeln. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695

23.3.13 Dubletten entfernen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696

23.3.14 Reihenfolge umdrehen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698

23.3.15 Anzahl der Elemente, die einer Bedingung genügen. . . . . . . . . . . . . . . . . . . . . . . . . . 699

23.3.16 Gilt X für alle, keins oder wenigstens ein Element einer Folge? . . . . . . . . . . . . 700

23.3.17 Permutationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701

23.3.18 Lexikografischer Vergleich. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703

23.4 Sortieren und Verwandtes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705

23.4.1 Partitionieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705

23.4.2 Sortieren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707

23.4.3 Stabiles Sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708

23.4.4 Partielles Sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709

23.4.5 Das n.-größte oder n.-kleinste Element finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710

23.4.6 Verschmelzen (merge) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711

23.5 Suchen und Finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714

23.5.1 Element finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714

23.5.2 Element einer Menge in der Folge finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715

23.5.3 Teilfolge finden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716

23.5.4 Bestimmte benachbarte Elemente finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718

23.5.5 Bestimmte aufeinanderfolgende Werte finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719

23.5.6 Binäre Suche. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720

23.6 Mengenoperationen auf sortierten Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723

Page 20: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 19

23.6.1 Teilmengenrelation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723

23.6.2 Vereinigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724

23.6.3 Schnittmenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725

23.6.4 Differenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725

23.6.5 Symmetrische Differenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726

23.7 Heap-Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727

23.7.1 pop_heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728

23.7.2 push_heap. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729

23.7.3 make_heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729

23.7.4 sort_heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730

23.7.5 is_heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730

23.8 Vergleich von Containern auch ungleichen Typs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731

23.8.1 Unterschiedliche Elemente finden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731

23.8.2 Prüfung auf gleiche Inhalte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733

23.9 Rechnen mit komplexen Zahlen: Der C++-Standardtyp complex . . . . . . . . . . . . . . . . . . . . . . 734

23.10 Schnelle zweidimensionale Matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736

23.10.1 Optimierung mathematischer Array-Operationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739

23.11 Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742

23.11.1 Implementierung mit einem Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742

23.11.2 Implementierung mit einer Referenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743

23.11.3 Meyers’ Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744

23.12 Vermischtes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746

23.12.1 Erkennung eines Datums. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746

23.12.2 Erkennung einer IP4-Adresse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748

23.12.3 Erzeugen von Zufallszahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749

23.12.4 for_each — Auf jedem Element eine Funktion ausführen . . . . . . . . . . . . . . . . . . . . 754

23.12.5 Verschiedene Möglichkeiten, Container-Bereiche zu kopieren. . . . . . . . . . . . . . . 754

23.12.6 Vertauschen von Elementen, Bereichen und Containern . . . . . . . . . . . . . . . . . . . . . 757

23.12.7 Elemente transformieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757

23.12.8 Ersetzen und Varianten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759

23.12.9 Elemente herausfiltern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 760

23.12.10 Grenzwerte von Zahltypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762

23.12.11 Minimum und Maximum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763

24 Datei- und Verzeichnisoperationen ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .765

24.1 Datei oder Verzeichnis löschen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766

24.2 Datei oder Verzeichnis umbenennen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 767

Page 21: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

20 Inhalt

24.3 Verzeichnis anlegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768

24.4 Verzeichnis anzeigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769

24.5 Verzeichnisbaum anzeigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770

Teil V: Die C++-Standardbibliothek .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .773

25 Aufbau und Übersicht .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .775

25.1 Auslassungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777

25.2 Beispiele des Buchs und die C++-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779

26 Hilfsfunktionen und -klassen ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781

26.1 Relationale Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781

26.2 Unterstützung der Referenzsemantik für R-Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782

26.2.1 move() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782

26.2.2 forward() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783

26.3 Paare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784

26.4 Tupel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786

26.5 Indexfolgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788

26.6 Funktionsobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789

26.6.1 Arithmetische, vergleichende und logische Operationen . . . . . . . . . . . . . . . . . . . . . 789

26.6.2 Funktionsobjekte zum Negieren logischer Prädikate . . . . . . . . . . . . . . . . . . . . . . . . . . 790

26.6.3 Binden von Argumentwerten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791

26.6.4 Funktionen in Objekte umwandeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792

26.7 Templates für rationale Zahlen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794

26.8 Hüllklasse für Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796

26.9 Type Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797

26.9.1 Wie funktionieren Type Traits? — ein Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798

26.9.2 Abfrage von Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800

26.9.3 Abfrage numerischer Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802

26.9.4 Typbeziehungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802

26.9.5 Typumwandlungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802

26.9.6 Auswahl weiterer Transformationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803

27 Container .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .805

27.1 Gemeinsame Eigenschaften. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807

27.1.1 Initialisierungslisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809

27.1.2 Konstruktion an Ort und Stelle. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810

27.1.3 Reversible Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810

Page 22: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 21

27.2 Sequenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811

27.2.1 vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812

27.2.2 vector<bool> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813

27.2.3 list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814

27.2.4 deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817

27.2.5 stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818

27.2.6 queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820

27.2.7 priority_queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821

27.2.8 array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823

27.3 Sortierte assoziative Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826

27.3.1 map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826

27.3.2 multimap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831

27.3.3 set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831

27.3.4 multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835

27.4 Hash-Container. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835

27.4.1 unordered_map. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837

27.4.2 unordered_multimap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842

27.4.3 unordered_set. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843

27.4.4 unordered_multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845

27.5 bitset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846

28 Iteratoren ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .849

28.1 Iterator-Kategorien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850

28.1.1 Anwendung von Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852

28.2 distance() und advance() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854

28.3 Bereichszugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855

28.3.1 Reverse-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856

28.4 Insert-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857

28.5 Stream-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858

29 Algorithmen ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861

29.1 Algorithmen mit Prädikat. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862

29.1.1 Algorithmen mit binärem Prädikat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862

29.2 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863

30 Nationale Besonderheiten ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .867

30.1 Sprachumgebungen festlegen und ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868

30.1.1 Die locale-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870

Page 23: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

22 Inhalt

30.2 Zeichensätze und -codierung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871

30.3 Zeichenklassifizierung und -umwandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875

30.4 Kategorien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876

30.4.1 collate. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876

30.4.2 ctype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877

30.4.3 numeric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879

30.4.4 monetary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880

30.4.5 time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883

30.4.6 messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885

30.5 Konstruktion eigener Facetten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886

31 String ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .889

32 Speichermanagement ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .899

32.1 unique_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899

32.1.1 make_unique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901

32.2 shared_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902

32.2.1 make_shared . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903

32.2.2 Typumwandlung in einen Oberklassentyp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903

32.3 weak_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904

32.4 new mit Speicherortangabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 905

33 Numerische Arrays (valarray) .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .907

33.1 Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908

33.2 Elementfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908

33.3 Binäre Valarray-Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911

33.4 Mathematische Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 913

33.5 slice und slice_array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 914

33.6 gslice und gslice_array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917

33.7 mask_array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920

33.8 indirect_array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921

34 Ausgewählte C-Header .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .923

34.1 <cassert> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924

34.2 <cctype> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924

34.3 <cerrno> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925

34.4 <cmath>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925

34.5 <cstdarg> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 926

Page 24: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Inhalt 23

34.6 <cstddef>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927

34.7 <cstdio> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927

34.8 <cstdlib> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927

34.9 <cstring> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929

34.10 <ctime> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931

A Anhang... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .933

A.1 ASCII-Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 933

A.2 C++-Schlüsselwörter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935

A.3 Compilerbefehle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936

A.4 Rangfolge der Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937

A.5 Lösungen zu den Übungsaufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 938

A.6 Installation der Software für Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948

A.6.1 Installation des Compilers und der Entwicklungsumgebung. . . . . . . . . . . . . . . . . 948

A.6.2 Integrierte Entwicklungsumgebung einrichten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949

A.6.3 De-Installation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950

A.7 Installation der Software für Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951

A.7.1 Installation des Compilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951

A.7.2 Installation von Boost . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952

A.7.3 Installation und Einrichtung von Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952

A.7.4 Beispieldateien entpacken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 953

Glossar .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .955

Literaturverzeichnis .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .965

Register.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .969

Page 25: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.
Page 26: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

C++14

Vorwort

Dieser korrigierte Nachdruck der 4. Auflage enthält Korrekturen und kleinere Ergänzun-gen. Die 4. Auflage unterscheidet sich von der 3. Auflage durch die vollständige Umstel-lung auf den 2014 verabschiedeten ISO C++-Standard. Um die Seitenzahl beizubehalten,wurden einige Kapitel neu strukturiert und zusammengefasst, und die Lösungen zu denProgrammierübungsaufgaben sind nicht mehr abgedruckt, sondern auf der Internetseitezum Buch zu finden. Die Veränderungen gegenüber dem C++-Standard von 2011 sinddurch Markierungen am Seitenrand (wie hier) gekennzeichnet. Das Buch ist konformzum neuen C++-Standard, ohne den Anspruch auf Vollständigkeit zu erheben – dasStandard-Dokument [ISOC++] umfasst allein schon mehr als 1300 Seiten. Sie finden indiesem Buch eine verständliche und mit vielen Beispielen angereicherte Einführung indie Sprache. Im Teil »Das C++ Rezeptbuch« gibt es zahlreiche Tipps und Lösungen für ty-pische Aufgaben, die in der täglichen Praxis anfallen. Es gibt konkrete, sofort umsetzbareLösungsvorschläge. Zahlreiche Algorithmen für praxisnahe Problemstellungen helfen beider täglichen Arbeit. Auf größtmögliche Portabilität wird geachtet: Die Beispiele funktio-nieren unter Linux genau so wie unter Windows. Die problembezogene Orientierung lässtdie in die Sprache einführenden Teile kürzer werden. Damit wird das Lernen erleichtert,und die Qualität des Buchs, auch als Nachschlagewerk dienen zu können, bleibt erhalten.

Für wen ist dieses Buch geschrieben?Dieses Buch ist für alle geschrieben, die einen kompakten und gleichzeitig gründlichenEinstieg in die Konzepte und Programmierung mit C++ suchen. Es ist für Anfänger1

gedacht, die noch keine Programmiererfahrung haben, aber auch für andere, die dieseProgrammiersprache kennen lernen möchten. Beiden Gruppen und auch C++-Erfahrenendient das Buch als ausführliches Nachschlagewerk.Die ersten 10 Kapitel führen in die Sprache ein. Es wird sehr schnell ein Verständnis desobjektorientierten Ansatzes entwickelt. Die sofortige praktische Umsetzung des Gelern-ten steht im Vordergrund. C++ wird als Programmiersprache unabhängig von speziellen

1 Geschlechtsbezogene Formen meinen hier und im Folgenden stets Männer, Frauen und andere.

Page 27: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

26 Vorwort

Produkten beschrieben; C-Kenntnisse werden nicht vorausgesetzt. Das Buch eignet sichzum Selbststudium und als Begleitbuch zu einer Vorlesung oder zu Kursen. Die vielenBeispiele sind leicht nachzuvollziehen und praxisnah umsetzbar. Klassen und Objekte,Templates und Exceptions sind Ihnen bald keine Fremdworte mehr. Es gibt mehr als 90Übungsaufgaben – mit Musterlösungen im Anhang. Durch das Studium dieser Kapitelwerden aus Anfängern bald Fortgeschrittene.Diesen und anderen Fortgeschrittenen und Profis bietet das Buch kurze Einführungen indie Themen Thread-Programmierung, Netzwerk-Programmierung mit Sockets einschließ-lich eines kleinen Webservers, Datenbankanbindung, grafische Benutzungsoberflächenund mehr. Dabei wird durch Einsatz der Boost-Library und des Qt-Frameworks größt-mögliche Portabilität erreicht.Softwareentwicklung ist nicht nur Programmierung: Einführend werden anhand vonBeispielen unter anderem die Automatisierung der Programmerzeugung mit Make, dieDokumentationserstellung mit Doxygen und die Versionskontrolle behandelt. Das Pro-grammdesign wird durch konkrete Umsetzungen von UML2-Mustern nach C++ unter-stützt. Das integrierte »C++-Rezeptbuch« mit mehr als 150 praktischen Lösungen, dassehr umfangreiche Register und das detaillierte Inhaltsverzeichnis machen das Buch zueinem praktischen Nachschlagewerk für alle, die sich mit der Softwareentwicklung inC++ beschäftigen.

Moderne Programmiermethodik

Beim Programmieren geht es nicht nur darum, eine Programmiersprache zu lernen. Esgeht auch darum, Programme zu schreiben, die hohen Qualitätsansprüchen gerecht wer-den. Dazu gehört das Knowhow, die Mittel der Sprache richtig einzusetzen. Dass einProgramm läuft, reicht nicht. Es soll auch gut entworfen sein, möglichst wenige Fehlerenthalten, selbst mit Fehlern in Daten umgehen können, verständlich geschrieben undschnell in der Ausführung sein. In diesem Sinn liegt ein Schwerpunkt des Buchs aufder Methodik des Programmierens, unterstützt durch Darstellung der informatorischenGrundlagen, viele Hinweise im Text und demonstriert an vielen Beispielen.

ÜbersichtSchwerpunkt von Teil I ist die Einführung in die Programmiersprache. Die anschließen-den Teile gehen darüber hinaus und konzentrieren sich auf die verschiedenen Problemeder täglichen Praxis. Die in dieser Übersicht verwendeten Begriffe mögen Ihnen zum Teilnoch unbekannt sein – in den betreffenden Kapiteln werden sie ausführlich erläutert.

Teil I – Einführung in C++

Das Kapitel 1 vermittelt zunächst die Grundlagen, wie ein Programm geschrieben undzum Laufen gebracht wird. Es folgen einfache Datentypen und Anweisungen zur Kon-trolle des Programmablaufs. Die Einführung der C++-Datentypen vector und string unddie einfachen Ein- und Ausgabe beenden das Kapitel.Das Kapitel 2 zeigt Ihnen, wie Sie Funktionen schreiben. Makros, Templates für Funktio-nen und die modulare Gestaltung von Programmen folgen.

2 UML ist eine grafische Beschreibungssprache für die objektorientierte Programmierung.

Page 28: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

Vorwort 27

Objektorientierung ist der Schwerpunkt von Kapitel 3. Dabei geht es nicht nur um dieKonstruktion von Objekten, sondern auch um den Weg von der Problemstellung zu Klas-sen und Objekten. Zeiger sowie die Erzeugung von Objekten zur Laufzeit sind Inhalt vonKapitel 4. Kapitel 5 führt das Thema Objektorientierung fort. Dabei lernen Sie kennen,wie eine String-Klasse funktioniert, und wie Sie Klassen-Templates und Templates miteiner variablen Anzahl von Parametern konstruieren. Das Kapitel 6 zeigt Ihnen das Mit-tel objektorientierter Sprachen, um Generalisierungs- und Spezialisierungsbeziehungenauszudrücken: die Vererbung mit ihren Möglichkeiten. Strategien zur Fehlerbehandlungmit Exceptions finden Sie in Kapitel 7. Das Kapitel 8 zeigt, wie Sie Operatorsymbolen wie+ und - eigene Bedeutungen zuweisen können und in welchem Zusammenhang das sinn-voll ist. Sie lernen, »intelligente« Zeiger (Smart Pointer) zu konstruieren und Objekte alsFunktionen einzusetzen. Kapitel 9 beschreibt ausführlich die Ein- und Ausgabemöglich-keiten, die vorher nur einführend gestreift werden, einschließlich der Fehlerbehandlungund der Formatierung der Ausgabe. Eine Einführung in die Standard Template Library(STL) bietet Kapitel 10. Die STL und ihre Wirkungsweise bilden die Grundlage eines sehrgroßen Teils der C++-Standardbibliothek.

Teil II – Fortgeschrittene Themen

In diesem Teil folgen fortgeschrittene Themen wie Lambda-Funktionen (Kapitel 11),Wert- und Referenzsemantik (Kapitel 12), reguläre Ausdrücke (Kapitel 13) und die Pro-grammierung paralleler Abläufe mit Threads (Kapitel 14). Das Kapitel 15 zeigt, wie gra-fische Benutzungsschnittstellen konstruiert werden. Wie ein Programm die Verbindungmit dem Internet aufnehmen kann, dokumentiert das Kapitel 16. Und wohin mit denganzen Daten, die bei Programmende nicht verloren gehen sollen? In Kapitel 17 lernenSie, wie ein Programm an eine Datenbank angebunden wird. Manche der genanntenThemen sind so umfangreich, dass sie selbst Bücher füllen. Für diese Themen wird dahernur ein Einstieg geboten.

Teil III – Praktische Methoden und Werkzeuge der Softwareentwicklung

Die Entwicklung von Programmen besteht nicht nur im Schreiben von Code. Die Compi-lation eines Projekts mit vielen Programmdateien und Abhängigkeiten kann schnell einkomplexer Vorgang werden. Die Automatisierung dieses Prozesses mit dem Tool make istThema von Kapitel 18. Programme sind nicht auf Anhieb fehlerfrei. Sie müssen getestetwerden: Kapitel 19 stellt ein Werkzeug für den Unit-Test vor und zeigt den praktischenEinsatz. Kapitel 20 demonstriert ein Werkzeug zur automatischen Dokumentationserstel-lung und geht kurz auf die Versionsverwaltung und die Projektverwaltung ein.

Teil IV – Das C++-Rezeptbuch: Tipps und Lösungen für typische Aufgaben

Sichere Programmentwicklung ist die Überschrift des Kapitels 21. Sie finden dort Regelnzum Design von Methoden und Tipps zur defensiven Programmierung, die die Wahr-scheinlichkeit von Fehlern vermindern. Kapitel 22 zeigt Rezepte, wie Sie bestimmte UML-Muster in C++-Konstruktionen umwandeln können. Algorithmen für viele verschiedeneAufgaben finden Sie in Kapitel 23. Wegen der Vielzahl empfiehlt sich ein Blick in dasInhaltsverzeichnis, um einen Überblick zu gewinnen. Der C++-Standard bietet für viele

Page 29: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

28 Vorwort

Datei- und Verzeichnisoperationen keine Unterstützung an. Kapitel 24 enthält deshalbfertige Rezepte zum Anlegen, Löschen und Lesen von Verzeichnissen und mehr.

Teil V – Die C++-Standardbibliothek

Hier wird die C++-Standardbibliothek in Kürze beschrieben. Die Inhalte dieses Teils sind:Hilfsfunktionen und -klassen, Container, Iteratoren, Algorithmen, Einstellung nationalerBesonderheiten, String, Speicherverwaltung, Funktionen der Programmiersprache C.

Anhang

Der Anhang enthält unter anderem verschiedene hilfreiche Tabellen und die Lösungender Übungsaufgaben.

Wo finden Sie was?Bei der Programmentwicklung wird häufig das Problem auftauchen, etwas nachschlagenzu müssen. Es gibt die folgenden Hilfen:Erklärungen zu Begriffen sind im Glossar ab Seite 955 aufgeführt.Es gibt ein recht umfangreiches Stichwortverzeichnis ab Seite 969 und ein sehr detail-liertes Inhaltsverzeichnis.

Software zum BuchAuf der Webseite http://www.cppbuch.de/ finden Sie die Software zu diesem Buch. Sieenthält unter anderem einen Compiler, eine Integrierte Entwicklungsumgebung sowiealle Programmbeispiele und die Lösungen zu den Aufgaben. Sie finden dort auch weitereHinweise, Errata und nützliche Links.

Zu guter LetztAllen Menschen, die dieses Buch durch Hinweise und Anregungen verbessern halfen,sei an dieser Stelle herzlich gedankt, insbesondere Prof. Dr. Ulrich Eisenecker von derUniversität Leipzig. Frau Brigitte Bauer-Schiewek und Frau Irene Weilhart vom HanserVerlag danke ich für die sehr gute Zusammenarbeit.

Bremen, im August 2016Ulrich Breymann

Page 30: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

I.Teil I:Einführung in C++

Page 31: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.
Page 32: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1Es geht los!

Dieses Kapitel behandelt die folgenden Themen:

Entstehung und Entwicklung der Programmiersprache C++Objektorientierte Programmierung – Erste GrundlagenWie schreibe ich ein Programm und bringe es zum Laufen?Einfache Datentypen und OperationenAblauf innerhalb eines Programms steuernErste Definition eigener DatentypenStandarddatentypen vector und stringEinfache Ein- und Ausgabe

1.1 HistorischesC++ wurde etwa ab 1980 von Bjarne Stroustrup als die Programmiersprache »C withclasses« (englisch C mit Klassen), die Objektorientierung stark unterstützt, auf der Basisder Programmiersprache C entwickelt. Später wurde die neue Sprache in C++ umbe-nannt. ++ ist ein Operator der Programmiersprache C, der den Wert einer Größe um 1erhöht. Insofern spiegelt der Name die Eigenschaft »Nachfolger von C«. 1998 wurde C++erstmals von der ISO (International Standards Organisation) und der IEC (Internatio-nal Electrotechnical Commission) standardisiert. Diesem Standard haben sich nationaleStandardisierungsgremien wie ANSI (USA) und DIN (Deutschland) angeschlossen. DieAnforderungen an C++ sind gewachsen, auch zeigte sich, dass manches fehlte und an-deres überflüssig oder fehlerhaft war. Das C++-Standardkomitee hat kontinuierlich ander Verbesserung von C++ gearbeitet, sodass 2003, 2011 und 2014 neue Versionen des

Page 33: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

32 1 Es geht los!

Standards herausgegeben wurden. Die Kurznamen sind dementsprechend C++03, C++11und C++14.C++14 wurde im August 2014 von der zuständigen ISO/IEC-Arbeitsgruppe JTC1/ SC22/WG21 verabschiedet und am 15. Dezember 2014 von der ISO veröffentlicht. ISO-Standardssind kostenpflichtig. Deshalb verweise ich in diesem Buch auf das per Internet zugängli-che Dokument [ISOC++].

1.2 Objektorientierte ProgrammierungNach üblicher Auffassung heißt Programmieren, einem Rechner mitzuteilen, was er tunsoll und wie es zu tun ist. Ein Programm ist ein in einer Programmiersprache formulier-ter Algorithmus oder, anders ausgedrückt, eine Folge von Anweisungen, die der Reihenach auszuführen sind, ähnlich einem Kochrezept, geschrieben in einer besonderen Spra-che, die der Rechner »versteht«. Der Schwerpunkt dieser Betrachtungsweise liegt auf deneinzelnen Schritten oder Anweisungen an den Rechner, die zur Lösung einer Aufgabeabzuarbeiten sind.Was fehlt hier beziehungsweise wird bei dieser Sicht eher stiefmütterlich behandelt? DerRechner muss »wissen«, womit er etwas tun soll. Zum Beispiel soll er

eine bestimmte Summe Geld von einem Konto auf ein anderes transferieren;eine Ampelanlage steuern;ein Rechteck auf dem Bildschirm zeichnen.

Häufig, wie in den ersten beiden Fällen, werden Objekte der realen Welt (Konten, Am-pelanlage ...) simuliert, das heißt im Rechner abgebildet. Die abgebildeten Objekte habeneine Identität. Das Was und das Womit gehören stets zusammen. Beide sind also Eigen-schaften eines Objekts und sollen daher nicht getrennt werden. Ein Konto kann schließ-lich nicht auf Gelb geschaltet werden, und eine Überweisung an eine Ampel ist nichtvorstellbar.Ein objektorientiertes Programm kann man sich als Abbildung von Objekten der rea-len Welt in Software vorstellen. Die Abbildungen werden selbst wieder Objekte genannt.Klassen sind Beschreibungen von Objekten. Die objektorientierte Programmierung be-rücksichtigt besonders die Kapselung von Daten und den darauf ausführbaren Funk-tionen sowie die Wiederverwendbarkeit von Software und die Übertragung von Eigen-schaften von Klassen auf andere Klassen, Vererbung genannt. Auf die einzelnen Begriffewird noch eingegangen. Das Motiv hinter der objektorientierten Programmierung ist dierationelle und vor allem ingenieurmäßige Softwareentwicklung.Wiederverwendung heißt, Zeit und Geld zu sparen, indem bekannte Klassen wiederver-wendet werden. Das Leitprinzip ist hier, das Rad nicht mehrfach neu zu erfinden! Unteranderem durch den Vererbungsmechanismus kann man Eigenschaften von bekanntenObjekten ausnutzen. Zum Beispiel sei Konto eine bekannte Objektbeschreibung mit den»Eigenschaften« Inhaber, Kontonummer, Betrag, Dispo-Zinssatz und so weiter. In einemProgramm für eine Bank kann nun eine Klasse Waehrungskonto entworfen werden, für die

Page 34: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.2 Objektorientierte Programmierung 33

alle Eigenschaften von Konto übernommen (= geerbt) werden könnten. Zusätzlich wärenur noch die Eigenschaft »Währung« hinzuzufügen.Wie Computer können Objekte Anweisungen ausführen. Wir müssen ihnen nur »erzäh-len«, was sie tun sollen, indem wir ihnen eine Auf forderung oder Anweisung senden, diein einem Programm formuliert wird. Anstelle der Begriffe »Aufforderung« oder »Anwei-sung« wird manchmal Botschaft (englisch message) verwendet, was jedoch den Auffor-derungscharakter nicht zur Geltung bringt. Eine gängige Notation (= Schreibweise) fürsolche Aufforderungen ist Objektname.Anweisung(gegebenenfalls Daten). Beispiele:

dieAmpel.blinken(gelb);dieAmpel.ausschalten(); // keine Daten notwendig!dieAmpel.einschalten(gruen);dasRechteck.zeichnen(position, hoehe, breite); // Daten in cmdasRechteck.verschieben(5.0); // Daten in cm

Die Beispiele geben schon einen Hinweis, dass die Objektorientierung uns ein Hilfsmittelzur Modellierung der realen Welt in die Hand gibt.

Klassen

Es wird zwischen der Beschreibung von Objekten und den Objekten selbst unterschieden.Die Beschreibung besteht aus Attributen und Operationen. Attribute bestehen aus ei-nem Namen und Angaben zum Datenformat der Attributwerte. Eine Kontobeschreibungkönnte so aussehen:Attribute:Inhaber: Folge von BuchstabenKontonummer: ZahlBetrag: ZahlDispo-Zinssatz in %: Zahl

Operationen:überweisen (Ziel-Kontonummer, Betrag)abheben(Betrag)einzahlen(Betrag)

Eine Aufforderung ist nichts anderes als der Aufruf einer Operation, die auch Methodegenannt wird. Ein tatsächliches Konto k1 enthält konkrete Daten, also Attributwerte,deren Format mit dem der Beschreibung übereinstimmen muss. Die Tabelle zeigt k1 undein weiteres Konto k2. Beide Konten haben dieselben Attribute, aber verschiedene Wertefür die Attribute.

Tabelle 1.1: Attribute und Werte zweier Konten

Attribut Wert für Konto k1 Wert für Konto k2

Inhaber Roberts, Julia Depp, JohnnyKontonummer 12573001 54688490Betrag -200,30 e 1222,88 eDispo-Zinssatz 13,75 % 13,75 %

Page 35: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

34 1 Es geht los!

Julia will Johnny 1000 e überweisen. Dem Objekt k1 wird also der Auftrag mit denbenötigten Daten mitgeteilt: k1.überweisen(54688490, 1000.00).Johnny will 22 e abheben. Die Aufforderung wird an k2 gesendet: k2.abheben(22).Es scheint natürlich etwas merkwürdig, wenn einem Konto ein Auftrag gegeben wird. Inder objektorientierten Programmierung werden Objekte als Handelnde aufgefasst, die aufAnforderung selbstständig einen Auftrag ausführen, entfernt vergleichbar einem Sach-bearbeiter in einer Firma, der seine eigenen Daten verwaltet und mit anderen Sachbear-beitern kommuniziert, um eine Aufgabe zu lösen.

Die Beschreibung eines tatsächlichen Objekts gibt seine innere Datenstruktur und diemöglichen Operationen oderMethoden an, die auf die inneren Daten anwendbar sind.Zu einer Beschreibung kann es kein, ein oder beliebig viele Objekte geben.

Die Beschreibung eines Objekts in der objektorientierten Programmierung heißt Klasse.Die tatsächlichen Objekte heißen auch Instanzen einer Klasse.Auf die inneren Daten eines Objekts nur mithilfe der vordefinierten Methoden zuzugrei-fen, dient der Sicherheit der Daten und ist ein allgemein anerkanntes Prinzip. Das Prinzipwird Datenabstraktion oder Geheimnisprinzip genannt. Der Softwareentwickler, der dieMethoden konstruiert hat, weiß ja, wie die Daten konsistent (das heißt widerspruchs-frei) bleiben und welche Aktivitäten mit Datenänderungen verbunden sein müssen. ZumBeispiel muss eine Erhöhung des Kontostands mit einer Gutschrift oder einer Einzah-lung verbunden sein. Außerdem wird jeder Buchungsvorgang protokolliert. Es darf nichtmöglich sein, dass jemand anders die Methoden umgeht und direkt und ohne Protokollseinen eigenen Kontostand erhöht. Wenn Sie die Unterlagen eines Kollegen haben möch-ten, greifen Sie auch nicht einfach in seinen Schreibtisch, sondern Sie bitten ihn darum(= Sie senden ihm eine Aufforderung), dass er sie Ihnen gibt.Die hier verwendete Definition einer Klasse als Beschreibung der Eigenschaften einerMenge von Objekten wird im Folgenden beibehalten. Gelegentlich findet man in derLiteratur andere Definitionen, auf die hier nicht weiter eingegangen wird. Weitere Infor-mationen zur Objektorientierung sind in Kapitel 3 und in der einschlägigen Literatur zufinden.

1.3 CompilerCompiler sind die Programme, die Ihren Programmtext in eine für den Computer ver-arbeitbare Form übersetzen. Von Menschen geschriebener und für Menschen lesbarerProgrammtext kann nicht vom Computer »verstanden« werden. Das vom Compiler er-zeugte Ergebnis der Übersetzung kann der Computer aber ausführen. Das Erlernen einerProgrammiersprache ohne eigenes praktisches Ausprobieren ist kaum sinnvoll. NutzenSie daher die Dienste des Compilers möglichst bald anhand der Beispiele – wie, zeigtIhnen der Abschnitt direkt nach der Vorstellung des ersten Programms. Falls Sie nichtschon einen C++-Compiler oder ein C++-Entwicklungssystem haben, um die Beispielekorrekt zu übersetzen, bietet sich die Benutzung der in Abschnitt 1.5 beschriebenen Ent-

Page 36: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.4 Das erste Programm 35

wicklungsumgebungen an. Ein viel verwendeter Compiler ist der GNU1 C++-Compiler[GCC]. Entwicklungsumgebung und Compiler sind kostenlos erhältlich. Ein Installations-programm dafür finden Sie auf der Website zum Buch [SW].

1.4 Das erste ProgrammSie lernen hier die Entwicklung eines ganz einfachen Programms kennen. Dabei wird Ih-nen zunächst das Programm vorgestellt, und wenige Seiten später erfahren Sie, wie Sie eseingeben und zum Laufen bringen können. Der erste Schritt besteht in der Formulierungder Aufgabe. Sie lautet: »Lies zwei Zahlen a und b von der Tastatur ein. Berechne dieSumme beider Zahlen und zeige das Ergebnis auf dem Bildschirm an.« Die Aufgabe istso einfach, wie sie sich anhört! Im zweiten Schritt wird die Aufgabe in die Teilaufgaben»Eingabe«, »Berechnung« und »Ausgabe« zerlegt:

int main() { // Noch tut dieses Programm nichts!// Lies zwei Zahlen ein/∗ Berechne die Summe beiderZahlen ∗/

// Zeige das Ergebnis auf dem Bildschirm an}

Hier sehen Sie schon ein einfaches C++-Programm. Es bedeuten:int ganze Zahl zur Rückgabemain Schlüsselwort für Hauptprogramm() Innerhalb dieser Klammern können dem Hauptprogramm

Informationen mitgegeben werden.{ } Block/* ... */ Kommentar, der über mehrere Zeilen gehen kann// ... Kommentar bis Zeilenende

Ein durch { und } begrenzter Block enthält die Anweisungen an den Rechner. Der Compi-ler übersetzt den Programmtext in eine rechnerverständliche Form. Im obigen Programmsind lediglich Kommentare enthalten und noch keine Anweisungen an den Computer, so-dass unser Programm (noch) nichts tut.Kommentare werden einschließlich der Kennungen vom Compiler vollständig ignoriert.Ein Kommentar, der mit /* beginnt, ist mit der ersten */-Zeichenkombination beendet,auch wenn er sich über mehrere Zeilen erstreckt. Ein mit // beginnender Kommentarendet am Ende der Zeile. Auch wenn Kommentare vom Compiler ignoriert werden, sindsie doch sinnvoll für den menschlichen Leser eines Programms, um ihm die Anweisungenzu erläutern, zum Beispiel für den Programmierer, der Ihr Nachfolger wird, weil Siebefördert worden sind oder die Firma verlassen haben. Kommentare sind auch wichtigfür den Autor eines Programms, der nach einem halben Jahr nicht mehr weiß, warum ergerade diese oder jene komplizierte Anweisung geschrieben hat. Sie sehen:

1 Siehe Glossar Seite 958

Page 37: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

36 1 Es geht los!

Ein Programm ist »nur« ein Text!

Der Text hat eine Struktur entsprechend den C++-Sprachregeln: Es gibt Wörter wiehier das Schlüsselwort main (in C++ werden alle Schlüsselwörter kleingeschrieben).Es gibt weiterhin Zeilen, Satzzeichen und Kommentare.Die Bedeutung des Textes wird durch die Zeilenstruktur nicht beeinflusst. Mit \ undnachfolgendem ENTER ist eine Worttrennung am Zeilenende möglich. Das Zeichen\ wird »Backslash« genannt.Mit dem Symbol ENTER ist hier und im Folgenden dieBetätigung der großen Taste ←↩ rechts auf der Tastatur gemeint.Groß- und Kleinschreibung werden unterschieden! main() ist nicht dasselbe wieMain().

Weil die Zeilenstruktur für den Rechner keine Rolle spielt, kann der Programmtext nachGesichtspunkten der Lesbarkeit gestaltet werden. Im dritten Schritt müssen »nur« nochdie Inhalte der Kommentare als C++-Anweisungen formuliert werden. Dabei bleiben dieKommentare zur Dokumentation stehen, wie im Beispielprogramm unten zu sehen ist.

Listing 1.1: Summe zweier Zahlen berechnen

// cppbuch/k1/summe.cpp// Hinweis: Alle Programmbeispiele sind von der Internet-Seite zum Buch herunterladbar// (http://www.cppbuch.de/ ).// Die erste Zeile in den Programmbeispielen gibt den zugehörigen Dateinamen an.#include<iostream>using namespace std;

int main() {int summe;int summand1;int summand2;// Lies zwei Zahlen eincout << " Zwei ganze Zahlen eingeben:";cin >> summand1 >> summand2;/∗ Berechne die Summe beider Zahlen ∗/summe = summand1 + summand2;

// Zeige das Ergebnis auf dem Bildschirm ancout << "Summe=" << summe << ’\n’;return 0;

}

Es sind einige neue Worte dazugekommen, die hier kurz erklärt werden. Machen Sie sichkeine Sorgen, wenn Sie nicht alles auf Anhieb verstehen! Alles wird im Verlauf des Buchswieder aufgegriffen und vertieft. Wie das Programm zum Laufen gebracht wird, erfahrenSie nur wenige Seiten später.#include<iostream> Einbindung der Ein-/Ausgabefunktionen. Diese Zeile muss in je-

dem Programm stehen, das Eingaben von der Tastatur erwartetoder Ausgaben auf den Bildschirm bringt. Sie können sich vorstel-len, dass der Compiler beim Übersetzen des Programms an dieserStelle erst alle zur Ein- und Ausgabe notwendigen Informationenliest. Details folgen in Abschnitt 2.3.5.

Page 38: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.4 Das erste Programm 37

using namespace std;Der Namensraum std wird benutzt. Schreiben Sie es einfach injedes Programm an diese Stelle und haben Sie Geduld: Eine ge-nauere Erklärung folgt später (Seiten 64 und 149).

int main() main() ist das Hauptprogramm (es gibt auch Unterprogramme).Der zu main() gehörende Programmcode wird durch die ge-schweiften Klammern { und } eingeschlossen. Ein mit { und }

begrenzter Bereich heißt Block. Mit int ist gemeint, dass das Pro-gramm main() nach Beendigung eine Zahl vom Typ int (= gan-ze Zahl) an das Betriebssystem zurückgibt. Dazu dient die untenbeschriebene return-Anweisung. Normalerweise – das heißt beiordnungsgemäßem Programmablauf – wird die Zahl 0 zurückge-geben. Andere Zahlen könnten verwendet werden, um über dasBetriebssystem einem nachfolgenden Programm einen Fehler zusignalisieren.

int summe;

int summand1;

int summand2;

Deklaration von Objekten: Mitteilung an den Compiler, der ent-sprechend Speicherplatz bereitstellt und ab jetzt die Namen summe,summand1 und summand2 innerhalb des Blocks { } kennt. Es gibtverschiedene Zahlentypen in C++. Mit int sind ganze Zahlen ge-meint: summe, summand1, summand2 sind ganze Zahlen.

; Ein Semikolon beendet jede Deklaration und jede Anweisung (aberkeine Verbundanweisung, siehe später).

cout Ausgabe: cout (Abkürzung für character out oder console out) istdie Standardausgabe. Der Doppelpfeil deutet an, dass alles, wasrechts davon steht, zur Ausgabe cout gesendet wird, zum Beispielcout << summand1;. Wenn mehrere Dinge ausgegeben werden sol-len, sind sie durch << zu trennen.

cin Eingabe: Der Doppelpfeil zeigt hier in Richtung des Objekts, das javon der Tastatur einen neuen Wert aufnehmen soll. Die Informa-tion fließt von der Eingabe cin zum Objekt summand1 beziehungs-weise zum Objekt summand2.

= Zuweisung: Der Variablen auf der linken Seite des Gleichheits-zeichens wird das Ergebnis des Ausdrucks auf der rechten Seitezugewiesen.

"Text" beliebige Zeichenkette, die die Anführungszeichen selbst nichtenthalten darf, weil sie als Anfangs- beziehungsweise Endemar-kierung einer Zeichenfolge dienen. Wenn die Zeichenfolge die An-führungszeichen enthalten soll, sind diese als \" zu schreiben: cout<< "\"C++\" ist der Nachfolger von \"C\"!"; erzeugt die Bild-schirmausgabe "C++" ist der Nachfolger von "C"!.

’\n’ die Ausgabe des Zeichens \n bewirkt eine neue Zeile.

return 0; Unser Programm läuft einwandfrei; es gibt daher 0 zurück. DieseAnweisung darf im main()-Programm fehlen, dann wird automa-tisch 0 zurückgegeben.

Page 39: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

38 1 Es geht los!

<iostream> ist ein Header. Dieser aus dem Englischen stammende Begriff (head = dt. Kopf)drückt aus, dass Zeilen dieser Art am Anfang eines Programmtextes stehen. Der Begriffwird im Folgenden verwendet, weil es zurzeit keine gängige deutsche Entsprechung gibt.Einen Header mit einem Dateinamen gleichzusetzen, ist meistens richtig, nach dem C++-Standard aber nicht zwingend.summand1, summand2 und summe sind veränderliche Daten und heißen Variablen. Sie sindObjekte eines vordefinierten Grunddatentyps für ganze Zahlen (int), mit denen die üb-lichen Ganzzahloperationen wie +, - und = durchgeführt werden können. Der Begriff»Variable« wird für ein veränderliches Objekt gebraucht. Für Variablen gilt:

Objekte müssen deklariert werden. int summe; ist eine Deklaration, wobei int derDatentyp des Objekts summe ist, der die Eigenschaften beschreibt. Entsprechendes giltfür summand1 und summand2. Die Objektnamen sind frei wählbar im Rahmen der untenangegebenen Konventionen. Unter Deklaration wird verstanden, dass der Name demCompiler bekannt gemacht wird. Wenn dieser Name später im Programm versehent-lich falsch geschrieben wird, z. B. sume = summand1 + summand2; im Programm aufSeite 36, kennt der Compiler den falschen Namen sume nicht und gibt eine Fehler-meldung aus. Damit dienen Deklarationen der Programmsicherheit.Objektnamen bezeichnen Bereiche im Speicher des Computers, deren Inhalte verän-dert werden können. Die Namen sind symbolische Adressen, unter denen der Wertgefunden wird. Über den Namen kann dann auf den aktuellen Wert zugegriffen wer-den (siehe Abbildung 1.1).

1127102

4089

1002

Inhalt

Adresse Name

1012310124101251012610127

summand1

summand2

summe

Abbildung 1.1: Speicherbereiche mit Adressen

Der Speicherplatz wird vom Compiler reserviert. Man spricht dann von der Definition derObjekte. Definition und Deklaration werden unterschieden, weil es auch Deklarationenohne gleichzeitige Definition gibt, doch davon später. Zunächst sind die Deklarationenzugleich Definitionen. Abbildung 1.2 zeigt den Ablauf der Erzeugung eines lauffähi-gen Programms. Ein Programm ist ein Text, von Menschenhand geschrieben (über Pro-grammgeneratoren soll hier nicht gesprochen werden) und dem Rechner unverständlich.Um dieses Programm auszuführen, muss es erst vom Compiler in eine für den Computerverständliche Form übersetzt werden. Der Compiler ist selbst ein Programm, das bereitsin maschinenverständlicher Form vorliegt und speziell für diese Übersetzung zuständigist. Nach Eingabe des Programmtextes mit dem Editor können Sie den Compiler star-ten und anschließend das Programm binden oder linken (eine Erklärung folgt bald) undausführen.

Page 40: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.4 Das erste Programm 39

Problembeschreibung

Quellprogramm editieren

Modellbildung: Datenstrukturen undAlgorithmen entwerfen(später: Klassen, Methoden)

ÜbersetzenObjektprogramm

Fehlerkorrektur,Vervollständigung

Binden derObjektprogrammeausführbares Programm

Ausführen und Testen des Programms

fertiges Programm

Editor

Compiler

Linker

Einfügen von Quell-programm-Modulen

Einfügen vonBibliotheks-Modulen

Lader

Abbildung 1.2: Erzeugung eines lauffähigen Programms

Ein Programmtext wird auch »Quelltext« (englisch source code) genannt. Der Compilererzeugt aus dem Quelltext den Objektcode, der noch nicht ausführbar ist. Hinter deneinfachen Anweisungen cin >> ... und cout << ... verbergen sich eine Reihe von Ak-tivitäten wie die Abfrage der Tastatur und die Ansteuerung des Bildschirms, die nichtspeziell programmiert werden müssen, weil sie schon in vorübersetzter Form in Biblio-theksdateien vorliegen. Die Aufrufe dieser Aktivitäten im Programm müssen mit dendafür vorgesehenen Algorithmen in den Bibliotheksdateien zusammengebunden werden,eine Aufgabe, die der Linker übernimmt, auch Binder genannt. Der Linker bindet IhrenObjektcode mit dem Objektcode der Bibliotheksdateien zusammen und erzeugt darausein ausführbares Programm, das nun gestartet werden kann. Der Aufruf des Programmsbewirkt, dass der Lader, eine Funktion des Betriebssystems, das Programm in den Rech-nerspeicher lädt und startet. Diese Schritte werden stets ausgeführt, auch wenn sie inden Programmentwicklungsumgebungen verborgen ablaufen. Bibliotheksmodule kön-nen auch während der Programmausführung geladen werden (nicht im Bild dargestellt).Weitere Details werden in Abschnitt 2.3 erläutert.

Page 41: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

40 1 Es geht los!

Wie bekomme ich ein Programm zum Laufen?

Der erste Schritt ist das Schreiben mit einem Textsystem, Editor genannt. Der Text darfkeine Sonderzeichen zur Formatierung enthalten, weswegen nicht alle Editoren geeig-net sind. Integrierte Entwicklungsumgebungen (englisch Integrated Development Envi-ronment, IDE) haben einen speziell auf Programmierzwecke zugeschnittenen Editor, derdarüber hinaus auf Tastendruck oder Mausklick die Übersetzung anstößt. Alternativ be-steht die Möglichkeit, Compiler und Linker per Kommandozeile in einer Linux-Shell odereinem Windows-Eingabeaufforderungs-Fenster zu starten. Beispiel für das Programmsumme.cpp und den Open Source C++-Compiler g++[GCC]:

g++ -c -std=c++14 summe.cpp compilieren (summe.o wird erzeugt)g++ -std=c++14 -o summe.exe summe.o linken. Beide Schritte zusammengefasst:g++ -std=c++14 -o summe.exe summe.cpp

Die Option -std=c++14 informiert den Compiler, den C++-Standard 2014 zu verwenden.Hier ist das zwar nicht notwendig, aber bei vielen anderen Beispielen dieses Buchs. DasProgramm wird durch Eintippen von summe.exe gestartet. Dabei wird vorausgesetzt, dassder g++-Compiler im Pfad ist – sonst wird er nicht gefunden. Weitere Einzelheiten zurBedienung von Compiler und Linker finden Sie in Abschnitt A.3 auf Seite 936 oder inden Hilfedateien Ihres C++-Systems. Der folgende Abschnitt 1.5 zeigt Installation undBedienung einer Integrierten Entwicklungsumgebung.

1.4.1 NamenskonventionenFunktions-, Variablen- und andere Namen unterliegen der folgenden Konvention:

Ein Name ist eine Folge von Zeichen, bestehend aus Buchstaben (ohne Umlaute), Zif-fern und Unterstrich (_). Der Name soll kurz sein, aber die Bedeutung widerspiegeln.So ist der Name summe klarer als ein Name s.Ein Name beginnt stets mit einem Buchstaben oder einem Unterstrich _. Am Anfangeines Namens sollen Unterstriche vermieden werden, ebenso Namen, die zwei Unter-striche direkt nacheinander enthalten. Solche Namen werden systemintern benutzt.Selbsterfundene Namen dürfen nicht mit den vordefinierten Schlüsselwörtern über-einstimmen (zum Beispiel for, int, main . . . ). Eine Tabelle der Schlüsselwörter ist imAnhang auf Seite 935 zu finden.Ein Name kann prinzipiell beliebig lang sein. In den Compilern ist die Länge jedochbegrenzt, zum Beispiel auf 31 oder 255 Zeichen.

Die hier aufgelisteten Konventionen zeigen die Regeln für die Struktur eines Namens,auch Syntax oder Grammatik genannt. Ein Name darf niemals ein Leerzeichen enthal-ten! Wenn eine Worttrennung aus Gründen der Lesbarkeit gewünscht ist, helfen derUnterstrich und Wechsel in der Groß- und Kleinschreibung. Beispiele:

int 1_Zeile; falsch! (Ziffer am Anfang)int anzahl der Zeilen; falsch! (Name enthält Leerzeichen)int AnzahlDerZeilen; richtig! andere Möglichkeit:int anzahl_der_Zeilen; richtig!

Zur Abkürzung können Variablen des gleichen Datentyps aufgelistet werden, sofern siedurch Kommas getrennt werden. int a; int b; int c; ist gleichwertig mit int a,b,c;.Lesbarer ist jedoch die Deklaration auf drei getrennten Zeilen.

Page 42: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.5 Integrierte Entwicklungsumgebung 41

1.5 Integrierte Entwicklungs-umgebung

Im Folgenden stelle ich Ihnen kurz die Integrierte Entwicklungsumgebung (abgekürztIDE) Code::Blocks [CB] vor. Sie gibt es für Windows und Linux. Natürlich gibt es nocheinige andere, aber Anfängern empfehle ich Code::Blocks, weil diese IDE am einfachstenzu installieren und zu bedienen ist.Weil hier nur sehr kurz auf die Bedienung eingegangen werden kann, empfehle ich Ihnendie Bedienungsanleitung von Code::Blocks, die Sie auf der Internetseite [CB] finden.Die Installationsanleitung für die Software zu diesem Buch, die auch Code::Blocks ent-hält, finden Sie in Abschnitt A.6 (Seite 948) für Windows. Bevor Sie das erste Programmeingeben, nehmen Sie die Einstellungen gemäß Abschnitt A.6.2 vor. Die Installationsan-leitung für Linux finden Sie in Abschnitt A.7.3 (Seite 952).

Programm eingeben, übersetzen und starten

Um jetzt ein Programm einzugeben, starten Sie Code::Blocks (Windows: Start →AlleProgramme →CodeBlocks). Nach dem Start legen Sie ein neues Projekt an, indem Sieauf »Create a new Project« im Startfenster klicken. Alternativ ist der Weg über den Me-nübalken möglich (File →New →Project). Es wird Ihnen eine Auswahl verschiedensterAnwendungstypen angeboten. Fürs Erste wählen Sie bitte die einfachste Art der Anwen-dung, die »Console Application«. Im dann erscheinenden Fenster müssen noch einigeAngaben eingetragen werden. Vorschlag:Project title: Projekt1Folder to create project in: C:\Users\IhrName\Documents\cpp_projekt

Sie können auch mit dem Button rechts vom Eingabefeld ein vorhandenes Verzeichniswählen. Die anderen Einstellungen werden belassen. Nun »Next« und »Finish« anklicken.Im linken Teil klicken Sie bitte auf Sources und dort auf main.cpp. Bitte ändern Sie dasangezeigte Programm so, dass Sie damit die Summe zweier Zahlen berechnen können(Programm von Seite 36). Um einen Fehler zu provozieren und seine Reparatur zu zeigen,wird noch eine nichtexistierende Variable x addiert. Ein Klick auf das Diskettensymboloben sichert die Datei.Ein Klick auf das Build-Icon oder Drücken der Tastenkombination Strg-F9 startet denÜbersetzungsprozess. Der mit Absicht erzeugte Fehler wird nun im Programm mit einemroten Balken markiert; Erklärungen dazu finden sich im Fenster unter dem Programm-code (siehe Abbildung 1.3). Es empfiehlt sich, die Hinweise auf Fehler genau zu lesen!Jetzt korrigieren Sie bitte das Programm, indem die fehlerhafte Addition der Variablenx entfernt wird. Ein weiterer Klick auf das Build-Icon wird nun von Erfolg gekrönt: 0errors, 0 warnings! Ein Klick auf das Run-Icon (oder die Tastenkombination Strg-F10)führt zur Ausführung des Programms. Im erscheinenden Fenster geben Sie einfach zweiZahlen ein und drücken ENTER . Das Ergebnis wird dann angezeigt. Mit einem weiterenTastendruck wird das Fenster geschlossen. Man kann eine weitere Pause erzwingen, fallsdas Fenster sich vorher zu schnell schließt. Dazu werden am Programmende die Zeilen

Page 43: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

42 1 Es geht los!

Fehler-markierung

Erläuterungdes Fehlers

Abbildung 1.3: Die Entwicklungsumgebung Code::Blocks zeigt einen Fehler an.

cin.ignore(1000, ’\n’); // genaue Erklärung folgt in Kap. 9cin.get();

hinzugefügt, wie in der Datei cppbuch/k1/summe.cpp gezeigt. Die Datei ist in den her-unterladbaren Beispielen enthalten [SW].

TippDas Programm kann direkt aus einem bestehenden Fenster, das dann weiter bestehenbleibt, gestartet werden, hier gezeigt für das Betriebssystem Windows: Dazu rufen Siedie Eingabeaufforderung auf (am besten legen Sie sich dazu vorher eine Verknüpfungauf dem Desktop an, Programm C:\Windows\System32\cmd.exe). Durch Anwendungdes Befehls cd gehen Sie in das Verzeichnis, in dem das Programm abgelegt wordenist, zum Beispiel cpp_projekt/Projekt1/bin/Debug. In diesem Verzeichnis befindet sich dievom Compiler erzeugte ausführbare Datei Projekt1.exe, wie der Befehl dir zeigt. WennSie nun Projekt1.exe eintippen, wird das Programm ausgeführt.

Page 44: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 43

1.6 Einfache Datentypen undOperatoren

Sie haben schon flüchtig den Datentyp int für ganze Zahlen kennengelernt. Es gibtdarüber hinaus noch eine Menge anderer Datentypen. Hier wird näher auf die Grundda-tentypen eingegangen. Sie sind definiert durch ihren Wertebereich sowie die mit diesenWerten möglichen Operationen. Nicht-veränderliche Daten sind für alle Grunddatenty-pen möglich; sie werden in Abschnitt 1.6.4 erläutert.

1.6.1 AusdruckEin Ausdruck besteht aus einem oder mehreren Operanden, die miteinander durch Ope-ratoren verknüpft sind. Die Auswertung eines Ausdrucks resultiert in einem Wert, der andie Stelle des Ausdrucks tritt. Der einfachste Ausdruck besteht aus einer einzigen Kon-stante, Variable oder einem Literal2. Die Operatoren müssen zu den Operanden passen,die zu bestimmte Datentypen gehören. Beispiele: 17, 1+ 17, a = 1 + 17, »Textliteral«.a = 1 + 17 ist ein zusammengesetzter Ausdruck. Die Operanden 1 und 17 sind Zahl-Literale, die hier ganze Zahlen repräsentieren und die durch den +-Operator verknüpftwerden. Der resultierende Wert 18 wird dem Objekt a zugewiesen. Der Wert des gesamtenAusdrucks ist der resultierende Wert von a.

1.6.2 Ganze ZahlenEs gibt verschiedene rechnerinterne Darstellungen von ganzen Zahlen, die sich durch diebereitgestellte Anzahl von Bits pro Zahl unterscheiden. Die verschiedenen Darstellungenwerden durch die Datentypen short, int und long repräsentiert, wobei gilt Bits(short)≤ Bits(int) ≤ Bits(long). Die tatsächlich verwendete Anzahl von Bits variiert je nachRechnersystem. Typische Werte sind:

short (oder short int) 16 Bitsint 32 Bits (oder 16 Bits)long (oder long int) 64 Bits (oder 32 Bits)long long (oder long long int) mindestens 64 Bits

Die eingeklammerten Zahlen sind entsprechend dem C++-Standard mindestens erforder-lich. Ein Bit wird für das Vorzeichen reserviert.

HinweisFür den ersten Einstieg können Sie den bitweisen Aufbau der Zahlen sowie die Bitope-rationen überspringen. Kehren Sie bei Bedarf zu diesem Abschnitt zurück. Der bitweiseAufbau der Zahlen und die Bitoperationen sind keine C++-Spezialität, sondern gelten fürviele Programmiersprachen. Die Zahlenbereiche sollen Sie allerdings kennen, weil davondie Korrektheit und die Genauigkeit von Programmen abhängen können.

2 Siehe Glossar Seite 959

Page 45: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

44 1 Es geht los!

In den folgenden ausgewählten Bitkombinationen für 16-Bit-int-Zahlen repräsentiertdas links stehende Bit das Vorzeichen:

binär dezimal0111 1111 1111 1111 327670000 0000 0000 0000 01111 1111 1111 1111 -11111 1111 1111 1110 -21000 0000 0000 0000 -32768

Negative Zahlen werden üblicherweise im Zweierkomplement dargestellt3. Das Zweier-komplement wird gebildet, indem alle Bits invertiert werden und dann auf das Ergebnis1 addiert wird. Durch das Schlüsselwort unsigned werden Zahlen ohne Vorzeichen defi-niert, zum Beispiel unsigned int, unsigned long (Langform: unsigned long int). Durchdas damit gewonnene zusätzliche Bit teilt sich der Zahlenbereich anders auf:

signed 16 Bits –215 . . . 215–1 = –32.768 . . . 32.767unsigned 16 Bits 0 . . . 216–1 = 0 . . . 65.535signed 32 Bits –231 . . . 231–1 = –2.147.483.648 . . . 2.147.483.647unsigned 32 Bits 0 . . . 232–1 = 0 . . . 4.294.967.295signed 64 Bits –263 . . . 263–1 = –9.223.372.036.854.775.808

. . . 9.223.372.036.854.775.807unsigned 64 Bits 0 . . . 264–1 = 0 . . . 18.446.744.073.709.551.615

Die in Ihrem C++-System zutreffenden Zahlenbereiche finden Sie im Header <limits>.Das folgende Programm gibt die Grenzwerte und den benötigten Speicherplatz für dieGanzzahl-Typen aus.

Listing 1.2: Grenzwerte und Speicherplatzbedarf

// cppbuch/k1/intlimits.cpp#include <iostream>#include<limits> // hier sind die Bereichsinformationenusing namespace std;

int main() {cout << "Grenzwerte für Ganzzahl-Typen:" << ’\n’; // = neue Zeilecout << "int Minimum = " << numeric_limits<int>::min() << ’\n’;cout << "int Maximum = " << numeric_limits<int>::max() << ’\n’;cout << "long Minimum = " << numeric_limits<long>::min() << ’\n’;cout << "long Maximum = " << numeric_limits<long>::max() << ’\n’;cout << "long long Minimum = " << numeric_limits<long long>::min() << ’\n’;cout << "long long Maximum = " << numeric_limits<long long>::max() << ’\n’;cout << "unsigned-Maxima (Minimum ist 0):\n"; // \n ebenfalls neue Zeilecout << "unsigned int = " << numeric_limits<unsigned int>::max() << ’\n’;cout << "unsigned long = " << numeric_limits<unsigned long>::max() << ’\n’;cout << "unsigned long long = " << numeric_limits<unsigned long long>::max();cout << "\nAnzahl der Bytes für:\n";cout << "int " << sizeof(int) << ’\n’;cout << "long " << sizeof(long) << ’\n’;cout << "long long " << sizeof(long long) << ’\n’;

}

3 Es gibt andere Möglichkeiten, die aber praktisch keine Bedeutung haben.

Page 46: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 45

C++14

C++14

Im Programm cppbuch/k1/climits.cpp (hier nicht abgedruckt) finden Sie die Grenzwertebasierend auf der C-Programmiersprache. Ganze Zahlen können auf verschiedene Artendargestellt werden:1. Die übliche Darstellung ist die als Dezimalzahl. Eine Dezimalzahl, die ungleich 0 ist,

beginnt mit einer der Ziffern im Bereich 1 bis 9. Ein Suffix (l, ll, L, LL) kennzeichnetlong- oder long long-Zahlen, etwa 2147483647L oder 9223372036854775806LL. Einmögliches Vorzeichen gehört nicht zu der Zahl, obwohl es in einem Programm oderbeim Einlesen mit cin berücksichtigt wird.

2. Ein Suffix u oder U kennzeichnet unsigned-Zahlen, zum Beispiel 1836u. Kombina-tionen wie in 2036854775806ul sind möglich.

3. Wenn eine Zahl mit 0b oder 0B beginnt, wird sie als Binärzahl interpretiert, etwa0b1011 = 1*23 + 0*22 + 1*21 + 1*20 = 11 dezimal.

4. Wenn eine Zahl mit einer 0 beginnt, wird sie als Oktalzahl interpretiert, zum Beispiel0377 = 3778 = 3*82 + 7*81 + 7*80 = 25510 (dezimal).

5. Wenn eine Zahl mit 0x oder 0X beginnt, wird sie als Hexadezimalzahl interpretiert,zum Beispiel 0xAFFE (= 45054 dezimal).

6. Bei allen Zahlen ist es zur besseren Lesbarkeit erlaubt, zur Gruppierung Hochkom-mata einzufügen, wie etwa in 1’000’000 statt 1000000 oder 0xabc’def statt 0xabcdef.Bei der Auswertung der Zahl durch den Compiler werden die Hochkommata ignoriert.Mehr zeigt das Programm cppbuch/k1/zahltypen.cpp (hier nicht abgedruckt).

Beim Rechnen mit ganzen Zahlen ist der begrenzte Wertebereich zu beachten! Aufgrundder Tatsache, dass nur eine begrenzte Anzahl von Bits für die rechnerinterne Repräsen-tation einer Zahl zur Verfügung steht, ergibt sich, dass der von int abgedeckte Zahlen-bereich nur eine Untermenge der ganzen Zahlen darstellt, wie das folgende Programm-beispiel zeigt. Im Programm wird unter Initialisierung die Belegung einer Variable miteinem Anfangswert verstanden.

Listing 1.3: Arithmetischer Überlauf

// cppbuch/k1/overflow.cpp#include <limits>#include <iostream>using namespace std;

int main() {int ai { 50000}; // Initialisierung mit 50000int bi { 1’000’000}; // Initialisierung mit 1000000, mit Gruppierung der Tausenderint ci { ai*bi}; // Initialisierung mit dem Produktcout << "int-Zahlen haben auf Ihrem System " << 8*sizeof(int) << " Bits\n";cout << "Rechnung mit int: ";cout << ai << " * " << bi << " = " << ci << ’\n’;// Ausgabe -1539607552 statt 50000000000 bei 32 Bit-intlong al { 50000L}; // L (oder l): Kennzeichnung als longlong bl { 1000000L};long cl { al*bl};cout << "long-Zahlen haben auf Ihrem System " << 8*sizeof(long) << " Bits\n";cout << "Rechnung mit long: ";cout << al << " * " << bl << " = " << cl << ’\n’;cout << "long-Overflow produzieren:\n";

Page 47: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

46 1 Es geht los!

al = numeric_limits<long>::max() / 2 + 1000;cout << "2 * " << al << " = " << (2*al) << " ?\n";

}

Aus dem Programmergebnis folgt, dass die Regeln der Mathematik in der Nähe der Gren-zen des Zahlenintervalls nur noch eingeschränkt gelten. Das Ergebnis einer Folge arith-metischer Operationen ist nur dann korrekt, wenn kein Zwischenergebnis den durchden Datentyp vorgegebenen maximalen Zahlenbereich überschreitet. 50000000000 liegtaußerhalb des durch 32 Bits darstellbaren Zahlenbereichs. Wird das Resultat einer Ope-ration betragsmäßig zu groß, liegt ein Überlauf (englisch overflow) vor, der durch denComputer nicht gemeldet wird. Der Ersatz von int durch long führt im obigen Beispielnur dann zu einem Programm, das das korrekte Ergebnis ausgibt, falls long mehr Bitsals int hat. Dies ist oft nicht der Fall. Das Problem ist nicht grundsätzlich lösbar; eswird bei Ganzzahl-Datentypen mit mehr Bits pro Zahl nur in Richtung größerer Zahlenverschoben.Beim Schreiben eines Programms müssen Sie also Gedanken über die möglichen vorkom-menden Zahlenwerte machen. »Sicherheitshalber« immer den größtmöglichen Datentypzu wählen, ist nicht sinnvoll, weil Variablen dieses Typs mehr Speicherplatz benötigenund weil Rechenoperationen mit ihnen länger dauern.

Operatoren für ganze Zahlen

Die Tabelle 1.2 zeigt die für ganze Zahlen möglichen Operatoren. Die zusammengesetz-ten Operatoren wie += heißen Kurzform-Operatoren. Auf Daten eines bestimmten Typskönnen nur bestimmte Operationen durchgeführt werden. Eine Zeichenkette kann manzum Beispiel nicht durch eine andere dividieren, sehr wohl jedoch eine ganze Zahl durcheine andere 6= 0. Daraus folgt: Ein Datum und die zugehörigen Operationen gehören zu-sammen! Einige der Operatoren der Tabelle 1.2 werden durch Beispiele erläutert. MancheOperatoren setzen jedoch hier noch nicht besprochene Dinge voraus, weshalb gelegent-lich auf spätere Abschnitte verwiesen wird. Beispiel für die Anwendung des ++-Operators:

int a; // undefinierter Anfangswertint i {5}; // Anfangswert 5a = ++i; // vorangestelltes ++

i wird erst um eins inkrementiert, dann benutzt. Unter Inkrementierung wird die Additionvon 1 verstanden, unter Dekrementierung die Subtraktion von 1. ++ ist der Inkremen-tierungsoperator, der je nach Stellung eine Variable vor oder nach Benutzung des Wertshochzählt. Nach dieser Anweisung haben sowohl a als auch i den Wert 6.

int j = 2; // Initialisierung mit = ist auch möglichint b = j++; // nachgestelltes ++

j wird erst benutzt, dann inkrementiert. Nach dieser Anweisung hat b den Wert 2 und j

den Wert 3.

j = j + 4;j += 5;

Hier wird 4 zu j addiert. Ergebnis: j = 7. Anschließend wird 5 mit dem Kurzformoperatorhinzugefügt. j hat danach den Wert 12.

Page 48: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 47

Tabelle 1.2: Operatoren für GanzzahlenOperator Beispiel Bedeutung

Arithmetische Operatoren:

+ +i unäres Plus (kann weggelassen werden)- -i unäres Minus++ ++i vorherige Inkrementierung um eins

i++ nachfolgende Inkrementierung um eins-- --i vorherige Dekrementierung um eins

i-- nachfolgende Dekrementierung um eins+ i + 2 binäres Plus- i - 5 binäres Minus

* 5 * i Multiplikation/ i / 6 Division% i % 4 Modulo (Rest mit Vorzeichen von i)= i = 3 + j Zuweisung

*= i *= 3 i = i * 3/= i /= 3 i = i / 3%= i %= 3 i = i % 3+= i += 3 i = i + 3-= i -= 3 i = i - 3

relationale Operatoren:

< i < j kleiner als> i > j größer als<= i <= j kleiner gleich>= i >= j größer gleich== i == j gleich!= i != j ungleich

Bit-Operatoren:

<< i << 2 Linksschieben (Multiplikation mit 2er-Potenzen)>> i >> 1 Rechtsschieben (Division durch 2er-Potenzen)& i & 7 bitweises UND^ i ^ 7 bitweises XOR (Exklusives Oder)| i | 7 bitweises ODER~ ~i bitweise Negation<<= i <<= 3 i = i << 3>>= i >>= 3 i = i >> 3&= i &= 3 i = i & 3|= i |= 3 i = i | 3

Ganzzahlige Division

Wenn bei der Division nur ganze Zahlen beteiligt sind, ist das Ergebnis auch ganzzahlig!Der Rest wird verworfen und bei Bedarf mit dem Modulo-Operator % ermittelt:

int m {9};int n {5};int ergebnis { m / n}; // 1int rest {m % n}; // 4

Page 49: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

48 1 Es geht los!

Initialisierung oder Zuweisung?

Eine Initialisierung weist einem Objekt beim Anlegen des Objekts einen Anfangswert zu.Eine Zuweisung bezieht sich immer auf ein bereits existierendes Objekt. Einige Beispielefür beide Fälle:

int a {}; // Initialisierung mit 0int b {0}; // Initialisierung mit 0int c = 1; // Initialisierung mit 1 (Variante mit = Operator)int d; // nicht initialisiertd = 3; // Zuweisung (d existiert bereits)

Bit-Operatoren

Weil ganze Zahlen als Bitvektoren aufgefasst werden können, sind zusätzlich Bit-Opera-tionen möglich. Es folgen Beispiele für zwei int-Zahlen c und k. Die Zahl k soll den Wert5 repräsentieren. Die binäre Darstellung der Zahl 5 ist für 16-Bit-int-Zahlen 0000 0000

0000 0101. Die Anweisung c = k << 2; bewirkt eine Bitverschiebung um 2 Stellen nachlinks (= Multiplikation mit 22, also 4) und Zuweisung des Ergebnisses an c.

0000 0000 0000 0101 k ist gleich 50000 0000 0001 0100 c= k << 2, d.h. 2 Stellen verschoben. c hat den Wert 20.

Bei der Verschiebung nach links werden von rechts Nullen nachgezogen. Wenn nachrechts verschoben wird, werden links Nullen eingefügt, falls der Operand vom Typ un-

signed ist. Bei vorzeichenbehafteten Typen wird links entweder das Vorzeichenbit kopiertoder es werden Nullbits eingefügt – je nach C++-System. Die Anweisung c = c & k;

bewirkt die bitweise UND-Verknüpfung:0000 0000 0000 0101 k ist gleich 50000 0000 0001 0100 c ist gleich 200000 0000 0000 0100 Das Ergebnis von c & k ist 4.

Die Anweisung c = ~k; bewirkt die bitweise Negation:0000 0000 0000 0101 k ist gleich 51111 1111 1111 1010 c = ~k (also –6)

Die Addition von 1 auf c ergibt das Zweierkomplement, also die Darstellung der negati-ven Zahl -5.

size_t

Der Datentyp size_t ist ein vorzeichenloser Ganzzahl-Typ für Größenangaben, die nichtnegativ werden können, wie zum Beispiel die Anzahl der Einträge in einer Tabelle. Erist groß genug, die Größe eines beliebigen Objekts in Bytes abzubilden. Die Definitionsteht im Header <cstddef>, der von vielen Standard-Headern bereits eingeschlossen wird.Auf 32-Bit-Systemen gibt es oft keinen Unterschied zu unsigned int, aber auf 64-Bit-Systemen kann sizeof(unsigned int) den Wert 4 ergeben und sizeof(size_t) den Wert8. Deswegen ist es im Allgemeinen besser, size_t statt unsigned int zu verwenden, wennes um Größeninformationen oder Adressen im Speicher geht. Generell schadet es nicht,wenn Sie size_t immer dann verwenden, wenn es um nicht-negative ganze Zahlen geht.

Page 50: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 49

Weitere Typen für ganze Zahlen4

Im Header <cstdint> gibt es weitere Typen für ganze Zahlen. Sie sind weniger gebräuch-lich und werden in diesem Buch daher nicht weiter benutzt. Sie sollen nur wissen, dasses sie gibt, und im Zweifel die Bedeutung nachschlagen können.

int_fast8_t int_fast16_t int_fast32_t int_fast64_t

uint_fast8_t uint_fast16_t uint_fast32_t uint_fast64_t

Jeder dieser Typen ist normalerweise der schnellste für int-Operationen. Die Zahl gibtan, wieviele Bits mindestens für den Typ zur Verfügung stehen. So ist uint_fast32_tein unsigned int-Typ mit mindestens 32 Bits.int_least8_t int_least16_t int_least32_t int_least64_t

uint_least8_t uint_least16_t uint_least32_t uint_least64_t

Jeder dieser Typen hat mindestens die angegebene Bitbreite.intmax_t uintmax_t

Diese Typen sind int bzw. unsigned int-Typen mit der maximal zur Verfügung ge-stellten Bitbreite. Sie kann größer sein als die von size_t.

Die oben genannten Typen müssen laut [ISOC++] vorhanden sein. Es kann jedoch optio-nal weitere Typen geben:

int8_t int16_t int32_t int64_t

uint8_t uint16_t uint32_t uint64_t

Jeder dieser Typen hat exakt die angegebene Bitbreite.intptr_t uintptr_t

Jeder gültige Zeiger auf void kann in einen dieser Typen umgewandelt werden. DieRückumwandlung ergibt wieder den ursprünglichen Zeiger.

Mit Hilfe des ggf. zu modifizierenden Programms cppbuch/k1/inttypen.cpp können Siespäter (sobald Sie wissen, was static_assert bedeutet) prüfen, welche Typen auf IhremSystem vorhanden sind.

1.6.3 Reelle ZahlenReelle Zahlen, auch Gleitkommazahlen genannt, sind wegen der beschränkten Bit-Anzahlin der Regel nicht beliebig genau darstellbar. Sie werden in C++ wie folgt geschrieben:Vorzeichen (optional), Vorkommastellen, Dezimalpunkt, Nachkommastellen, e oder E undGanzzahl-Exponent (optional), Suffix f, F oder l, L (optional, Zahlen ohne Suffix sinddouble). Es können wegfallen:

entweder Vorkommastellen oder Nachkommastellen (aber nicht beide), oderentweder Dezimalpunkt oder e (oder E) mit Exponent (aber nicht beide).

Einige Beispiele für Gleitkommazahlen sind:-123.789e6f 1.8E6 88.009 1e-03 1.8L

Der Exponent meint Zehnerpotenzen. 1.8e6 ist dasselbe wie 1.8 · 106 oder 1800000.Reelle Zahlen werden durch die drei Datentypen der Tabelle 1.3 (ungenau) dargestellt.

4 Dieser Abschnitt (bis »Reelle Zahlen«) kann wegen des Bezugs auf nachfolgende Kapitel beim ersten Lesenübersprungen werden.

Page 51: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

50 1 Es geht los!

Tabelle 1.3: Datentypen und Bereiche für reelle ZahlenTyp Bits Zahlenbereich Genauigkeit in Dezimalstellen

float 32 ± 1.2*10–38 . . . ± 3.4*1038 ca. 6double 64 ± 2.2*10–308 . . . ±1.8*10308 ca. 15long double 80 ± 3.4*10–4932 . . . ±1.2*104932 ca. 18

An die Stelle des Kommas tritt ein Dezimalpunkt, wie im angelsächsischen Sprachraumüblich. Die Festlegungen der Minimal- bzw. Maximal-Werte Ihres C++-Systems sind imHeader <limits> zu finden. Die numeric_limits<float>- bzw. numeric_limits<double>-Funktionen können Auskunft geben, wie das folgende Programm zeigt:

Listing 1.4: Grenzwerte von float-Zahlen

// Auszug aus cppbuch/k1/floatlimits.cpp#include<iostream>#include<limits> // hier sind die Bereichsinformationenusing namespace std;

int main() {cout << "Grenzwerte für Float-Zahl-Typen:\n";cout << "Float-Min: " << numeric_limits<float>::min() << ’\n’;cout << "Float-Max: " << numeric_limits<float>::max() << ’\n’;cout << "Double-Min: " << numeric_limits<double>::min() << ’\n’;cout << "Double-Max: " << numeric_limits<double>::max() << ’\n’;cout << "Long-Double-Min: " << numeric_limits<long double>::min() << ’\n’;cout << "Long-Double-Max: " << numeric_limits<long double>::max() << ’\n’;cout << "float- und double-Zahlen entsprechen";if(!numeric_limits<float>::is_iec559) {

cout << " nicht";}cout << " dem IEC 559 (=IEEE 754)-Standard.\n";

}

Intern werden Mantisse und Exponent jeweils durch Binärzahlen einer bestimmten Bit-breite verkörpert. Die hier angegebenen für Mantisse und Exponent aufsummierten Bit-breiten und damit Zahlenbereiche und Rechengenauigkeiten sind implementationsabhän-gig und dienen als Beispiel. Meistens entsprechen die Zahlen dem IEC559 (=IEEE 754)-Standard. Ob das auf Ihrem System so ist, können Sie mithilfe des obigen Programmsherausfinden. Die Tabelle 1.4 zeigt ein Beispiel der Repräsentation reeller Zahlen imRechner.

Tabelle 1.4: Anzahl der Bits (Beispiel)

float double long double

Vorzeichen 1 1 1Mantisse 23 52 64Exp-Vorzeichen 1 1 1Exponent 7 10 14

Gesamtanzahl Bits 32 64 80

Page 52: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 51

In C++ ist festgelegt, dass die Genauigkeit von double-Zahlen nicht schlechter sein darfals die von float-Zahlen, und die von long double-Zahlen5 darf nicht schlechter seinals die von double-Zahlen. Die Genauigkeit hängt von der Anzahl der Bits ab, die fürdie Mantisse verwendet werden. Es sei angenommen, dass für den Typ double die obenangegebenen Bitzahlen gelten. Da 252 ca. 4.5 · 1015 ist, ergibt sich eine etwa 15- bis 16-stellige Genauigkeit. Sie können sich die Genauigkeit für die verschiedenen Datentypendirekt ausgeben lassen:

// cppbuch/k1/genauigkeiten.cpp#include <limits>#include <iostream>using namespace std;

int main() {cout << "Die Genauigkeit von float beträgt etwa "

<< numeric_limits<float>::digits10 << " Dezimalstellen\n";cout << "Die Genauigkeit von double beträgt etwa "

<< numeric_limits<double>::digits10 << " Dezimalstellen\n";cout << "Die Genauigkeit von long double beträgt etwa "

<< numeric_limits<long double>::digits10 << " Dezimalstellen\n";}

Der Zahlenbereich wird wesentlich durch die Anzahl der Bits für den Exponenten be-stimmt, der Einfluss der Mantisse ist minimal: 210 ist 1024. Der Exponent kann damitim Bereich von 0 bis 210 – 1 (d.h. 0 bis 1023) liegen. Übertragen auf das Dezimalsystemergibt sich mithilfe der Schulmathematik ein maximaler Exponent von 1023 · log 2/ log10, also etwa 308. Der darstellbare Bereich geht also bis ca. 10308. Insgesamt ergibt sich,dass eine beliebige Genauigkeit nicht für alle Zahlen möglich ist. Falls 32 Bits für dieDarstellung einer reellen Zahl verwendet werden, existieren nur 232 = 4 294 967 296verschiedene Möglichkeiten, eine Zahl zu bilden.Mit dem mathematischen Begriff eines reellen Zahlenkontinuums hat das nur näherungs-weise zu tun, und alle Illusionen von der computertypischen Genauigkeit und Korrektheitsind dahin, wie das folgende Programm demonstriert:

Listing 1.5: Genauigkeit von float-Zahlen

// cppbuch/k1/ungenau.cpp#include<iostream>using namespace std;int main() {

float a { 1.234567E-7f};float b { 1.000000f};float c { -b};float s1 {a + b};s1 += c; // entspricht s1 = s1 + c;float s2 {a};s2 += b + c;cout << "Ungenauigkeit bei float-Arithmetik:\n";

5 sizeof(long double) kann größer als 10 (= 80 Bits) sein, etwa 12 oder 16, um ein Vielfaches der Längeeines Rechnerworts (32 oder 64 Bits) zu erreichen. Interne Operationen sind einfacher zu gestalten, wennDatentypen auf Wortgrenzen enden. Wenn nur mit 80 Bits gerechnet wird, werden also Bits »verschenkt«.

Page 53: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

52 1 Es geht los!

cout << "(a+b)+c= " << s1 << ’\n’; // 1.19209e-7cout << "a+(b+c)= " << s2 << ’\n’; // 1.23457e-7

}

Die Ausgabe von ’\n’ im Beispielprogramm bedeutet, dass nach den Zahlen s1 und s2

jeweils eine neue Zeile auf dem Bildschirm begonnen wird. Folgen der nicht exaktenDarstellung können sein:

Bei der Subtraktion zweier fast gleich großer Werte heben sich die signifikantenZiffern auf. Die Differenz wird damit ungenau. Dieser Effekt ist unter dem Namennumerische Auslöschung bekannt.Die Division durch betragsmäßig zu kleine Werte ergibt einen Überlauf (englischoverflow).Eine Unterschreitung (englisch underflow) tritt auf, wenn der Betrag des Ergebnisseszu klein ist, um mit dem gegebenen Datentyp darstellbar zu sein. Das Resultat wirddann gleich 0 gesetzt.Ergebnisse können von der Reihenfolge der Berechnungen abhängen. Im Programm-beispiel wird a+b+c auf zwei verschiedene Arten berechnet: Um s1 zu berechnen,werden erst a und b addiert und danach c, während zur Berechnung von s2 zu a dieSumme von b und c addiert wird. Rein mathematisch betrachtet müsste das gleicheErgebnis herauskommen, die Computerarithmetik liefert jedoch abweichende Ergeb-nisse. Daher gehört zu kritischen Rechnungen immer eine Genauigkeitsbetrachtung.

Es gibt verschiedene Methoden, den Fehler bei ungenauen Rechnungen zu minimieren.Bei der Addition einer großen Menge verschiedener Zahlen werden zunächst die Zahlensortiert. Erst danach folgen die Additionen, beginnend mit der kleinsten Zahl. Für die»reellen« Zahlentypen float, double und long double stehen mit Ausnahme des Modulo-Operators % alle arithmetischen und relationalen Operatoren der Tabelle 1.2 zur Verfü-gung. Bei zu großer Ungenauigkeit von float-Rechnungen könnte man also double-Zahlen nehmen, die etwas mehr Speicher kosten. Mit diesen wird das Problem nichtgrundsätzlich gelöst, die Genauigkeit ist jedoch größer. Tabelle 1.5 zeigt einige Beispielezur Umsetzung mathematischer Ausdrücke in die Programmiersprache C++.

Tabelle 1.5: Umsetzen mathematischer Ausdrücke

Mathematische C++-Schreibweise Notation

a –√1 + x2 a - sqrt(1+x*x)

a–b1+ x

y(a-b)/(1+x/y)

| x | abs(x) intlabs(x) long intfabs(x) float, double, long double

xy

z x/y/z klarer: (x/y)/z

e–δt sin(ωt + ϕ) exp(-delta*t)*sin(omega*t+phi)

Page 54: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 53

Einige mathematische Funktionen wie sqrt(), exp() u.a. sind vordefiniert. Die Deklara-tion der meisten dieser Funktionen befindet sich im Header <cmath>. Aus historischenGründen befindet sich die Funktion abs() für int-Zahlen jedoch im Header <cstdlib>.Zum Nachschlagen der verschiedenen Möglichkeiten bieten sich die Tabellen 34.3 und34.5 an (ab Seite 926). Um dem Compiler die Deklarationen bekannt zu machen, genügtes, im Programm am Anfang der Programmdatei, die Zeile #include<cmath> einzufügen,wie unten im Programm gezeigt.

Listing 1.6: Mathematische Funktionen

// cppbuch/k1/mathexpr.cpp : Berechnung mathematischer Ausdrücke#include<iostream>#include<cmath>using namespace std;

int main() {float x;cout << "x eingeben:";cin >> x;cout << " x = " << x << ’\n’;cout << " fabs(x) = " << fabs(x) << ’\n’;cout << " sqrt(x) = " << sqrt(x) << ’\n’;cout << " sin(x) = " << sin(x) << ’\n’; // Argument von sin() im Bogenmaß!cout << " exp(x) = " << exp(x) << ’\n’;cout << " log(x) = " << log(x) << ’\n’; // log() ist der natürliche Logarithmus

}

Übung1.1 Probieren Sie das vorstehende Programm aus! Wie verhält sich Ihr Rechner bei Ein-gabe von 0 oder einer negativen Zahl? (Dies ist eine von etwa sechs Übungsaufgaben,die mathematisch mehr als die Kenntnis der vier Grundrechenarten voraussetzen – einAnteil von etwa 7 % bei 91 Aufgaben insgesamt.) Die Lösungen zu den Aufgaben findenSie im Anhang.

1.6.4 KonstanteIn einem Programm kommen häufig Zahlen oder andere Datenstrukturen vor, die im Pro-grammlauf nicht verändert werden dürfen. Sie heißen Konstanten. Zum Beispiel könnteman »umfang = 3.1415926 * durchmesser;« schreiben. Besser ist

const float PI = 3.1415926; // noch besser: Konstante pi<float> nehmen, siehe Seite 174umfang = PI * durchmesser;

weil bei anschließendem häufigerem Gebrauch der Zahl PI Schreibfehler leicht ausge-schlossen werden können, und Änderungen oder Korrekturen einer Konstante nur aneiner Stelle vorgenommen werden müssen (siehe Stichwort »magic number« im Glossar).PI ist nur einmal als Konstante zu vereinbaren (= zu deklarieren). float bedeutet, dassdie Konstante eine Gleitkommazahl ist.

Eine Konstante besteht aus einem Namen und dem zugeordneten Wert, der nichtveränderbar ist. Manche schreiben alles groß, andere stellen ein k davor (kPi).

Page 55: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

54 1 Es geht los!

Eines der Schlüsselwörter const oder constexpr leitet die Deklaration einer Konstan-ten ein. Arithmetik ist erlaubt, z.B. const int MAX = GROESSE-1;

Eine Zahl auf der rechten Seite zur Initialisierung einer Konstanten heißt »Zahlliteral«.Die Auswertung geschieht nicht erst, wenn das Programm läuft, sondern schon durch denCompiler. Ein Literal für eine ganze Zahl besteht nur aus einem optionalen Vorzeichen,gefolgt von Ziffern und möglicherweise einem Suffix, um den Typ zu spezifizieren, zumBeispiel l oder L für long-Zahlen. Unveränderliche Größen sollen stets als const oderconstexpr deklariert werden! Der Compiler wird damit in die Lage versetzt, fehlerhafteZuweisungen zu finden:

const float PI = 3.1415926f; // f = Suffix für floatconstexpr unsigned int ANZAHL = 1000;// weiterer Programmtext ...PI = 17.5; // ergibt eine Fehlermeldung des Compilers!ANZAHL = 10; // ergibt eine Fehlermeldung des Compilers!

Auf den Unterschied zwischen const und constexpr, der hier noch keine Rolle spielt,wird in Abschnitt 2.6 eingegangen. Vorweg sei schon bemerkt, dass constexpr verwendetwerden kann, wenn das Ergebnis zur Compilierzeit aus einem Literal abgeleitet werdenkann:

int a {3}; // nicht konstanta = 5; // a ändert sichconst int b {a}; // ok, b ist konstantconstexpr int c {a}; // Fehler! nicht aus Literal ableitbarconstexpr int d {3}; // ok, Literalconst int e {4}; // ok, e ist konstantconstexpr int f {e}; // ok, indirekt aus Literal ableitbar

1.6.5 ZeichenZeichen sind in diesem Zusammenhang Buchstaben wie A, b, c, D, Ziffernzeichen wie1, 2, 3 und Sonderzeichen wie ; , . ! , und andere. Dabei werden Zeichenkonstanten(= Zeichenliterale) immer in Hochkommata eingeschlossen, also zum Beispiel ’a’, ’1’, ’?’usw. Für Zeichen wird der Datentyp char bereitgestellt (Beispieldeklaration siehe einigeZeilen weiter unten). Ein Zeichen ist in diesem Sinne stets auch nur ein Zeichen, insbe-sondere sind Ziffernzeichen etwas anderes als die Ziffern selbst, das heißt ’1’ ist nicht 1!Ein Zeichen wird intern als 1-Byte-Ganzzahl interpretiert (0 ... 255 unsigned char bezie-hungsweise -128 ... +127 signed char). Hier und im Folgenden wird angenommen, dassein Byte aus 8 Bits besteht6. Die ASCII-Tabelle definiert die ersten 7 Bits eines Bytes, also128 Zeichen (siehe Abschnitt A.1). Es gibt drei verschiedene char-Datentypen:signed char

unsigned char

char // bedeutet systemabhängig unsigned oder signedMit numeric_limits<char>::is_signed können Sie herausfinden, ob Ihr System Zeichendes Typs char als unsigned oder signed interpretiert:

6 Dies muss nicht für jedes System gelten, ist aber verbreitet.

Page 56: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 55

// cppbuch/k1/is_signed.cpp#include <iostream>#include <limits> // hier sind die Bereichsinformationenusing namespace std;

int main() {if(numeric_limits<char>::is_signed) {

cout << "char wird auf diesem System als signed interpretiert.\n";}else {

cout << "char wird auf diesem System als unsigned interpretiert.\n";}cout << "Grenzwerte für char, in int umgewandelt:\n";cout << "Minimum= " << static_cast<int>(numeric_limits<char>::min()) << ’\n’;cout << "Maximum= " << static_cast<int>(numeric_limits<char>::max()) << ’\n’;

}

Auf die Umwandlung mit static_cast<int> wird unten noch genauer eingegangen. Zu-sätzlich gibt es »lange«-Zeichen (englisch wide characters), die den Typ wchar_t haben.»Wide characters« sind für Zeichensätze gedacht, bei denen ein Byte nicht zur Darstel-lung eines Zeichens ausreicht, zum Beispiel japanische Zeichen. Ein Zeichenliteral vomTyp wchar_t beginnt mit einem L, zum Beispiel L’??’.

Mehr über wchar_t und Zeichenliterale lesen Sie in Abschnitt 30.2.

Alle Basisfunktionen der C++-Standardbibliothek gelten ebenso für wchar_t wie für char.Falls nicht ausdrücklich anders erwähnt, wird für char im Folgenden stets signed char

angenommen. Die Art der Voreinstellung variiert von Compiler zu Compiler. Beispielefür Deklarationen und Zuweisungen:

const char STERN {’*’};char a;a = ’a’;

a und ’a’ haben hier eine verschiedene Bedeutung. a ist hier eine Variable, die nur dankder Zuweisung den Wert ’a’ hat. Ein anderer Wert wäre ebenso möglich:

a = ’x’;a = STERN;

Es gibt besondere Zeichenkonstanten der ASCII-Tabelle, die nicht direkt im Druck oderin der Anzeige sichtbar sind. Um sie darstellen zu können, werden sie als Folge zweierZeichen geschrieben, nehmen aber dennoch ebenfalls nur ein Byte in Anspruch. DieseZeichen heißen auch Escape-Sequenzen, weil \ als Escape-Zeichen dient, um der norma-len Interpretation als einzelnes Zeichen zu entkommen (englisch to escape).Tabelle 1.6 zeigt einige Beispiele. Auch die Ausgabe von endl bewirkt eine neue Zeile.endl ist allerdings nicht als Zeichenkonstante zu verstehen, weil es zusätzlich für dassofortige Erscheinen der Zeile auf dem Bildschirm sorgt, was bei ’\n’ durch die gepufferteAusgabe nicht immer so sein muss (siehe Abschnitt 1.10.1 auf Seite 101).Da ein char-Zeichen genau ein Byte beansprucht, ist ein Zeichen der Oktaldarstellung\777 nicht erlaubt (\377 = 25510). Es sind beliebig viele Oktal- oder Hexadezimalzif-

Page 57: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

56 1 Es geht los!

Tabelle 1.6: Besondere Zeichenkonstanten (Escape-Sequenzen)

Zeichen Bedeutung ASCII-Name

\a Signalton BEL\b Backspace BS\f Seitenvorschub FF\n neue Zeile LF\r Zeilenrücklauf CR\t Tabulator HT\v Zeilensprung VT\\ Backslash\’ ’\" "

� = Platzhalter:\� � = Folge von Oktalziffern,

Beispiel: \377\0 Spezialfall davon (Nullbyte) NUL\x�, \X� � = Zeichenfolge aus Hex-Ziffern,

Beispiel: \xDB oder \x1f

fern erlaubt, wenn der Zeichentyp (z.B. wchar_t) sie darstellen kann. Ein Zeichen hateine eindeutige Position innerhalb der ASCII-Tabelle (siehe Seite 933). Der für eine Pro-grammdatei verwendete Zeichensatz kann ein anderer als ASCII sein. Er wird bei derCompilation in den Zeichensatz, der für die Ausführung des Programms maßgeblichist, umgewandelt. Der Einfachheit halber bleibe ich im Folgenden bei ASCII (mehr überdas Thema Zeichensatz lesen Sie in Abschnitt 30.2). Die Positionen zweier aufeinanderfolgender Ziffernzeichen liegen jedoch stets genau nacheinander (ohne andere Zeichendazwischen), was die Umwandlung einer Folge von Ziffernzeichen in eine Zahl erleich-tert.Genau genommen definiert die ASCII-Tabelle nur alle Zeichen mit 7 Bits, insgesamt 128.Der Datentyp char stellt 8 Bits, also 1 Byte zur Verfügung, sodass 256 Zeichen darstellbarsind. Die 128 zusätzlichen Zeichen sind nicht genormt, sondern unterscheiden sich fürverschiedene Rechner- und Betriebssystemtypen. Meistens werden in diesen 128 Zusatz-zeichen Blockgrafiksymbole und nationale Sonderzeichen wie ä, ö, ß untergebracht.Die Position eines Zeichens in der Tabelle kann über die Umwandlung in eine int-Zahlbestimmt werden, ebenso wie aus einer Position über die Umwandlung in char das zu-gehörige Zeichen ermittelt werden kann. Die Umwandlung geschieht einfach über denstatic_cast-Operator mit Angabe des gewünschten Datentyps in spitzen und Angabeder Variable in runden Klammern. Die Typumwandlung heißt in der englischsprachigenLiteratur type cast oder einfach cast. Der static_cast-Operator verlangt bestimmte, in[ISOC++] festgelegte Verträglichkeiten zwischen den zu wandelnden Typen.

char c {’x’};int i = static_cast<int>(c); // Typumwandlung char→ int

bedeutet, dass der Wert der Variablen i eine int-Repräsentation der char-Variablen c ist.Andere, einfachere Schreibweisen sind ebenfalls möglich:

i = int(c); // oderi = (int) c;

Page 58: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 57

Weil ein char vom Compiler wie eine 1-Byte-int-Zahl interpretiert wird, ist auch eineimplizite Typumwandlung möglich:

i = c;

Die einfacheren Schreibweisen werden jedoch nicht empfohlen, weil sie in bestimmtenZusammenhängen unsicherer sind. Eine Begründung wird in Abschnitt 6.8 gegeben. DieSchreibweise int(c) erinnert an die später zu besprechenden Funktionen. Hier wird vonint nach char und zurück gewandelt:

i = 66;c = static_cast<char>(i); // Typumwandlung int -> charcout << c; // ’B’c = ’1’; // Das Ziffernzeichen ’1’ hat die Positioni = static_cast<int>(c); // 49 innerhalb der ASCII-Tabelle:cout << i; // 49

Es gelten die Identitätenc == static_cast<char>( static_cast<int>(c)); undi == static_cast<int>( static_cast<char>(i));,

falls -128 ≤ i ≤ 127 ist (beziehungsweise 0 ≤ i ≤ 255 bei unsigned char). Falls i au-ßerhalb dieses Bereichs liegt, gibt es einen Datenverlust, weil die überzähligen Bits bei derUmwandlung nicht berücksichtigt werden können. Mehr zu Standard-Typumwandlungenerfahren Sie in Kürze. Wie können Sie aus einem Ziffernzeichen die repräsentierte Ziffererhalten? Da die Folge der Ziffernzeichen in der ASCII-Tabelle mit ’0’ beginnt, genügt es,’0’ abzuziehen:

char c {’5’};int ziffer {c - ’0’}; // implizite Typumwandlung!cout << ziffer << ’\n’; // 5ziffer = static_cast<int>(c) - static_cast<int>(’0’); // explizite Typumwandlung!

Weil ein char vom Compiler wie eine 1-Byte-int-Zahl interpretiert wird, ist das Rech-nen ohne explizite Typumwandlung möglich. Zum Vergleich sind beide Möglichkeitenangegeben.

Operatoren für Zeichen

Da der Datentyp char intern als 1-Byte-Ganzzahl dargestellt wird, sind eigentlich alleGanzzahl-Operatoren (siehe Tabelle 1.2 auf Seite 47) möglich, aber im Sinne der Bedeu-tung von Zeichen sind nur die Operatoren aus Tabelle 1.7 sinnvoll.

Tabelle 1.7: Operatoren für charOperator Beispiel Bedeutung

= d = ’A’ Zuweisung< d < f kleiner als> d > f größer als<= d <= f kleiner gleich>= d >= f größer gleich== d == f gleich!= d != f ungleich

Page 59: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

58 1 Es geht los!

1.6.6 Logischer Datentyp boolEin logischer Datentyp wird zur Erinnerung an den englischen Mathematiker GeorgeBoole (1815–1864) mit bool bezeichnet. Boole hat die später nach ihm benannte Boo-lesche Algebra entwickelt. Variablen eines logischen Datentyps können nur die Wahr-heitswerte wahr (englisch true) beziehungsweise falsch (englisch false) annehmen. Fallsnotwendig, wird der Datentyp bool zu int gewandelt, wobei false der Wert 0 ist undtrue der Wert 1. Die Ausgabe als Text true bzw. false statt 0 oder 1 kann eingestelltwerden:

bool istGrossBuchstabe;char c;cin >> c;istGrossBuchstabe = (c >= ’A’) && (c <= ’Z’);cout << istGrossBuchstabe; // Wandlung in intcout.setf(ios_base::boolalpha); // Textformat einschaltencout << istGrossBuchstabe; // true oder falsecout.unsetf(ios_base::boolalpha); // Textformat ausschaltencout << istGrossBuchstabe << ’\n’; // 1 oder 0

Zunächst werden die Klammern ausgewertet, die jeweils für sich Wahrheitswerte vonfalse oder true ergeben. Die Relationen >= und <= beziehen sich dabei auf die ASCII-Tabelle. Es wird also geprüft, ob das Zeichen c gleich dem Zeichen ’A’ ist oder in derTabelle nach ihm folgt, und ob es gleich dem Zeichen ’Z’ ist oder in der Tabelle vor dem’Z’ liegt. Die Wahrheitswerte werden durch das logische UND (&&) verbunden, nicht zuverwechseln mit dem bitweisen UND (&) der Tabelle 1.2 auf Seite 47. Das Ergebnis wirdder Variablen istGrossBuchstabe zugewiesen. Die Klammern sind nur zur Verdeutlichungangegeben. Sie können entfallen, weil die relationalen Operatoren eine höhere Prioritätals die logischen haben. Die Tabelle 1.8 zeigt die Operatoren für den Datentyp bool.

Tabelle 1.8: Operatoren für logische Datentypen

Operator Beispiel Bedeutung

! !i logische Negation&& a && b logisches UND|| a || b logisches ODER== a == b Vergleich!= a != b Vergleich= a = a && b Zuweisung

Der Datentyp bool wird an allen Stellen, die nicht ausdrücklich bool verlangen, nach int

gewandelt (siehe obiges Beispiel). Dabei wird true zu 1 und false zu 0. Die umgekehrteWandlung von int nach bool ergibt false für 0 und true für alle anderen int-Werte.Hier ist die Wirkungsweise der logischen Negation zu sehen:

bool wahrheitswert {true};wahrheitswert = !wahrheitswert; // Negationcout << wahrheitswert << ’\n’; // 0, d.h. false// Beispiel mit int-Zahlen: Aus 0 wird 1 und aus einer Zahl ungleich 0// wird durch die Negation eine 0:int i {17};int j {!i}; // 0 (implizite Typumwandlung)

Page 60: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 59

i = !j; // 1 (implizite Typumwandlung)// Typumwandlung von int nach boolwahrheitswert = 99; // truewahrheitswert = 0; // false

1.6.7 ReferenzenEine Referenz ist ein Datentyp, der einen Verweis auf ein Objekt liefert. Referenzen wer-den in C++ häufig zur Parameterübergabe benutzt. Nähere Erklärungen und umfangrei-chere Beispiele finden sich erst später (ab Kapitel 2). Eine Referenz bildet einen Alias-Namen für ein Objekt, über den es ansprechbar ist. Ein Objekt hat damit zwei Namen!Der Compiler »weiß« aufgrund der Deklaration, dass es sich um eine Referenz handelt,und nicht etwa um ein neues Objekt. Um eine Variable als Referenz zu deklarieren, wirddas &-Zeichen benutzt, das neben dem bitweisen UND und dem (noch nicht benutzten)Adressoperator nun die dritte Bedeutung hat. Beispiele:

int i {2};int j {9};int& r {i}; // Referenz auf i (r ist ein Alias für i)r = 10; // ändert ir = j; // Wirkung: i = j;

Wo das &-Zeichen zwischen int und r steht, ist unerheblich. In diesem Buch wird dieSchreibweise int& r bevorzugt, um zu verdeutlichen, dass die Referenzeigenschaft zumTyp gehört. Eine Referenz wird genau wie eine Variable benutzt, der Compiler weiß, dasssie ein Alias-Name ist. Referenzen müssen bei der Deklaration initialisiert werden. Es istnicht möglich, eine Referenz nach der Initialisierung so zu ändern, dass sie ein Alias-Name für eine andere Variable als die erstzugewiesene wird. Die Deklaration int& s =

r; könnte vordergründig so interpretiert werden, dass die Referenz s eine Referenz auf rwäre, weil sie ja mit r initialisiert wird. Der Compiler setzt aber, wie oben beschrieben,auf der rechten Seite für r das referenzierte Objekt i ein. s ist daher nur ein weitererAlias-Name für i. Mit anderen Worten, wenn nach den obigen Deklarationen einer derNamen i, r oder s benutzt wird, könnte man ihn durch einen der anderen beiden ersetzen,ohne dass ein Programm in seiner Bedeutung geändert wird. Zusammengefasst:

Auf Objekte wird nur über symbolische Namen (Bezeichner) oder Zeiger zugegriffen.Zeiger (in Kapitel 4 beschrieben) seien hier ausgeklammert.Die Bezeichner (Namen) von Referenzen sind nichts anderes als Alias-Namen. Fürein Objekt kann es keinen oder beliebig viele Alias-Namen geben, die wie andereBezeichner auch verwendet werden.Alle Bezeichner für dasselbe Objekt sind in der Verwendung gleichwertig. Die obigeDeklaration int& s = r; hat daher dieselbe Wirkung wie int& s = i;.

1.6.8 Regeln zum Bilden von AusdrückenEs gelten im Allgemeinen Vorrangregeln der Algebra beim Auswerten eines Ausdrucksinklusive der Klammerregeln. Die Tabelle 1.9 zeigt die Rangfolge einiger ausgewählterOperatoren.

Page 61: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

60 1 Es geht los!

Tabelle 1.9: Präzedenz ausgewählter Operatoren (deswegen fehlen Ränge)

Rang Operatoren

1 . [ ] f() (Funktionsaufruf)2 ++ -- (postfix)3 sizeof ++ -- (präfix) ~ !

+ - (unär)(Typ) Ausdruck (C-Stil-Typumwandlung)

5 * / %6 + - (binär)7 << >>8 < > <= >=9 == !=10 & (bitweises UND)11 ˆ (bitweises exklusiv-ODER)12 | (bitweises ODER)13 && (logisches UND)14 || (logisches ODER)15 ?: (Bedingungsoperator)16 alle Zuweisungsoperatoren =, +=, <<= usw.

Einige der Operatoren werden erst in folgenden Kapiteln erklärt. Eine ausführliche Auf-listung der Operatorenrangfolge finden Sie im Anhang A.4, Seite 937.Auf gleicher Prioritätsstufe wird ein Ausdruck von links nach rechts abgearbeitet (links-assoziativ), mit Ausnahme der Ränge 2, 15 und 16 der Tabelle, die von rechts abgearbeitetwerden (rechtsassoziativ). Zuerst werden jedoch die Klammern ausgewertet. Beispiel:a = b + d + c; ist gleich mit: a = ((b + d) + c); → linksassoziativa = b = d = c; ist gleich mit: a = (b = (d = c)); → rechtsassoziativ

Die Reihenfolge der Auswertung von Unterausdrücken untereinander, also auch Klam-merausdrücken, ist jedoch undefiniert. Daher sollen Ausdrücke vermieden werden, dieeinen Wert sowohl verändern als auch benutzen. Zwei Beispiele:

int total {}; // {} entspricht {0}int sum = (total = 3) + total; // Fehler!int i {2};i = 3 * i++; // Fehler !

Die Variable sum kann hier den Wert 3 oder den Wert 6 annehmen, abhängig von derReihenfolge, in der die Unterausdrücke in den Klammern berechnet werden. Der Wertvon i ist undefiniert, weil die Reihenfolge der Auswertung nicht feststeht. Es gibt zweiMöglichkeiten:1. 3*i wird berechnet und ergibt 6. Dieser Wert wird i zugewiesen. Erst anschließend

wird i inkrementiert, sodass zum Schluss i gleich 7 gilt.2. 3*i wird berechnet und ergibt 6. Gleich nach der Berechnung wird i von 2 auf 3

inkrementiert. Erst dann erfolgt die Zuweisung des berechneten Ergebnisses an i,sodass zum Schluss i gleich 6 gilt.

Der Ausdruck sum = (total = 3) + total kann mit dem Kommaoperator in einen de-finierten Ausdruck verwandelt werden: sum = (total = 3, total + total);. Durch denKommaoperator wird die Auswertungsreihenfolge von links nach rechts festgelegt. Das

Page 62: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.6 Einfache Datentypen und Operatoren 61

Ergebnis des gesamten Ausdrucks ist das Ergebnis des Teilausdrucks nach dem letztenKomma.

1.6.9 Standard-TypumwandlungenStandard-Typumwandlungen sind implizite Typumwandlungen für eingebaute Typen.Implizit heißt, dass eine Typumwandlung nicht ausdrücklich hingeschrieben wird undder Compiler dennoch keine Fehlermeldung bei der Typumwandlung gibt. Es kann aucheine Folge von hintereinandergeschalteten Standard-Typumwandlungen geben. In die-sem Abschnitt werden die wichtigsten Möglichkeiten beschrieben. Im Einzelfall kann einInformationsverlust auftreten. Mögliche Ursachen:

Genauigkeitsverlust, weil zum Beispiel eine float-Zahl nicht soviele Mantissen-Bitswie eine double-Zahl hat.Überschreitung des Grenzbereichs, weil der mögliche Exponent einer float-Zahl klei-ner als der einer double-Zahl ist.Verlust des Nachkommateils bei der Umwandlung z.B. double nach int.Umwandlung z.B. einer zu großen double- oder float-Zahl in einen integralen Typ.Die Bitzahl reicht nicht, etwa wenn eine den int-Bereich überschreitende long-Zahlin eine int-Zahl umgewandelt wird. Dasselbe gilt für die Umwandlung einer Zahl,die größer als 127 ist, in den Typ signed char (bzw. > 255 in unsigned char). Dieüberzähligen Bits werden einfach abgeschnitten. Die folgenden Zahlen zeigen die8-Byte-long-Zahl 4294967364 in binärer Darstellung und die entstehende 4-Byteint-Zahl in binärer Darstellung nach Abschneiden der höchstwertigen 32 Bits:0000000000000000000000000000000100000000000000000000000001000100

00000000000000000000000001000100

So wird aus der long-Zahl 4294967364 die int-Zahl 68.Vorzeichenverlust und gleichzeitige Wertänderung, wenn zum Beispiel eine negativeint-Zahl in eine unsigned int-Zahl umgewandelt wird.Nichtbeachtung der Rechenregeln. Ein Beispiel:

double x = 3 / 4; // x ist 0.0double y = 3 / 4.; // y ist 0.75

3/4 ist eine Rechenoperation mit ganzen Zahlen. Das Ergebnis ist die ganze Zahl 0.Erst bei der Zuweisung wird die 0 in eine double-Zahl umgewandelt. 4. ist jedoch vomTyp double (beachten Sie den Punkt nach der 4!). Um die Rechenoperation ausführenzu können, wird die 3 vom Compiler ebenfalls in eine double-Zahl umgewandelt, sodass 3./4. ( dasselbe wie 3.0/4.0) berechnet wird. Das Ergebnis ist 0.75, also auchvom Typ double.

Typumwandlung integraler Typen nach int

Jeder R-Wert (Rechts-Wert, (englisch rvalue), vgl. Seite 66) des Typs char, signed char,unsigned char, short int oder unsigned short int kann ohne Informationsverlust ineinen int-Wert umgewandelt werden (englisch integral promotion).

Page 63: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

62 1 Es geht los!

Typumwandlung integraler Typen untereinander

Integrale Typen sind untereinander konvertierbar. Im C++-Standard werden alle Fälle, dienicht zur obigen »integral promotion« gehören, integral conversion genannt. Sonderfällesind die Typen bool (siehe Seite 58) und enum (siehe Seite 84).

float-Typumwandlungen

Jeder R-Wert des Typs float kann in einen double-Wert oder einen Integer-Wert um-gewandelt werden und umgekehrt. Für bool gilt ähnlich wie im int-Fall: true wird 1.0,false wird 0.0, 0.0 wird false, und ein float- bzw. double-Ausdruck ungleich 0 wirdtrue.

HinweisJe nach Art der Initialisierung ist das Verhalten des Compilers unterschiedlich:

int wert1 = 9.42; // keine Fehlermeldung, wert wird 9int wert2 {9.42}; // Fehler

Im ersten Fall gibt es einen möglicherweise unbemerkten Genauigkeitsverlust, im zwei-ten Fall eine Meldung des Compilers. Deswegen ist die zweite Art der Initialisierungvorzuziehen.

1.7 Gültigkeitsbereich undSichtbarkeit

In C++ gelten Gültigkeits- und Sichtbarkeitsregeln für Namen. Es gibt folgende Regeln:Namen sind nur nach der Deklaration und nur innerhalb des Blocks gültig, in dem siedeklariert wurden. Sie sind lokal bezüglich des Blocks. Zur Erinnerung: Ein Block istein Programmbereich, der durch ein Paar geschweifter Klammern { } eingeschlossenwird. Blöcke können verschachtelt sein, also selbst wieder Blöcke enthalten.Namen von Variablen sind auch gültig für innerhalb des Blocks neu angelegte innereBlöcke.Die Sichtbarkeit (englisch visibility) zum Beispiel von Variablen wird eingeschränktdurch die Deklaration von Variablen gleichen Namens. Für den Sichtbarkeitsbereichder inneren Variablen ist die äußere unsichtbar.

Der Datenbereich für lokale Daten wird bei Betreten des Gültigkeitsbereichs auf einembesonderen Speicherbereich mit dem Namen Stack angelegt und am Ende des Gültig-keitsbereichs, also am Blockende, wieder freigegeben. Der Stack (deutsch: »Stapel«, auchKellerspeicher) ist ein Bereich mit der Eigenschaft, dass die zuletzt darauf abgelegtenElemente zuerst wieder freigegeben werden (last in, first out). Damit lässt sich das be-schriebene Anlegen von Variablen bei Blockbeginn und ihre Freigabe bei Blockende gutverwalten, ohne dass wir uns darum kümmern müssen.

Page 64: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.7 Gültigkeitsbereich und Sichtbarkeit 63

Auf verschiedene Arten der Sichtbarkeitsbereiche (Funktionen, Dateien, Klassen) wirdspäter eingegangen. Das folgende Programm zeigt Beispiele für verschiedene Gültigkeits-bereiche (englisch scope). Der im Programm verwendete Operator :: bewirkt den Zugriffauf Variablen, die global sichtbar sind, also nicht Zugriff auf den nächsten äußeren Block.

Listing 1.7: Beispielprogramm: Variablen und Blöcke

// cppbuch/k1/bloecke.cpp#include<iostream>using namespace std;// a und b werden außerhalb eines jeden Blocks deklariert. Sie sind damit innerhalb// eines jeden anderen Blocks gültig und heißen daher globale Variablen.int a {1};int b {2};

int main() { // Ein neuer Block beginnt.cout << "globales a= " << a << ’\n’; // Ausgabe von a// Innerhalb des Blocks wird eine Variable a deklariert. Ab jetzt ist das globale a noch// gültig, aber nicht mehr unter dem Namen a sichtbar, wie die Folgezeile zeigt.int a {10};// Der Wert des lokalen a wird ausgegeben:cout << "lokales a= " << a << ’\n’;// Das globale a lässt sich nach der Deklaration des lokalen a nur noch mithilfe des// Bereichsoperators :: (englisch scope operator) ansprechen. Ausgabe von ::a :cout << "globales ::a= " << ::a << ’\n’;{ // Ein neuer Block innerhalb des bestehenden beginnt.

int b {20};// Variable b wird innerhalb dieses Blocks deklariert. Damit// wird das globale b zwar nicht ungültig, aber unsichtbar.int c {30}; // c wird innerhalb dieses Blocks deklariert.// Die Werte von b und c werden ausgegeben.cout << "lokales b = " << b << ’\n’;cout << "lokales c = " << c << ’\n’;// Wie oben beschrieben, ist das globale b nur über den// Scope-Operator ansprechbar. Ausgabe von ::b:cout << "globales ::b = " << ::b << ’\n’;

} // Der innere Block wird geschlossen. Damit ist das globale b// auch ohne Scope-Operator wieder sichtbar:

cout << "globales b wieder sichtbar: b = " << b << ’\n’;// cout << "c = " << c << ’\n’; // Fehler, siehe Text

} // Ende des äußeren Blocks

Das Programm wird im Kommentar zeilenweise erklärt. Es zeigt, dass Gültigkeit undSichtbarkeit nicht das Gleiche sind, und erzeugt folgende Ausgabe:globales a= 1lokales a= 10globales ::a= 1lokales b = 20lokales c = 30globales ::b = 2globales b wieder sichtbar: b = 2

Page 65: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

64 1 Es geht los!

Die Kommentarzeichen // in der vorletzten Programmzeile sind erforderlich, weil derCompiler diese Zeile sonst als fehlerhaft bemängeln würde. Grund: Durch Schließen desinneren Blocks ist der Gültigkeitsbereich aller in diesem Block deklarierten Variablenbeendet, also ist c außerhalb des Blocks unbekannt.Vermeiden Sie lokale Objekte mit Namen, die Objekte in einem äußeren Gültigkeitsbereichverdecken! Die Verständlichkeit eines Programms wird durch verschiedene Objekte mitdemselben Namen erschwert, wie das Beispiel hoffentlich zeigt.

1.7.1 Namespace stdEine weitere Möglichkeit zur Schaffung von Sichtbarkeitsbereichen sind Namensräu-me (englisch namespaces). Bisher wurde der zur C++-Standardbibliothek gehörende Na-mensraum std benutzt, wie Sie an den Zeilen using namespace std; in den Beispielengesehen haben. Namensräume spielen bei der Benutzung verschiedener Bibliotheken eineRolle. Einzelheiten werden weiter unten in Abschnitt 2.7 (ab Seite 149) erklärt. Hier wirdvorab darauf hingewiesen, dass eine pauschale Nutzung der Standardbibliothek nichtnotwendig ist, wenn die betreffenden Elemente mit einem sogenannten qualifiziertenNamen angesprochen werden, der den Namensraum angibt. Bezogen auf den Standard-Namensraum std gibt es im Wesentlichen drei Möglichkeiten:

// 1. Pauschale Nutzungusing namespace std; // macht alles aus std ab jetzt bekannt// ... ggf. weiterer Programmtextcout << "Ende" << endl; // zur Abwechslung mal endl statt ’\n’

oder

// 2. Nutzung von cout und endl aus std mit qualifizierten Namen:// using namespace std; sei nicht deklariert.std::cout << "Ende" << std::endl; // richtigcout << "Ende" << endl; // Fehlermeldung des Compilers!

oder

// 3. Deklaration ausgewählter Teile; using namespace std; sei nicht deklariert.using std::cout;using std::endl;cout << "Ende" << endl;

Die erste Möglichkeit wird in den main()-Programmen dieses Buchs bevorzugt, weil sieSchreibarbeit spart. Die dritte Möglichkeit kann ebenfalls in *.cpp-Dateien verwendetwerden. Die zweite Möglichkeit wird in allen anderen Fällen empfohlen. Sie werden inden Beispielen alle drei Varianten antreffen.

Übung1.2 Schreiben Sie ein Programm, das die größtmögliche unsigned int-Zahl (int, long,

unsigned long) ausgibt, ohne dass die Kenntnis der systemintern verwendeten Bitanzahlfür jeden Datentyp benutzt wird. Hinweise: Studieren Sie die möglichen Operatoren fürganze Zahlen. Der ~-Operator invertiert alle Bits, macht also aus einer 0 die maximalgrößte unsigned-Zahl. Sie ist die größte Zahl, weil alle Bits gesetzt sind. Bei signed-Zahlen wird ein Bit für das Vorzeichen gebraucht (Zweierkomplement-Darstellung).

Page 66: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 65

1.8 KontrollstrukturenKontrollstrukturen dienen dazu, den Programmfluss zu steuern. Im einfachsten Fall wer-den Anweisungen eines Programms eine nach der anderen in derselben Reihenfolge aus-geführt, wie sie hingeschrieben worden sind. Dies ist nicht immer erwünscht. Manchmalist es notwendig, dass der Programmfluss sich in Abhängigkeit von den Daten ändernsoll, oder es müssen Teile des Programms wiederholt durchlaufen werden. Erst mit Kon-trollstrukturen lassen sich überhaupt Programme von einiger Komplexität schreiben.

1.8.1 AnweisungenIn den folgenden Abschnitten wird des Öfteren der Begriff »Anweisung« gebraucht, derdeswegen an dieser Stelle erläutert werden soll. Eine Anweisung kann unter anderemsein (eine vollständige Auflistung ist nicht beabsichtigt):

eine Deklarationsanweisungeine Ausdrucksanweisungeine Schleifenanweisungeine Auswahlanweisungeine Verbundanweisung, auch Block genannt.

Deklarationsanweisung

Eine Deklarationsanweisung führt einen Namen in das Programm ein. Sie kann in ver-schiedenen Formen vorkommen, unter denen die einfachen Deklarationen wie zum Bei-spiel int x; die häufigsten sind. Nach dieser Deklaration ist das Objekt x in einem Pro-gramm bekannt und kann benutzt werden. Eine einfache Deklaration wird stets mit einemSemikolon ; abgeschlossen.

Ausdrucksanweisung

Eine Ausdrucksanweisung ist ein Ausdruck (siehe Seite 43), gefolgt von einem Semiko-lon. Ein Ausdruck repräsentiert nach der Auswertung einen Wert. Zum Beispiel kann derAusdruck x == 1 den Wert true oder false annehmen. In C++ ist mit einer Ausdrucks-anweisung in der Regel eine Aktivität verbunden, zum Beispiel eine Zuweisung (sieheunten) wie x = 3;. Eine Zuweisung hat einen Wert, weswegen verkettete Zuweisungenmöglich sind:

a = b = c;

meint, dass b (= der Wert der Zuweisung b = c) der Variablen a zugewiesen wird. DerWert der letzten Zuweisung a = b wird nicht mehr verwendet. Selbst eine Ausgabe aufden Bildschirm ist ein Ausdruck. Der Wert ist das Objekt cout selbst, das die Ausgabebewerkstelligt.

cout << a << b;

bedeutetet dasselbe wie

(cout << a) << b;

Page 67: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

66 1 Es geht los!

Das Ergebnis des Ausdrucks in den runden Klammern ist wieder cout, weswegen derzweite Teil der Ausgabe als cout << b; gelesen werden kann. Ein alleinstehendes Semi-kolon ist eine Leeranweisung.

Zuweisung

Eine Zuweisung ist ein Spezialfall einer Ausdrucksanweisung. Ein Zuweisungsausdruck,zum Beispiel a = b, besteht aus drei Teilen:

Einem linken Teil, auch L-Wert (englisch lvalue) genannt, was eine Abkürzung fürLinks-Wert ist.Dem Zuweisungsoperator =.Einem rechten Teil, auch R-Wert (englisch rvalue) genannt, was eine Abkürzung fürRechts-Wert ist.

Dabei wird der L-Wert7 als (symbolische) Adresse, der R-Wert als Wert interpretiert. Diesymbolische Adresse wird durch einen Namen repräsentiert. Hier ist es der Name a. DieBedeutung einer Zuweisung a = b; ist also: Der Wert der Variablen b wird an die Adresseder Variablen a kopiert, sodass danach Variable a denselben Wert hat.

Schleifenanweisung

Diese Anweisungen sind mit den Schlüsselwörtern for, while und do .. while verbun-den. Einzelheiten folgen in den nächsten Abschnitten.

Auswahlanweisung

Diese Anweisungen sind mit den Schlüsselwörtern if und switch verbunden. Einzelhei-ten folgen in den nächsten Abschnitten.

Verbundanweisung, Block

Eine Verbundanweisung, auch Block genannt, ist ein Paar geschweifte Klammern, dieeine Folge von Anweisungen enthalten. Die Folge kann leer sein. Die enthaltenen An-weisungen können selbst wieder Verbundanweisungen sein.

{ } // leerer Block

{ // Block mit einer AnweisungAnweisung

}

{ // Block mit zwei AnweisungenAnweisung1

Anweisung2

}

7 Mehr dazu im Glossar auf Seite 959.

Page 68: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 67

1.8.2 Sequenz (Reihung)Im einfachsten Fall werden die Anweisungen der Reihe nach durchlaufen:

a = b + 1;a += a;cout << "\nErgebnis =" << a;

Nebenbei sehen wir, dass das Zeichen ’\n’ in eine Zeichenkette eingebaut werden kann,sodass vor der Ausgabe von Ergebnis eine neue Zeile auf dem Bildschirm begonnen wird.

1.8.3 Auswahl (Selektion, Verzweigung)Häufig hängt die Ausführung von Anweisungen von einer Bedingung ab. C++ stellt fürsolche Zwecke die if-Anweisung bereit.

if (Bedingung)Anweisung1

bedeutet, dass Anweisung1 nur dann ausgeführt wird, wenn die Bedingung wahr ist,das heißt zu einem Ausdruck mit dem Wert true (oder ungleich 0) ausgewertet wird.Die Bedingung kann ein arithmetisches Ergebnis haben. Alternativ kann eine zweiteAnweisung angegeben werden, sodass Anweisung1 ausgeführt wird, falls die Bedingungwahr ist, und andernfalls Anweisung2 (else-Zweig):

if (Bedingung)Anweisung1

else

Anweisung2Zu einem if- oder else-Zweig gehört stets nur genau eine Anweisung! Diese kann na-türlich eine Verbundanweisung (Block) sein, also ein Paar geschweifter Klammern, diebeliebig viele Anweisungen umschließen können, also auch keine oder nur eine. Abbil-dung 1.4 zeigt das Syntaxdiagramm einer if-Anweisung.

if ( Bedingung

{ }

else

) AnweisungOderBlock

if-Anweisung:

AnweisungOderBlock

AnweisungOderBlock:Anweisung

AnweisungOderBlock

Abbildung 1.4: Syntaxdiagramm einer if-Anweisung

Ein Semikolon nach einem Block bedeutet eine zweite (leere) Anweisung. Diese ist immerunnütz und im Fall eines folgenden else sogar falsch. Beispiele für if-Anweisungen:

Page 69: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

68 1 Es geht los!

if(x < 100)cout << "x < 100"; // nur eine Anweisung

elsecout << "x >= 100";

if(a > b) {x = a - b; // zwei Anweisungen im Blocky = 2*a;

}

if((c >= ’A’) && (c <= ’Z’)) { // if-Anweisung mit elseistGrossBuchstabe = true;

}else {

istGrossBuchstabe = false;}

Die letzte if-Anweisung ist gleichwertig mit: istGrossBuchstabe = c >= ’A’ && c <=

’Z’;. Der Bedingungsausdruck muss vom Typ bool sein oder in bool umgewandelt wer-den können. Relationale Ausdrücke wie (a < b) werden zu true ausgewertet, falls a < b

ist, ansonsten zu false. if(a == 0) kann daher auch als if(!a) geschrieben werden: aist eine Zahl vom Typ int, die in einen Wahrheitswert umgewandelt wird. Dabei wird einWert, der gleich 0 ist, in false und ein Wert ungleich 0 in true umgewandelt.Auf dieses Ergebnis wird der Negationsoperator ! angewendet, womit sich das gewünsch-te Verhalten ergibt. Bedingungsausdrücke werden von links nach rechts ausgewertet. Da-bei werden unnötige Berechnungen übersprungen. Damit ist gemeint, dass in einer Be-dingung, die aus mehreren ODER-Verknüpfungungen besteht, die Berechnung nach demersten Ergebnis, das den Wahrheitswert wahr liefert, abgebrochen werden kann. Grundist, dass sich das Ergebnis nach weiteren Berechnungen nicht ändern kann. Umgekehrtist ein Weiterrechnen bei UND-Verknüpfungen nicht sinnvoll, sobald auch nur ein Teil-ergebnis falsch liefert. Diese Art der Auswertung wird Kurzschlussauswertung genannt(englisch short circuit evaluation).Mit Teilbedingungen verbundene Seiteneffekte werden daher nur bei Auswertung derjeweiligen Teilbedingung ausgeführt. Seiteneffekte sind Ergebnisse, die zusätzlich zumeigentlichen Zweck, gewissermaßen nebenbei, entstehen. In den folgenden Beispielen istdie Hauptsache die Auswertung der Bedingung. Als Seiteneffekt werden nach Auswer-tung der Bedingung, aber noch vor Ausführung des nachfolgenden Programmcodes, ibeziehungsweise j durch den Operator ++ modifiziert, sofern es nötig ist, die Teilbedin-gung zu berechnen. Vollziehen Sie die Beispiele nach! Das Ergebnis ist im Kommentaraufgeführt. Das Beispiel ist nur zur Übung gedacht. Im Allgemeinen sind Seiteneffektezu vermeiden!

int i = 0, j = 2; // stimmt’s oder nicht?if(i++ || j++) i++; // i == 2 und j == 3

int i = 1, j = 2;if(i++ || j++) i++; // i == 3 und j == 2

int i = 0, j = 2;if(i++ && j++) i++; // i == 1 und j == 2

Page 70: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 69

int i = 1, j = 2;if(i++ && j++) i++; // i == 3 und j == 3

if-Anweisungen können beliebig tief geschachtelt werden. Das Beispielprogramm hat dieAufgabe, ein Zeichen einzulesen und zu prüfen, ob es einer römischen Ziffer entspricht.Falls ja, soll die zugehörige arabische Zahl angezeigt werden, falls nein, eine passendeMeldung.

Listing 1.8: Umwandlung römischer Ziffern mit if/else

// cppbuch/k1/roemzif1.cpp#include <iostream>using namespace std;

int main( ) {int ziffer {0};char zeichen;cout << "Zeichen ?";cin >> zeichen;

if (zeichen == ’I’) ziffer = 1;else if (zeichen == ’V’) ziffer = 5;else if (zeichen == ’X’) ziffer = 10;else if (zeichen == ’L’) ziffer = 50;else if (zeichen == ’C’) ziffer = 100;else if (zeichen == ’D’) ziffer = 500;else if (zeichen == ’M’) ziffer = 1000;

if (ziffer == 0) {cout << "keine römische Ziffer!\n";

}else {

cout << ziffer << ’\n’;}

}

Achtung, Falle: Fehler in Verbindung mit if

Ein häufiger Fehler ist die versehentlich falsche Schreibweise des Gleichheitsoperators,sodass sich unfreiwillig der Zuweisungsoperator ergibt. a und b seien int-Zahlen.

if(a = b) { // Vorsicht! Vermutlich anders gemeint!cout << "a ist gleich b";

}

bewirkt, dass a ist gleich b immer dann ausgegeben wird, wenn b ungleich 0 ist. Dierichtige Schreibweise ist:

if(a == b) {cout << "a ist gleich b";

}

Die Verwendung von = statt == hat die Wirkung einer Zuweisung. Zunächst erhält a denWert von b. Das Ergebnis dieses Ausdrucks, nämlich a, wird dann als logische Bedingung

Page 71: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

70 1 Es geht los!

interpretiert. Die falsche Schreibweise führt also nicht nur zu einem falschen Ergebnisfür die Bedingung, sondern auch zur nicht beabsichtigten Änderung des Wertes von a.Freundliche Compiler geben an solchen Stellen eine Warnung aus – eine Chance, sichnoch einmal zu überlegen, ob wirklich eine Zuweisung gemeint ist.Eine weitere Gefahr sind Mehrdeutigkeiten durch falschen Schreibstil, das heißt falsche,wenn auch richtig gemeinte Einrückungen. Ohne Klammerung gehört ein else immerzum letzten if, dem kein else zugeordnet ist:

if(x == 1)if(y == 1)

cout << "x == y == 1 !";else

cout << "x != 1"; // falsch

Trotz der augenfälligen Übereinstimmung des Zeilenanfangs der untersten Zeile mit demZeilenanfang von if(x == 1) gehört das else syntaktisch zur if(y == 1)-Zeile! Richtigist:

if(x == 1) {if(y == 1) {

cout << "x == 1 und y == 1 !";}

}else {

cout << "x != 1"; // nun korrekt}

Auch einzelne Anweisungen nach if bzw. else sollen daher immer geklammert werden.Ein weiterer gelegentlicher Fehler aus der Praxis ist ein überflüssiges Semikolon (alsoeine Leeranweisung) nach der Bedingung, das beim Lesen leicht übersehen wird.

if(a == b); // Fehler!cout << "a ist gleich b";

Auf diese Art wird die if-Abfrage zwar durchgeführt, aber ohne Folgen, da sie bereitsbeim ersten Semikolon endet. Die anschließende Ausgabe wird also in jedem Fall durch-geführt, da die Ausgabe nun eine eigenständige Anweisung ist und somit nicht mehr zurif-Anweisung gehört. Solche Fälle akzeptiert der Compiler widerspruchslos!Abschließend die Empfehlung, int und unsigned nicht zu mischen. Eine unsigned-Zahlkann nicht negativ sein. Wenn so ein Wert unüberlegt mit einem int-Wert verglichenwird, kann es schwer zu entdeckende Fehler geben:

int i {-1};unsigned int u {0};

if(u < i) {cout << u << ’<’ << i << ’\n’;

}

Da diese Art der Verwendung prinzipiell zulässig ist, wird ein Compiler sie akzeptierenund allenfalls eine Warnung ausgeben. Ganz klar ist 0 > –1, dennoch wird 0 < –1 aus-gegeben! Der Grund besteht darin, dass der Compiler im Fall verschiedener Datentypen

Page 72: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 71

eine Typumwandlung des zweiten Bedingungsoperanden vornimmt, um den Vergleichdurchführen zu können. Bei der Umwandlung von –1 in eine unsigned-Zahl wird dasBitmuster beibehalten. Damit ist das Ergebnis die größtmögliche unsigned-Zahl, die na-türlich größer als 0 ist.

Bedingungsoperator ?:

Dieser Operator ist der einzige in C++, der drei Operanden benötigt. Abbildung 1.5 zeigtdie Syntax.

:Bedingung ? Ausdruck1 Ausdruck2

Abbildung 1.5: Syntaxdiagramm des Bedingungsoperators

Falls die Bedingung zutrifft, ist der Wert des gesamten Ausdrucks der Wert von Aus-druck1, ansonsten der Wert von Ausdruck2. Ein Ausdruck mit dem Bedingungsoperatorkann lesbarer durch eine if-Anweisung ersetzt werden, wird aber wegen seiner Kürzegeschätzt. Die Berechnung des Maximums zweier Zahlen lautet:

max = a > b ? a : b;

Das if-Äquivalent dazu ist:

if(a > b) {max = a;

}else {

max = b;}

Übungen1.3 Schreiben Sie ein Programm, das drei ganze Zahlen als Eingabe verlangt. Die ers-te ist als Anfang eines Zahlenbereichs, die zweite als Ende des Bereichs gemeint. DasProgramm soll prüfen, ob die dritte Zahl innerhalb des Bereichs einschließlich der Gren-zen liegt und eine entsprechende Meldung ausgeben. Geben Sie eine Fehlermeldung aus,wenn die Zahl für den Anfang größer als die Zahl für das Ende ist.1.4 Schreiben Sie ein Programm, das drei ganze Zahlen als Eingabe verlangt und dannden Wert der größten Zahl ausgibt.

1.8.4 Fallunterscheidungen mit switchEine if-Anweisung erlaubt nur zwei Möglichkeiten. Erst durch die Verschachtelungkonnte die Auswahl unter mehreren Möglichkeiten getroffen werden. Die Gefahr be-steht jedoch, dass die gesamte Anweisung bei größerer Schachtelungstiefe unübersicht-lich wird und Änderungen nur umständlich nachzutragen sind. Einfacher und übersicht-licher ist daher die Auswahl unter vielen Anweisungen mit switch. Abbildung 1.6 zeigtdie Syntax, ein Beispiel folgt unten.

Page 73: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

72 1 Es geht los!

switch ( Ausdruck

}

case

)

Anweisung(en)

{

Konstante : break;

default Ersatzanweisung(en):

Abbildung 1.6: Syntaxdiagramm einer switch-Anweisung

Der Ausdruck wird ausgewertet und muss ein Ergebnis vom Typ int haben oder nach int

konvertierbar sein wie zum Beispiel char. Dieses Ergebnis wird mit den case-Konstantenconst1, const2 ... verglichen, die zum Einsprung an die richtige Stelle dienen. Bei Über-einstimmung werden die zur Konstante gehörigen Anweisungen ausgeführt. Nach Aus-führung wird nicht automatisch aus der switch-Anweisung herausgesprungen. Erst breakführt zum Verlassen der switch-Anweisung und soll stets verwendet werden, um unnöti-ge Tests auf die Folgewerte zu vermeiden. Die case-Konstanten const1, const2 ... müsseneindeutig und auf int abbildbar sein. Zeichen (Typ char) sind erlaubt, float-Zahlen nicht.Die nach default stehenden Anweisungen (meistens nur eine) werden immer dann ausge-führt, wenn der switch-Ausdruck einen Wert liefert, der mit keiner der case-Konstantenübereinstimmt. default ist optional, doch ist es sinnvoll, default mit anzugeben. Ins-besondere werden an dieser Stelle nicht vorgesehene Werte des switch-Ausdrucks abge-fangen oder Daten, die nicht berücksichtigt werden sollen, wie zum Beispiel fehlerhafteTastatureingaben. Die Aufgabe, römische Ziffern zu erkennen, kann übersichtlicher mitder Fallunterscheidung durch switch als mit verschachtelten if-Anweisungen wie aufSeite 69 gelöst werden, wie das folgende Programm zeigt.

Listing 1.9: Umwandlung römischer Ziffernzeichen mit switch

// cppbuch/k1/roemzif2.cpp#include <iostream>using namespace std;

int main() {int ziffer {-1};char zeichen;cout << "Zeichen ?";cin >> zeichen;switch(zeichen) {

case ’I’ : ziffer = 1; break;case ’V’ : ziffer = 5; break;case ’X’ : ziffer = 10; break;case ’L’ : ziffer = 50; break;case ’C’ : ziffer = 100; break;case ’D’ : ziffer = 500; break;case ’M’ : ziffer = 1000; break;

Page 74: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 73

default : ziffer = 0;}if (ziffer > 0) {

cout << "Ziffer = " << ziffer;}else {

cout << "keine römische Ziffer!";}cout << ’\n’;

}

Wenn eine case-Konstante mit der switch-Variablen übereinstimmt, werden alle nach-folgenden Anweisungen bis zum ersten break ausgeführt. Mit einem fehlenden break undeiner fehlenden Anweisung nach einer case-Konstante lässt sich eine ODER-Verknüpfungrealisieren. Bei der Umwandlung römischer Ziffern lässt sich damit die Auswertung vonKleinbuchstaben bewerkstelligen:

switch(zeichen) {case ’i’ :case ’I’ : ziffer = 1; break;case ’v’ : case ’V’ : ziffer = 5; break; // andere Schreibweise// Rest weggelassen

Ein fehlendes break soll kommentiert werden, sofern sich die Absicht nicht klar ausdem Programm ergibt. Alle interessierenden case-Konstanten müssen einzeln aufgelistetwerden. Es ist in C++ nicht möglich, Bereiche anzugeben, etwa der Art case 7..11 :

Anweisung break; anstelle der länglichen Liste case 7: case 8: case 9: case 10: case

11: Anweisung break;. In solchen Fällen können Vergleiche aus der switch-Anweisungherausgenommen werden, um sie als Abfrage if(ausdruck >= startwert && ausdruck <=

endwert)... zu realisieren.

1.8.5 WiederholungenHäufig muss die gleiche Teilaufgabe oft wiederholt werden. Denken Sie nur an die Sum-mation von Tabellenspalten in der Buchführung oder an das Suchen einer bestimmtenTextstelle in einem Buch. In C++ gibt es zur Wiederholung von Anweisungen drei ver-schiedene Arten von Schleifen. In einer Schleife wird nach Abarbeitung einer Teilaufgabe(zum Beispiel Addition einer Zahl) wieder an den Anfang zurückgekehrt, um die gleicheAufgabe noch einmal auszuführen (Addition der nächsten Zahl). Durch bestimmte Be-dingungen gesteuert, zum Beispiel Ende der Tabelle, bricht irgendwann die Schleife ab.

Schleifen mit while

Abbildung 1.7 zeigt die Syntax von while-Schleifen. AnweisungOderBlock ist wie aufSeite 67 definiert. Die Bedeutung einer while-Schleife ist: Solange die Bedingung wahrist, die Auswertung also ein Ergebnis ungleich 0 oder true liefert, wird die Anweisungbzw. der Block ausgeführt. Die Bedingung wird auf jeden Fall zuerst geprüft. Wenn dieBedingung von vornherein unwahr ist, wird die Anweisung gar nicht erst ausgeführt(siehe Abbildung 1.8). Die Anweisung oder der Block innerhalb der Schleife heißt Schlei-fenkörper. Schleifen können wie if-Anweisungen beliebig geschachtelt werden.

Page 75: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

74 1 Es geht los!

)Bedingung( AnweisungOderBlockwhile

Abbildung 1.7: Syntaxdiagramm einer while-Schleife

while(Bedingung1) // geschachtelte Schleifen, ohne und mit geschweiften Klammernwhile(Bedingung2) {

.....while(Bedingung3) {

.....}

}

Bedingung erfüllt?

Anweisungja

nein

while (Bedingung) { Anweisung}

Abbildung 1.8: Flussdiagramm für eine while-Anweisung

Beispiele

Unendliche Schleife:

while(true)Anweisung

Anweisung wird nie ausgeführt (unerreichbarer Programmcode):

while(false)Anweisung

Summation der Zahlen 1 bis 99:

int sum = 0;int n = 1;int grenze = 99;while(n <= grenze) {

sum += n++;}

Berechnung des größten gemeinsamen Teilers GGT(x, y) für zwei natürliche Zahlenx und y nach Euklid. Es gilt:

GGT(x, x), also x = y: Das Resultat ist x.GGT(x, y) bleibt unverändert, falls die größere der beiden Zahlen durch die Diffe-renz ersetzt wird, also GGT(x, y) == GGT(x, y-x), falls x < y.

Das Ersetzen der Differenz geschieht im folgenden Beispiel iterativ, also durch eineSchleife.

Page 76: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 75

Listing 1.10: Beispiel für while-Schleife

// cppbuch/k1/ggt.cpp Berechnung des größten gemeinsamen Teilers#include <iostream>using namespace std;

int main() {unsigned int x;unsigned int y;cout <<"2 Zahlen > 0 eingeben :";cin >> x >> y;cout << "Der GGT von " << x << " und " << y << " ist ";while( x!= y) {

if(x > y) {x -= y;

}else {

y -= x;}

}cout << x << ’\n’;

}

Innerhalb einer Schleife muss es eine Veränderung derart geben, dass die Bedingungirgendwann einmal unwahr wird, sodass die Schleife abbricht (man sagt auch terminiert).Unbeabsichtigte »unendliche« Schleifen sind ein häufiger Programmierfehler. Im GGT-Beispiel ist leicht erkennbar, dass die Schleife irgendwann beendet sein muss:1. Bei jedem Durchlauf wird mindestens eine der beiden Zahlen kleiner.2. Die Zahl 0 kann nicht erreicht werden, da immer eine kleinere von einer größeren

Zahl subtrahiert wird. Die while-Bedingung schließt die Subtraktion gleich großerZahlen aus, und nur die könnte 0 ergeben.

Daraus allein ergibt sich, dass die Schleife beendet wird, und zwar in weniger als x Schrit-ten, wenn x die anfangs größere Zahl war. Im Allgemeinen sind es erheblich weniger, wieeine genauere Analyse ergibt.

TippDie Anweisungen zur Veränderung der Bedingung sollen an das Ende des Schleifenkör-pers gestellt werden, um sie leicht finden zu können.

Schleifen mit do while

Abbildung 1.9 zeigt die Syntax einer do while-Schleife. AnweisungOderBlock ist wie aufSeite 67 definiert. Die Anweisung oder der Block einer do while-Schleife wird ausgeführt,und erst anschließend wird die Bedingung geprüft. Ist sie wahr, wird die Anweisung einweiteres Mal ausgeführt usw. Die Anweisung wird also mindestens einmal ausgeführt.Im Flussdiagramm ist die Anweisung ein Block (siehe rechts in der Abbildung 1.10). dowhile-Schleifen eignen sich unter anderem gut zur sicheren Abfrage von Daten, indemdie Abfrage so lange wiederholt wird, bis die abgefragten Daten in einem plausiblenBereich liegen, wie im Primzahlprogramm unten zu sehen ist.

Page 77: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

76 1 Es geht los!

;Bedingung(AnweisungOderBlock whiledo )

Abbildung 1.9: Syntaxdiagramm einer do while-Schleife

Bedingung erfüllt?

Anweisung

ja

nein

do { Anweisung} while (Bedingung);

Abbildung 1.10: Flussdiagramm für eine do while-Anweisung

Es empfiehlt sich zur besseren Lesbarkeit, do while-Schleifen strukturiert zu schreiben.Die schließende geschweifte Klammer soll genau unter dem ersten Zeichen der Zeilestehen, die die öffnende geschweifte Klammer enthält. Dadurch und durch Einrücken desdazwischen stehenden Textes ist sofort der Schleifenkörper erkennbar, auch bei längerenProgrammen.

do {Anweisungen

} while(Bedingung);

Das direkt hinter die abschließende geschweifte Klammer geschriebene while macht un-mittelbar deutlich, dass dieses while zu einem do gehört. Das ist besonders wichtig, wennder Schleifenkörper in einer Programmliste über die Seitengrenze ragt. Eine do while-Schleife kann stets in eine while-Schleife umgeformt werden (und umgekehrt).

Listing 1.11: Beispiel für do while-Schleife

// cppbuch/k1/primzahl.cpp: Berechnen einer Primzahl, die auf eine gegebene Zahl folgt#include <iostream>#include <cmath>using namespace std;int main() {

// Mehrere, durch " getrennte Texte ergeben eine lange Zeile in der Ausgabe.cout << "Berechnung der ersten Primzahl, die >="

" der eingegebenen Zahl ist\n";long z;// do while-Schleife zur Eingabe und Plausibilitätskontrolledo { // Abfrage, solange z ≤ 3 ist

cout << "Zahl > 3 eingeben :";cin >> z;

} while(z <= 3);

Page 78: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 77

if(z % 2 == 0) { // Falls z gerade ist: nächste ungerade Zahl nehmen++z;

}bool gefunden {false};do {

// limit = Grenze, bis zu der gerechnet werden muss.// sqrt() arbeitet mit double, daher wird der Typ explizit umgewandelt.long limit {1 + static_cast<long>( sqrt(static_cast<double>(z)))};long rest;long teiler {1};do { // Kandidat z durch alle ungeraden Teiler dividieren

teiler += 2;rest = z % teiler;

} while(rest > 0 && teiler < limit);if(rest > 0 && teiler >= limit) {

gefunden = true;}else { // sonst nächste ungerade Zahl untersuchen:

z += 2;}

} while(!gefunden);cout << "Die nächste Primzahl ist " << z << ’\n’;

}

Schleifen mit for

Die letzte Art von Schleifen ist die for-Schleife. Sie wird häufig eingesetzt, wenn dieAnzahl der Wiederholungen vorher feststeht, aber das muss durchaus nicht so sein. Ab-bildung 1.11 zeigt die Syntax einer for-Schleife.

;Bedingung(

AnweisungOderBlock

for )VeränderungInitialisierung ;

Abbildung 1.11: Syntaxdiagramm einer for-Schleife

Der zu wiederholende Teil (Anweisung oder Block) wird auch Schleifenkörper genannt.Beispiel: ASCII-Tabelle im Bereich 65 ... 69 ausgeben

for(int i = 65; i <= 69; ++i) {cout << i << " " << static_cast<char>(i) << ’\n’;

}

Bei der Abarbeitung werden die folgenden Schritte durchlaufen:1. Durchführung der Initialisierung, zum Beispiel Startwert für eine Laufvariable fest-

legen. Eine Laufvariable wird wie i in der Beispielschleife als Zähler benutzt.2. Prüfen der Bedingung.3. Falls die Bedingung wahr ist, zuerst die Anweisung und dann die Veränderung aus-

führen.

Page 79: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

78 1 Es geht los!

Die Laufvariable i kann auch außerhalb der runden Klammern deklariert werden, diesgilt aber als schlechter Stil. Der Unterschied besteht darin, dass außerhalb der Klammerndeklarierte Laufvariablen noch über die Schleife hinaus gültig sind.

int i; // nicht empfohlenfor(i = 0; i < 100; ++i) {

// Programmcode, i ist hier bekannt}// i ist weiterhin bekannt ...

Im Fall der Deklaration innerhalb der runden Klammern bleibt die Gültigkeit auf denSchleifenkörper beschränkt:

for(int i = 0; i < 100; ++i) { // empfohlen// Programmcode, i ist hier bekannt

}// i ist hier nicht mehr bekannt

Die zweite, unten im Beispielprogramm verwendete Art erlaubt es, for-Schleifen alsselbstständige Programmteile hinzuzufügen oder zu entfernen, ohne Deklarationen inanderen Schleifen ändern zu müssen. Derselbe Mechanismus gilt für Deklarationen inden runden Klammern von if-, while- und switch-Anweisungen.

Listing 1.12: Beispiel für for-Schleife

// cppbuch/k1/fakultaet.cpp#include <iostream>using namespace std;

int main() {cout << "Fakultät berechnen. Zahl >= 0? :";unsigned int n;cin >> n;unsigned long fak {1L};for(unsigned int i = 2; i <= n; ++i) {

fak *= i;}cout << n << "! = " << fak << ’\n’;

}

Verändern Sie niemals die Laufvariable innerhalb des Schleifenkörpers! Das Auffindenvon Fehlern würde durch die Änderung erschwert.

for(int i = 65; i < 70; ++i) {// eine Seite Programmcode--i; // irgendwo dazwischen erzeugt eine unendliche Schleife// noch mehr Programmcode

}

Auch wenn der Schleifenkörper nur aus einer Anweisung besteht, wird empfohlen, ihnin geschweiften Klammern { } einzuschließen.

Page 80: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 79

Äquivalenz von for und while

Eine for-Schleife entspricht direkt einer while-Schleife, sie ist im Grunde nur eine Um-formulierung, solange nicht continue vorkommt (das im folgenden Abschnitt beschriebenwird):for(Initialisierung; Bedingung; Veraenderung)

Anweisung

ist äquivalent zu:

{Initialisierung;while(Bedingung) {

AnweisungVeraenderung;

}}

Die äußeren Klammern sorgen dafür, dass in der Initialisierung deklarierte Variablenwie bei der for-Schleife nach dem Ende nicht mehr gültig sind. Anweisung kann wieimmer auch eine Verbundanweisung (Block) sein, in der mehrere Anweisungen stehenkönnen, durch geschweifte Klammern begrenzt. Die umformulierte Entsprechung desobigen Beispiels (ASCII-Tabelle von 65 ... 69 ausgeben) lautet:

{int i {65}; // Initialisierungwhile(i < 70) { // Bedingung

cout << i << " " << static_cast<char>(i) << ’\n’; // Anweisung++i; // Veränderung

}}

float- oder double-Laufvariablen vermeiden!

Wegen der Rechenungenauigkeit kann es bei nicht-integralen Typen wie double oderfloat zu nicht vorhersagbarem Verhalten kommen. Das Beispiel:

for(double d = 0.4; d <= 1.2; d += 0.4) {cout << d << ’\n’;

}

lässt auf den ersten Blick die Ausgabe 0.4, 0.8, 1.2 erwarten, tatsächlich werden aufmeinem System nur die zwei Zahlen 0.4 und 0.8 ausgegeben. Wenn ich jedoch

for(double d = 0.5; d <= 1.5; d += 0.5) {cout << d << ’\n’;

}

ausführe, werden wie erwartet die drei Zahlen 0.5, 1 und 1.5 angezeigt. Der Grund liegtdarin, dass 0.5 intern exakt darstellbar ist (d.h. im Binärsystem), 0.4 jedoch nicht. Schonein Unterschied im letzten Bit lässt den Vergleich auf Gleichheit scheitern. Ganz ungüns-tig kann sich die Prüfung auf Ungleichheit mit != auswirken:

Page 81: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

80 1 Es geht los!

for(float f = 0.4; f != 10.4; ++f) { //∞-Schleifecout << f << ’\n’;

}

Abgesehen von möglichen arithmetischen Problemen wird in der letzten Schleife diefloat-Variable f mit der double-Konstante 10.4 verglichen. Um den Vergleich durchfüh-ren zu können, bringt der Compiler beide Größen auf dieselbe Bitbreite, das heißt, erinterpretiert den Vergleich als (double)f != 10.4. Die im Vergleich zu double wenigenBits der float-Zahl reichen nicht für die erforderliche Genauigkeit aus, wie die folgendenZeilen zeigen:

cout.setf(ios::showpoint|ios::fixed, ios::floatfield); // Formatierung siehe Kapitel 9cout.precision(15); // anzuzeigende Genauigkeit

// Ausgabe auf meinem System:cout << 10.4 << ’\n’; // 10.400000000000000cout << (double)10.4f << ’\n’; // 10.399999618530273

Wenn Sie bei double-Laufvariablen nur die Operatoren < oder > zum Vergleich verwen-den, ist das Problem zum Teil entschärft. Aber die Anzahl der Schleifendurchläufe bleibtmöglicherweise undefiniert: Nur eine sehr geringe Abweichung im Wert der Variablenoder der begrenzenden Konstanten entscheidet, ob der Schleifenkörper einmal mehr aus-geführt wird oder nicht. Also: Verwenden Sie nur integrale Datentypen wie int, size_toder char als Laufvariable in Schleifen!

Kommaoperator

Der Kommaoperator wird gelegentlich in den Bestandteilen Initialisierung; Bedingung;

Veraenderung einer for-Schleife benutzt, um die Schleife kompakter zu schreiben, meis-tens mit dem Ergebnis einer schlechteren Lesbarkeit. Er gibt eine Reihenfolge von linksnach rechts vor. Das folgende Programmstück summiert die Zahlen 1 bis 100:

int sum {0};for(int i = 1; i <= 100; ++i) {

sum += i;}

Mit Hilfe des Kommaoperators wird der Schleifenkörper in den Veränderungsteil verlegt.Sowohl i als auch sum bekommen im Initialisierungsteil ihre Anfangswerte zugewiesen:

int sum;for(int i = 1, sum = 0; i <= 100; sum += i, ++i);

Theoretisch könnte auch die Deklaration von sum in den Initialisierungsteil verlegt wer-den, nur kommt man dann außerhalb der Schleife nicht mehr an den Wert der Variablen.Der Kommaoperator hat die niedrigste Priorität von allen Operatoren. (siehe Tabelle A.4auf Seite 937).

1.8.6 Kontrolle mit break und continuebreak und continue sind Sprunganweisungen. Bisher wurde break in der switch-Anwei-sung verwendet, um sie zu verlassen. break wirkt genauso in einer Schleife, das heißt,dass die Schleife beim Erreichen von break beendet wird. continue hingegen überspringt

Page 82: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.8 Kontrollstrukturen 81

den Rest des Schleifenkörpers. In einer while- oder do while-Schleife würde als Nächstesdie Bedingung geprüft werden, um davon abhängig die Schleife am Beginn des Schlei-fenrumpfs fortzusetzen. Das folgende kleine Menü-Programm zeigt break und continue.

Listing 1.13: Menü mit break und continue

// cppbuch/k1/menu.cpp#include <iostream>using namespace std;int main() {while (true) { // unendliche Schleife

char c;cout << "Wählen Sie: a, b, x = Ende : ";cin >> c;

if (c == ’a’) {cout << "Programm a\n";continue; // zurück zur Auswahl

}

if (c == ’b’) {cout << "Programm b\n";continue; // zurück zur Auswahl

}

if (c == ’x’) {break; // Schleife verlassen

}cout << "Falsche Eingabe! Bitte wiederholen!\n";

}cout << "\n Programmende mit break\n";

}

In einer for-Schleife würde als Nächstes die Veränderung ausgeführt und dann erstdie Bedingung erneut geprüft. Insofern stimmt die oben erwähnte Äquivalenz der for-Schleife mit einer while-Schleife exakt nur für for-Schleifen ohne continue. Ohne break

und continue wären gegebenenfalls viele if-Abfragen notwendig, die die Lesbarkeit ei-nes Programms verschlechtern.In einem größeren Programm können viele verteilte break- oder continue-Anweisungendie Verständlichkeit beeinträchtigen. Deshalb gibt es die Meinung, dass jeder Block nureinen einzigen Einstiegs- und einen einzigen Ausstiegspunkt haben soll (englisch singleentry/ single exit). Um dies zu erreichen, wird eine Hilfsvariable eingeführt, die denAbbruch signalisiert (siehe Übungsaufgabe 1.9).Eine Alternative besteht darin, break-Anweisungen mit einem deutlichen Kommentarwie zum Beispiel // EXIT! am rechten Rand zu kennzeichnen. Wenn eine Schleife mitbreak nur wenige Zeilen umfasst, trägt eine Hilfsvariable nicht zur Übersichtlichkeit bei.

Übungen1.5 Schreiben Sie eine Schleife, die eine gegebene Zahl binär ausgibt, indem Sie mit ge-eigneten Bit-Operationen prüfen, welche Bits der Zahl gesetzt sind. Tipp: Verwenden Sie

Page 83: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

82 1 Es geht los!

die Zahl 1, verschoben um 0 bis z.B. 31 Bit-Positionen, als »Maske«. Mögliche Ergebnissekönnen sein:5→ 00000000000000000000000000000101

-5→ 11111111111111111111111111111011

Es ist zu sehen, dass -5 intern als Zweierkomplement von 5 dargestellt wird.1.6 Welche Fallstricke sind in den folgenden Schleifen verborgen? Dabei soll auch daraufgeachtet werden, unter welchen Umständen die Schleifen terminieren (sich beenden).a) while(i > 0)

k = 2 * k;

b) while(i != 0)

i = i - 2;

c) while(n != i) {

++i;

n = 2 * i;

}

1.7 Fünf Personen haben versucht, die Summe der Zahlen von 1 bis 100 zu berechnen.Beurteilen Sie die folgenden Lösungsvorschläge:(a) int n = 1, sum = 0; (d) int n {1};

while(n <= 100) { while(n < 100) {

++n; int sum = 0;

sum += n; n = n + 1;

} sum = sum + n;

}

(b) int n = 1, sum = 1;

while(n < 100) (e) int n = 1, sum = 0;

n += 1; while(n <= 0100) {

sum += n; sum += n;

++n;

(c) int n {100}; }

int sum {n*(n+1)/2};

1.8 Berechnen Sie die Summe aller natürlichen Zahlen von n1 bis n2 mita) einer for-Schleife,b) einer while-Schleife,c) einer do while-Schleife,d) ohne Schleife.Es sei n2 ≥ n1 vorausgesetzt. Tipp: Erst die vorstehende Aufgabe lösen.1.9 Formulieren Sie das Programm in Abschnitt 1.8.6 um, sodass ein funktionsäquiva-lentes Programm ohne continue in der Schleife entsteht. Anstelle der if-Anweisungensoll eine switch-Anweisung eingesetzt werden, um das Programm zu verkürzen.

Page 84: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 83

1.9 Benutzerdefinierte undzusammengesetzte Datentypen

Außer den Grunddatentypen gibt es Datentypen, die aus den Grunddatentypen zusam-mengesetzt sind und die Sie selbst definieren können (benutzerdefinierte Datentypen).Darüber hinaus gibt es zusammengesetzte Datentypen in der C++-Bibliothek, von denenhier die sehr gebräuchlichen Datentypen vector und string beschrieben werden.

1.9.1 AufzählungstypenHäufig gibt es nicht-numerische Wertebereiche. So kann zum Beispiel ein Wochentagnur die Werte Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag und Samstagannehmen. Oder ein Farbwert in einem C++-Programm soll nur den vier Farben Rot,Grün, Blau, und Gelb entsprechen. Eine mögliche Hilfskonstruktion wäre die Abbildungauf den Datentyp int:

int eineFarbe; // rot = 0// grün = 1// blau = 2// gelb = 3

int einWochentag; // Sonntag = 0// Montag = 1 usw.

Dieses Verfahren hätte einige Nachteile:Die Bedeutung muss im Programm als Kommentar festgehalten werden.Zugeordnete Zahlen sind nicht eindeutig: 0 kann rot, aber auch Sonntag bedeuten,und 1 kann für grün oder auch Montag stehen.Schlechter Dokumentationswert, zum Beispiel if(eineFarbe == 2) ...

Hieraus ist nicht zu ersehen, welche Farbe gemeint ist. Oder:if(eineFarbe == 5) ... Der Wert 5 ist undefiniert!

Die Lösung für solche Fälle sind die Aufzählungs- oder Enumerationstypen, die eineErweiterung der vordefinierten Typen durch den Programmierer darstellen. Abbildung1.12 zeigt das etwas vereinfachte8 Syntaxdiagramm einer enum-Deklaration.

}Typname { Variablenlisteclass Werteliste ;enum

Abbildung 1.12: Vereinfachtes Syntaxdiagramm einer enum-Deklaration

Typname oder Variablenliste können weggelassen werden (in diesem Fall aber nicht bei-de). Sinnvoll ist meistens nur das Weglassen der Variablenliste. class wegzulassen istmöglich, wird aber nicht empfohlen (siehe unten). Der neue Datentyp Farbtyp wird de-klariert:

8 Der (integrale) Typ der Werte in der Werteliste kann festgelegt werden, auch ist struct statt classmöglich.

Page 85: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

84 1 Es geht los!

enum class Farbtyp {rot, gruen, blau, gelb};

Der neue Datentyp Wochentag wird deklariert:

enum class Wochentag {sonntag, montag, dienstag, mittwoch,donnerstag, freitag, samstag};

Wenn der Datentyp erst einmal bekannt ist, können weitere Variablen definiert werden:

Farbtyp gewaehlteFarbe; // DefinitionWochentag derFeiertag, einWerktag, // Definitionen

heute = dienstag; // Definition + Initialisierung

Falls ein Aufzählungstyp nur ein einziges Mal in einer Variablendefinition benötigt wird,kann der Typname weggelassen werden. Das Ergebnis ist eine anonyme Typdefinition:

enum class {fahrrad, mofa, lkw, pkw} einFahrzeug;

Den mithilfe von Aufzählungstypen definierten Variablen können ausschließlich Werteaus der zugehörigen Liste zugewiesen werden, Mischungen sind nicht erlaubt. Aufzäh-lungstypen sind eigene Datentypen, werden aber intern auf die natürlichen Zahlen ab-gebildet, beginnend bei 0. Eine Voreinstellung mit anderen Zahlen ist möglich, wird abernur gelegentlich erforderlich sein, vielleicht bei einer gewünschten binären Codierung,zum Beispiel

// Abweichung von der Standardeinstellung 0, 1, 2, 3 ...:// Deklaration:enum class Farbtyp {rot = 0, gruen = 1, blau = 2, gelb = 4};

// Deklaration mit Variablendefinition:enum class Palette { weiss = 0, grau = 1, braun = 2, amber = 4, lila = 8

} mischung;

// Deklaration mit Bitshift-Operator (hier ohne class):enum Bitmaske { wert1 = 1 << 0, wert2 = 1 << 1, wert3 = 1 << 2,

wert4 = 1 << 3, wert5 = 1 << 4, wert6 = 1 << 5};

Die in Farbtyp definierten Farben rot, gruen, blau, gelb dürfen in Palette nicht mehrverwendet werden, wenn class fehlt und sich Palette im gleichen Gültigkeitsbereichbefindet. Eine Umwandlung in int ist erlaubt, aber nur mit dem static_cast-Operator.Nicht erlaubt ist die Umwandlung einer int-Zahl in einen enum-Typ. Welche Anweisun-gen möglich oder falsch sind, zeigen die nächsten Zeilen, wobei die Variablennamen sichauf die obigen Definitionen beziehen:

Farbtyp gewaehlteFarbe; // DefinitionWochentag derFeiertag, einWerktag, // Definitionenheute = Wochentag::dienstag; // Definition + Initialisierungint j = Wochentag::dienstag; // Fehler! Typ nicht kompatibelint i = static_cast<int>(Wochentag::dienstag); // jetzt okheute = Wochentag::montag; // richtigheute = i; // Fehler, Datentyp inkompatibelheute = static_cast<Wochentag>(i); // erlaubt, aber...// Falls die Variable i einen Wert hat, der nicht einem der Werte des// Aufzählungstyps entspricht, ist der Wert der Variablen heute undefiniert!

Page 86: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 85

montag = heute; // Fehler (montag ist Konstante)++mischung; // Fehler, undefinierter Operatorif(mischung > Palette::grau) // erlaubter Vergleich

mischung = Palette::lila; // richtig

class weglassen?

Das Schlüsselwort class bei enum-Deklarationen wurde 2011 eingeführt. Es wegzulas-sen ist weiterhin gültiges C++, nur ist die Typsicherheit eingeschränkt, weil impliziteUmwandlungen nach int möglich sind. Ein Beispiel:

enum Farbtyp { rot, gruen, blau, gelb }; // ohne classint i = rot + blau; // nun möglich

Außer der impliziten Umwandlung von rot und blau nach int fällt auf, dass die Typqua-lifizierung Farbtyp:: fehlen kann.

TippWegen der erhöhten Typsicherheit wird empfohlen, enum class zu verwenden und nur inbegründeten Ausnahmefällen darauf zu verzichten.

1.9.2 StrukturenHäufig möchte man logisch zusammengehörige Daten zusammenfassen, die nicht vomselben Datentyp sind, zum Beispiel die zusammengehörigen Daten eines Punktes aufdem Bildschirm, also seine Koordinaten x und y, seine Farbe, und ob er sichtbar ist. Fürdiesen Zweck stellt C++ die Struktur bereit. Sie fasst einen Datensatz zusammen. EineStruktur im Sinne dieses Abschnitts enthält ausschließlich Daten (und keine Funktionen,was auch möglich wäre). Strukturen sind benutzerdefinierte Datentypen. Sie werden miteiner Syntax definiert, die bis auf den inneren Teil der Syntax von Aufzählungstypenähnelt (siehe Abbildung 1.13).

}Typname { Variablenlistestruct Elemente ;

Abbildung 1.13: Syntaxdiagramm einer struct-Definition

In einem Grafikprogramm gehören zu jedem Punkt auf dem Bildschirm verschiedeneDaten wie die Koordinaten in x- und y-Richtung, die Farbe und die Information, ob ergerade sichtbar ist. Alle diese logisch zusammengehörenden Daten werden in der StrukturPunkt zusammengefasst. Die strukturinternen Daten heißen Elemente (auch Felder oderKomponenten). Im Beispiel werden zwei Variablen p und q vom Datentyp Punkt definiertund der Zugriff auf die internen Größen gezeigt.

enum class Farbtyp {rot, gelb, gruen};

struct Punkt { // Punkt ist ein Typ.int x;int y;

Page 87: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

86 1 Es geht los!

bool istSichtbar;Farbtyp dieFarbe;

} p; // p ist ein Punkt-Objekt

// q ist ebenfalls ein Punkt-Objekt:Punkt q; // noch undefinierter Inhalt// Zugriff, hier: mit Werten versehenp.x = 270; p.y = 20; // Koordinaten von pp.istSichtbar = false;p.dieFarbe = Farbtyp::gelb;

Die internen Elemente sind nicht allein zugreifbar, weil sie nur in Verbindung mit einemObjekt existieren. Die Anweisung dieFarbe = Farbtyp::rot; ist unsinnig, weil nicht klarist, welcher Punkt rot werden soll. p.dieFarbe = Farbtyp::rot; hingegen sagt eindeutig,dass der Punkt p gefärbt werden soll. Der Zugriff geschieht über einen Punktoperator ».«zwischen Variablen- und Elementnamen. Ein struct kann in einem Schritt initialisiertwerden, wie das folgende Programm zeigt. Bei der Ausgabe wird der bool-Wert false in0 umgewandelt, und der enum-Wert gelb in 1 (rot ergäbe 0).

Listing 1.14: Beispiel mit struct

// cppbuch/k1/struct.cpp#include <iostream>using namespace std;

enum class Farbtyp { rot, gelb, gruen };

struct Punkt {int x;int y;bool istSichtbar;Farbtyp dieFarbe;

};

int main() {Punkt p1 { 100, 200, false, Farbtyp::gelb }; // direkte Initialisierungcout << "p1.x = " << p1.x << " p1.y = " << p1.y

<< " p1.istSichtbar= " << p1.istSichtbar<< " p1.dieFarbe= " << static_cast<int>(p1.dieFarbe) << ’\n’;

}

Übung

1.10 Schreiben Sie eine Struktur (struct) namens Person, die Vorname, Nachname undAlter einer Person enthält. Vorname und Nachname seien vom Typ string, Alter vomTyp int. Verwenden Sie diese Struktur in einem Programm so, dass den Elementen derStruktur Werte mit cin (Eingabe über die Tastatur) zugewiesen werden. Anschließendsollen die Elemente auf dem Bildschirm ausgegeben werden.

Page 88: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 87

1.9.3 Der C++-Standardtyp vectorIm täglichen Leben benutzen wir häufig Tabellen, von Programmierern Arrays (engl.für Reihe oder Feld) genannt. Einspaltige Tabellen, und um die geht es hier zunächst,werden in C++ durch eine Vektor genannte Konstruktion abgebildet. Aus der SpracheC sind primitive Arrays bekannt. Diese sind zwar auch Bestandteil von C++, sind abernicht besonders komfortabel und werden daher erst in Abschnitt 4.2 behandelt.Ein Vektor ist eine Tabelle von Elementen desselben Datentyps, also eine Tabelle zum Bei-spiel nur mit ganzen Zahlen oder nur mit double-Zahlen. Mit Ausnahme von Referenzenkann der Datentyp beliebig sein, insbesondere kann er selbst wieder zusammengesetztsein. Auf ein Element der Tabelle wird über die Positionsangabe zugegriffen, also überdie Nummer der Reihe, in der sich das Element befindet.In C++ ist ein Vektor eine vordefinierte Klasse, um deren internen Aufbau wir uns erstspäter kümmern. Zunächst geht es nur um die Benutzung als Baustein in eigenen Pro-grammen. Es kommen dabei zwei Sichtweisen zum Ausdruck:1. »Bausteine« werden benutzt, um Programme oder Programmteile zu entwickeln, die

selbst wieder Bausteine sein können. Dazu ist die Kenntnis notwendig, was ein Bau-stein leistet und wie er verwendet werden muss, aber nicht wie er intern funktioniert.

2. »Bausteine« sollen von Grund auf entwickelt oder weiterentwickelt werden. Dann isteine gründliche Kenntnis der inneren Funktion unerlässlich.

Für Softwareentwickler sind beide Sichten wichtig. Hier wird der Standardtyp vector

zunächst nur benutzt, und erst viel weiter unten wird erklärt, was im Innern vorgeht. DieAnweisung

vector<int> v(10); // runde Klammern

stellt einen Vektor v bereit, der aus 10 Elementen des Typs int besteht. Alle Werte sind0. Die Feldelemente sind stets von 0 bis (Anzahl der Elemente –1) durchnummeriert, hieralso von 0 bis 9. Mit geschweiften Klammern können die Elemente des Vektors direktangegeben werden:

vector<int> v1 {}; // leerer Vektorvector<int> v2 {7, 0, 9}; // Vektor mit den Elementen 7, 0, 9

Die Klasse für Vektoren stellt einige Dienstleistungen zur Verfügung, die mit der in derEinführung beschriebenen Notation Objektname.Anweisung(gegebenenfalls Daten) ab-gerufen werden können. Eine dieser Dienstleistungen ist die Ermittlung der Zahl derElemente, das heißt die Größe (englisch size) des Vektors:

cout << v.size() << ’\n’; // 10cout << v1.size() << ’\n’; // 0cout << v2.size() << ’\n’; // 3

Daten müssen in diesem Fall nicht zwischen den runden Klammern übergeben werden,das Vektor-Objekt enthält alle nötigen Informationen. Der Zugriff auf ein spezielles Ele-ment wird mit dem Indexoperator [] realisiert. Zum Beispiel zeigt

cout << v[0] << ’\n’; // Zählung beginnt bei 0

das erste Element des Vektors an. Der zwischen den eckigen Klammern stehende Wertheißt Index. Zugriffe auf Vektor-Elemente kommen typischerweise in Schleifen vor, weil

Page 89: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

88 1 Es geht los!

meistens eine Operation für die gesamte Tabelle ausgeführt werden soll. Dabei ist sorg-fältig darauf zu achten, dass die Indexwerte die Vektorgrenzen nicht unter- oder über-schreiten.

Vorsicht Falle!

Es gibt keine Überprüfung der Bereichsüber- oder -unterschreitung! Zugriffe auf nichtexistierende Elemente wie c = v[100] erzeugen keine Fehlermeldung, weder durch denCompiler noch zur Laufzeit, sofern nicht der zulässige Speicherbereich für das Programmüberschritten wird! Der Grund: Schnelligkeit ist ein Designprinzip von C++, und dieÜberprüfung eines jeden Zugriffs kostet eben Zeit.

Umgehen der Falle

Man kann auf die eckigen Klammern verzichten und einen Vektor nach einem Wert an(englisch at) einer Position fragen. Diese Art des Zugriff wird geprüft:

cout << v.at(0) << ’\n’; // alles bestens, dasselbe wie v[0]// 1000 ist zuviel!cout << v.at(1000) << ’\n’; // Programmabbruch mit Fehlermeldung

Im Beispiel unten wird dafür gesorgt, dass der Index niemals einen falschen Wert habenkann – dies ist sowieso ein besseres Verfahren, als das Programm zu korrigieren, nachdemdas Kind in den Brunnen gefallen ist. Der laufende Index wird einfach mit v.size()

verglichen. v.size() gibt die Anzahl der Elemente als nicht-vorzeichenbehafteten Wertzurück. Natürlich muss in der for-Schleife i < v.size() abgefragt werden anstatt i <=

v.size()... Wer sich leicht vertippt, schreibt also besser v.at(i) statt v[i].

Ein Beispiel

Das Programm unten verdeutlicht die Arbeitsweise mit Vektoren. Es werden einige typi-sche Operationen demonstriert, die sich weitgehend selbst erklären.

Listing 1.15: Standardklasse vector

// cppbuch/k1/vektor.cpp#include <iostream>#include <vector> // Standard-Vektor bekannt machen#include <algorithm> // enthält Sortierfunktion sort()#include <cstddef> // size_tusing namespace std;

int main() { // Programm mit typischen Vektor-Operationenvector<float> kosten(12); // Tabelle mit 12 float-Werten

// Füllen der Tabelle mit beliebigen Daten, dabei Typumwandlung int→ floatfor(size_t i = 0; i < kosten.size(); ++i) {

kosten[i] = static_cast<float>(150-i*i)/10.0f;}

// Als Typ der Laufvariablen i in der Schleife wird der auf Seite 48 beschriebene Typ// size_t statt int gewählt, weil die Anzahl der Elemente ja ≥ 0 sein muss. Tabelle ausgeben:for(size_t i = 0; i < kosten.size(); ++i) {

Page 90: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 89

cout << i << ": " << kosten[i] << ’\n’;}// Berechnung und Anzeige von Summe und Mittelwertfloat sum = 0.0;for(size_t i = 0; i < kosten.size(); ++i) {

sum += kosten[i];}cout << "Summe = " << sum << ’\n’;cout << "Mittelwert = "

<< sum/kosten.size() // implizite Typumwandlung des Nenners nach float<< ’\n’;

// Maximum anzeigenfloat maxi {kosten[0]};for(size_t i = 1; i < kosten.size(); ++i) {

if(maxi < kosten[i])maxi = kosten[i];

}cout << "Maximum = " << maxi << ’\n’;

// zweite Tabelle sortierteKosten deklarieren und mit der ersten initialisierenvector<float> sortierteKosten {kosten};

// zweite Tabelle aufsteigend sortieren (siehe Erläuterung unten)sort(sortierteKosten.begin(), sortierteKosten.end());

// und mit der laufenden Nummer ausgebenfor(size_t i = 0; i < sortierteKosten.size(); ++i) {

cout << i << ": " << sortierteKosten[i] << ’\n’;}// Kurzform, wenn die lfd. Nummer nicht gebraucht wird// Einzelheiten dazu folgen bald in den Abschnitten 1.9.5 und 1.9.6for(auto diekosten : sortierteKosten) {

cout << diekosten << ’\n’;}

}

Die Bibliotheksfunktion sort() ist sehr schnell. Sie verlangt als Eingabe den Anfangdes Vektors, gegeben durch begin(), und die Position nach dem letzten Vektorelement,gegeben durch end(). Diese Art der Bereichsangabe ist in C++ üblich und wird nochhäufig verwendet werden. An der Stelle

vector<float> sortierteKosten {kosten}; // Objekt anlegen und initialisieren

wäre auch Folgendes möglich gewesen:

vector<float> sortierteKosten; // Objekt anlegensortierteKosten = kosten; // Zuweisung

Wenn man die Wahl hat so wie hier, soll stets der ersten Variante der Vorzug gegebenwerden, weil das Initialisieren während der Objekterzeugung schneller vonstatten geht,als erst das Objekt zu erzeugen und dann im zweiten Schritt die Zuweisung der Wertevorzunehmen.

Page 91: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

90 1 Es geht los!

Lineare Suche in Tabellen

Hier seien vier programmiertechnische Möglichkeiten gezeigt, in einer unsortierten Ta-belle tabelle mit N Elementen auf den Positionen 0...N – 1 ein bestimmtes Element keyzu suchen. Die C++-Standardbibliothek bietet die find()-Funktion, aber hier sollen pro-grammiertechnisch verschiedene Schleifenvarianten verglichen werden. Die Variable i

gibt anschließend die Position an, an der das gesuchte Element key erstmalig auftritt.Falls key nicht in tabelle enthalten ist, muss i einen Wert außerhalb 0..N – 1 annehmen.Die folgenden Algorithmen enden bei erfolgloser Suche mit i = N . Die letzte Varianteerlaubt eine kürzere Formulierung der Schleife, setzt aber voraus, dass das Feld um einenEintrag erweitert wird, der als »Wächter« (englisch sentinel) für den Abbruch der Schleifedient.

// Definitionen für die nächsten Fälleconst int N = ...vector<int> tabelle(N);int key = ... // gesuchtes Elementint i; // Laufvariable. Ergebnis: i = 0..N – 1 : gefunden, i = N : nicht gefunden!

1. while-SchleifeIn der Bedingung wird abgefragt, ob die Zählvariable noch im gültigen Bereich unddas aktuelle Element ungleich key ist. So lange wird die Zählvariable inkrementiert.

i = 0;while(i < N && tabelle[i] != key) {

++i;}

2. do while-SchleifeWie oben, nur dass die Zählvariable vorher inkrementiert und daher anders vorbesetztwird. Die vorhergehende Lösung soll im Vergleich bevorzugt werden, weil es generellbesser ist, eine Bedingung zu prüfen und dann zu handeln als umgekehrt.

i = -1;do {++i;

} while(i < N && tabelle[i] != key);

3. for-SchleifeDie Schleife wird mit break verlassen, wenn das Element gefunden wird. Es wäreauch möglich gewesen, die Bedingung i < N zu erweitern.

for(i = 0; i < N; ++i) {if(tabelle[i] == key) {

break;}

}

4. (N+1). Element als »Wächter« (sentinel)Achtung! Die Definition muss hier das zusätzliche Element berücksichtigen, sodassdie Tabelle (N+1) statt N Elemente enthält:

vector<int> tabelle(N+1); // letztes Element nur für diesen Fall

Page 92: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 91

In das zusätzliche Element tabelle[N] wird key eingetragen. Die Schleife muss spä-testens hier abbrechen, auch wenn key vorher nicht gefunden wurde. Die Zählvariablewird als Seiteneffekt beim Zugriff auf ein Vektorelement hochgezählt.

i = -1;tabelle[N] = key; // garantiert Abbruch der Schleifewhile(tabelle[++i] != key);

Vektoren sind dynamisch!

Oft ist unklar, wie groß ein Vektor sein soll, zum Beispiel beim Einlesen von Daten perDialog oder aus einer Datei unbekannter Größe. Ein Vektor der C++-Standardbibliothekhat den Vorteil, dass er bei Bedarf Elemente hinten anhängt und dabei seine Größeändert. Falls »hinten« kein Platz mehr im Computerspeicher sein sollte, wird der gesamteVektor an eine neue, ausreichend große Stelle im Speicher verlagert. Dies geschieht ohneZutun des Programmierers. Mit push_back wird der Vektor dazu aufgefordert, ein Elementanzufügen, wobei ihm der anzuhängende Wert in runden Klammern übergeben wird(siehe Beispielprogramm). Eine tabellarische Übersicht der Möglichkeiten von Objektender Klasse vector ist in Abschnitt 27.2.1 zu finden. Ein Großteil der Möglichkeiten istaber erst nach Kenntnis der folgenden Kapitel bis einschließlich Kapitel 8 verständlich.

Listing 1.16: Vektor dynamisch vergrößern

// cppbuch/k1/dynvekt.cpp#include <iostream>#include <vector> // Standard-Vektor#include <cstddef> // size_tusing namespace std;

int main() {vector<int> meineDaten; // anfängliche Größe ist 0int wert;do {

cout << "Wert eingeben (0 = Ende der Eingabe):";cin >> wert;if(wert != 0) {

meineDaten.push_back(wert); // Wert anhängen}

} while(wert != 0);cout << "Es wurden die folgenden Werte eingegeben:\n";for(size_t i = 0; i < meineDaten.size(); ++i) {

cout << i << ". Wert : " << meineDaten[i] << ’\n’;}

}

1.9.4 Zeichenketten: Der C++-Standardtyp stringEine Zeichenkette, auch String genannt, ist aus Zeichen des Typs char zusammengesetzt.Im Grunde kann eine Zeichenkette wie eine horizontale Tabelle mit nur einer Reihe aufge-fasst werden. Dennoch wird nicht ein vector<char>, sondern eine andere Standardklassemit dem Namen string als Baustein verwendet, ohne dass wir uns um ihre Innereien

Page 93: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

92 1 Es geht los!

kümmern – jedenfalls jetzt noch nicht. Die Klasse hat gegenüber dem uns bekanntenVektor einige zusätzliche Eigenschaften, von denen eine Auswahl im folgenden Pro-gramm beispielhaft gezeigt werden soll. Eine tabellarische Übersicht der Möglichkeitenvon Strings ist in Kapitel 31 zu finden.

Listing 1.17: Standardklasse string

// cppbuch/k1/zbstring.cpp#include <iostream>#include <string> // Standard-String einschließen#include <cstddef> // size_tusing namespace std;

int main() { // Programm mit typischen String-Operationen// String-Objekt einString anlegen und mit "hallo" initialisieren.string einString {"hallo"}; // einString kann ein beliebiger Name sein.cout << einString << ’\n’; // String ausgeben

// Beim Vektor wäre stattdessen für die Ausgabe// eine Schleife notwendig, etwa der folgenden Art:// String zeichenweise ausgeben, ungeprüfter Zugriff wie bei vector:for(size_t i = 0; i < einString.size(); ++i) {

cout << einString[i];}cout << ’\n’;

// String zeichenweise mit Indexprüfung ausgeben. Die Anzahl der// Zeichen kann bei Strings auch mit length() ermittelt werden.for(size_t i = 0; i < einString.length(); ++i) {

cout << einString.at(i);}cout << ’\n’;// Die Prüfung geschieht wie beim Vektor. Ein Versuch, einString.at(i) mit// i ≥ einString.size(i) abzufragen, führt zum Programmabbruch mit Fehlermeldung.

string eineStringKopie(einString); // Kopie des Strings einString erzeugencout << eineStringKopie << ’\n’; // hallostring diesIstNeu {"neu!"}; // Kopie durch ZuweisungeineStringKopie = diesIstNeu;cout << eineStringKopie << ’\n’; // neu!eineStringKopie = "Buchstaben"; // Zuweisung einer Zeichenkettecout << eineStringKopie << ’\n’; // BuchstabeneinString = ’X’; // Zuweisung nur eines Zeichens vom Typ charcout << einString << ’\n’; // XeinString += eineStringKopie; // Strings mit dem +=-Operator verkettencout << einString << ’\n’; // XBuchstabeneinString = eineStringKopie + " ABC"; // Strings mit dem +-Operator verkettencout << einString<< ’\n’; // Buchstaben ABCeinString = "123" + eineStringKopie;cout << einString << ’\n’; // 123Buchstaben// einString = "123"+ "ABC"; geht nicht! Erklärung folgt in Kapitel 8einString = string("123") + "ABC"; // ok!

Page 94: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 93

// Vergleich von Stringsstring a{"Albert"};string z{"Alberta"};if(a < z) {

cout << a << " < " << z << ’\n’;}if(z > a) {

cout << z << " > " << a << ’\n’;}

} // Ende von main()

Übungen1.11 Gegeben sei eine Zeichenkette des Typs string, die eine natürliche Zahl darstellensoll und daher nur aus Ziffern besteht. Beispiel: "17462309".a) Wandeln Sie den String in eine Zahl z vom Typ long um.b) Berechnen Sie die Quersumme von z.Geben Sie die Zahl und die Quersumme auf dem Bildschirm aus.1.12 Schreiben Sie ein Programm, das eine einzugebende natürliche Zahl in römischerDarstellung ausgibt. Die römischen Ziffern seien in einem konstanten String ZEICHEN-

VORRAT = "IVXLCDM" gegeben. Die syntaktische Regel lautet: Keine Ziffer außer ’M’ darfmehr als dreimal hintereinanderstehen. Das heißt, ein vierfaches Vorkommen wird durchSubtraktion vom nächsthöheren passenden Wert ersetzt. Subtraktion geschieht durchVoranstellen des kleineren Werts. So wird 4 nicht zu IIII, sondern zu IV, und 9 wird nichtzu VIIII, sondern zu IX. (Etwas schwierig und nur für Knobelfreunde!)1.13 Schreiben Sie ein Programm, das beliebig viele Zahlen im Bereich von -99 bis +100(einschließlich) von der Standardeingabe liest. Der Zahlenbereich sei in 10 gleich großeIntervalle eingeteilt. Sobald eine Zahl außerhalb des Bereichs eingegeben wird, sei dieEingabe beendet. Das Programm soll dann für jedes Intervall ausgeben, wie viele Zah-len eingegeben worden sind. Benutzen Sie für -99, +100 usw. Konstanten (const). ZurSpeicherung der Intervalle soll ein vector<int> verwendet werden.1.14 Das folgende Problem ist klassisch, und es haben sich schon viele Menschen damitbeschäftigt: Wenn Zahlen Achterbahn fahren. Gegeben sei eine natürliche Zahl > 0.1. Wenn die Zahl gerade ist, teile sie durch 2. Wenn nicht, multipliziere sie mit 3 und

addiere 1.2. Wenn die sich ergebende Zahl größer als 1 ist, wende Schritt 1 auf diese Zahl an.

Wenn nicht, ist das Verfahren beendet.Es zeigt sich, dass die Zahlen erheblich anwachsen können und auch wieder kleiner wer-den – daher der Name Achterbahn. Schreiben Sie ein Programm, das eine Startzahl alsEingabe erwartet und den obigen Algorithmus durchführt. Lassen Sie sich die erreichteZahl und das erreichte Maximum anzeigen. Am Ende des Programms soll ausgegebenwerden, wieviele Iterationen (Durchläufe der Schleife) bis zum Ende des Programms be-nötigt werden. Mit den Anweisungen

string dummy;getline(cin, dummy); // weiter mit Tastendruck

Page 95: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

94 1 Es geht los!

können Sie die Ausgabe nach Erreichen eines neuen Höchstwertes anhalten. VersuchenSie die Startzahlen 4096, 142587, 1501353. Bei der ersten Zahl (4096) ist klar, dassder Algorithmus schnell endet, weil 4096 eine Zweierpotenz ist. Die Frage ist letztlich:Gibt es eine Startzahl, mit der der Algorithmus nicht irgendwann endet? Dieses Problemtritt auch unter einer Reihe anderer Namen auf: Syracuse-Problem, Ulams Problem oderCollatz-Problem. Hinweis: Bei großen Zahlen wie der letzten angegebenen wird der int-Zahlenbereich überschritten; nehmen Sie stattdessen long long.

1.9.5 Container und SchleifenEin Container ist eine Datenstruktur, die gleichartige Elemente enthält. So enthält einObjekt des Typs vector<double> Elemente des Typs double. Auch ein string kann alsContainer aufgefasst werden: Er enthält Elemente des Typs char. Die klassische Zähl-schleife, die einzelnen Elemente eines Vektors auszugeben, lautet beispielsweise

for(size_t i = 0; i < einVektor.size(); ++i) {cout << einVektor[i] << ’\n’;

}

Auf die Zählvariable kann verzichtet werden, wenn auf alle Elemente des Containerszugegriffen werden soll, weil dann die folgende Kurzform verwendet werden kann:

for(double wert : einVektor) { // Kopie jedes Elementscout << wert << ’\n’;

}

Man kann die Schleife so lesen: Führe den Schleifenkörper für alle Objekte wert ineinVektor aus. Die lokale Variable wert ist dabei eine Kopie des jeweiligen Elementsdes Containers. Die Kopie kann innerhalb der Schleife verändert werden, ohne dass sichdie Änderung auf den Container auswirkt, etwa

for(double wert : einVektor) { // veränderliche Kopiewert = 2.0 * wert;cout << wert << ’\n’;

}

Wenn Änderungen der Kopie verhindert werden sollen, hilft const:

for(const double wert : einVektor) { // unveränderliche Kopiecout << wert << ’\n’;

}

Wenn die Elemente direkt im Container verändert werden sollen, muss eine Referenzangegeben werden:

for(double& wert : einVektor) { // Referenz zum Ändernwert *= 2.0; // alle Werte verdoppeln

}

Wenn auf die Elemente nur lesend zugegriffen werden soll, ist eine Kopie bei großenObjekten zeitraubend (bei double nicht kritisch). In solchen Fällen kann eine Referenzauf const verwendet werden:

Page 96: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 95

for(const double& wert : einVektor) { // Referenz zum Lesencout << wert << ’\t’;

}

Für string-Objekte und andere Container können dieselben Konstruktionen verwendetwerden. Hier werden zum Beispiel alle Leerzeichen des Strings in Unterstriche umgewan-delt:

for(char& zeichen : einString) { // Referenz zum Ändernif(zeichen == ’ ’) {

zeichen = ’_’;}

}

1.9.6 Typermittlung mit autoDas Schlüsselwort auto sagt dem Compiler, er soll selbst den Typ bei der Initialisierungermitteln. Das kann die Schreibarbeit verringern und vermeidet Tippfehler bei komplexenDatentypen. Beispiele:

const auto a = 2; // a ist vom Typ const intauto b = a; // b ist ein int, initialisiert mit der Konstante aauto & c = b; // c ist eine Referenz auf b, Typ & int

Das funktioniert auch bei Schleifen. So kann in den Schleifen des vorhergehenden Ab-schnitts auto statt double bzw. char geschrieben werden, zum Beispiel

for(const auto& wert : einVektor) { // Referenz zum Lesencout << wert << ’\n’;

}for(auto& zeichen : einString) { // Referenz zum Ändern

if(zeichen == ’ ’) {zeichen = ’_’;

}}

TippVerwenden Sie möglichst die gezeigte Kurzform einer Schleife. Vorteile:1. Es gibt keine Probleme mit einer Bereichsüberschreitung, wie sie etwa bei der verse-

hentlich falschen Schreibweise einer for-Schleife auftreten können, z.B.for(int i = 0; i <= container.size(); ++i) ... (Fehler: <= statt <).

2. Kürzere Schreibweise und verbesserte Lesbarkeit.3. Bei Verwendung von auto muss die Schleife nicht geändert werden, wenn der Typ

der Elemente des Containers sich ändern sollte.

Das folgende Programm zeigt weitere Anwendungen von auto bei selbstgeschriebenenund Standard-Datentypen:

Listing 1.18: Beispiele mit auto

// cppbuch/k1/auto.cpp

Page 97: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

96 1 Es geht los!

#include <iostream>#include <vector> // Standard-Vektor einschließen#include <string> // Standard-String einschließen#include <cstddef> // size_tusing namespace std;

// eigener Typstruct Punkt {

int x;int y;

};

int main() {Punkt p1 { 100, 200 };auto p2 = p1; // p2 ist auch vom Typ Punktcout << "p2.x= " << p2.x << " p2.y= " << p2.y << ’\n’;vector<double> v1 {1.1, 2.2, 3.3, 4.4, 5.5};auto v2(v1); // v2 ist auch vom Typ vector<double>for(auto wert : v2) {

cout << wert << ’\n’;}v2[0] = 9.9;string s1 {"Ende!"};auto s2(s1); // s2 ist auch vom Typ stringcout << s2 << ’\n’;

}

In den obigen Fällen sind die Typnamen recht kurz. Dies ist jedoch in Teilen der C++-Standardbibliothek anders, sodass dort das Schlüsselwort auto erheblich zur Erleichte-rung der Schreibarbeit beiträgt. Im Programm oben liegen die Anweisungen mit auto

direkt nach der Deklaration, auf die Bezug genommen wird. Bei größeren Programmenist das nicht unbedingt der Fall, sodass sich als weiterer Vorteil ergibt, dass man nichtirgendwo weiter oben im Programm oder in der Dokumentation der Standardbibliothekdie genaue Deklaration nachschlagen muss.Wie oben beim Vektor v1 zu sehen, wird der Inhalt der geschweiften Klammern beider Initialisierung als Liste interpretiert. Wenn ein Typ auto ermittelt werden soll, istdie Interpretation als Liste meistens nicht gewünscht oder sogar falsch. Verwenden Siedeshalb auto stets ohne geschweifte Klamern:

auto s2 {s1}; // falsch (s2 von oben)auto s2(s1); // richtigauto s2 = s1; // richtig

1.9.7 Unions und Bitfelder9

Unions

Unions sind Strukturen, in denen verschiedene Elemente denselben Speicherplatz be-zeichnen. Man kann damit Speicherplatz sparen. Im folgenden Beispiel werden eine int-Zahl und eine float-Zahl überlagert, das heißt, sie haben dieselbe Adresse. Der gesamte

9 Dieser Abschnitt kann beim ersten Lesen übersprungen werden.

Page 98: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.9 Benutzerdefinierte und zusammengesetzte Datentypen 97

Speicherplatz einer Variablen vom Typ union ist identisch mit dem Speicherplatzbedarfdes jeweils größten internen Elements. Das jeweils zuletzt geschriebene Element, aktivesElement genannt, kann benutzt werden, wie im Programm unten gezeigt.

Listing 1.19: Union

// cppbuch/k1/union.cpp#include <iostream>using namespace std;

union IntFloat {int intzahl;float floatzahl;

};

int main() {IntFloat eineUnion;eineUnion.intzahl = 42; // Speicherplatz belegencout << eineUnion.intzahl << ’\n’; // gespeicherten Wert verwendeneineUnion.floatzahl = 0.987f; // denselben Speicherplatz belegen// eineUnion.floatzahl ist jetzt aktives Elementcout << eineUnion.floatzahl << ’\n’; // ... und verwendencout << &eineUnion.intzahl << ’\n’ // Ausgabe der Adresse

<< &eineUnion.floatzahl << ’\n’; // muss gleich sein// Fehler: union-Missbrauch zur Typumwandlungcout << eineUnion.intzahl << ’\n’; // Interpretation eines float-Bitmusters als int

}

Am Ende des Programms wird das Bitmuster des zuletzt zugewiesenen Elements (float-Zahl) als Objekt eines andern Typs (int-Zahl) interpretiert. Diese Art von Typumwand-lung (englisch type punning) wird vom Compiler zwar akzeptiert, aber das Ergebnis istin der Regel falsch. Nur wenn eine gemeinsame Folge gleicher Typen am Anfang exis-tiert, kann das nicht-aktive Element ohne Probleme benutzt werden, wie im Folgendenzu sehen. Gegegeben sei, bezogen auf das obige Beispiel, eine weitere union:union Union2 {

int wert;IntFloat einIntFloat; // siehe oben

};

wert und einIntFloat.intzahl nehmen denselben Speicherplatz ein und haben auchdenselben Typ. Damit gibt es eine gemeinsame Folge gleicher Typen am Anfang, auchwenn die Folge hier nur aus einem Element besteht. Der folgende Programmcode istdeshalb korrekt und unproblematisch, obwohl u2.einIntFloat.intzahl nicht das aktive(d.h. zuletzt geschriebene) Element ist.Union2 u2;u2.wert = 99; // aktives Elementcout << u2.einIntFloat.intzahl << ’\n’; // nicht-aktives Element

TippVerwenden Sie union nur in besonders begründeten Fällen.

Page 99: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

98 1 Es geht los!

Bitfelder

Gerade in der Hardware-nahen oder Systemprogrammierung ist es oft wünschenswert,Bitfelder verschiedener Länge anzulegen, in denen die einzelnen Bitpositionen verschie-dene Bedeutungen haben. Die Deklaration eines Bitfelds in C++ hat die Syntax Daten-typ Name :Konstante;, wobei Datentyp char, short, int, long (mit den vorzeichenlosenunsigned-Varianten) oder ein Aufzählungstyp sein kann. Beispiel: int Bitfeld : 13;.Es dürfen keine Annahmen darüber getroffen werden, wo die 13 Bits innerhalb eines16- oder 32-Bit-Worts angeordnet sind – dies kann je nach Implementation verschiedensein. Es gibt keine Zeiger oder Referenzen auf ein Bitfeld. Bitfelder sollen nicht zumSparen von Speicher benutzt werden, weil der Aufwand des Zugriffs auf einzelne Bitsbeträchtlich sein kann und nicht einmal sicher ist, ob wirklich Speicher gespart wird: Eskönnte sein, dass eine Implementation jedes Bitfeld auch der Länge 1 stets an einer 32-Bit-Wortgrenze beginnen lässt. Üblich ist allerdings, dass aufeinanderfolgende Bitfelderaneinandergereiht werden. Beispielprogramm zum Zugriff auf Bitfelder (es sind die fürint-Typen erlaubten Bit-Operatoren möglich):

Listing 1.20: Bitfeld

// cppbuch/k1/bitfeld.cpp#include <iostream>

struct Bitfeldstruktur {unsigned int a : 4; // a und b sind Bitfelderunsigned int b : 3;

};

int main() {Bitfeldstruktur x;x.a = 06;x.b = x.a | 3;std::cout << x.b << ’\n’; // Umwandlung in unsigned und Ausgabe

}

1.10 Einfache Ein- und AusgabeEin- und Ausgabe werden soweit beschrieben, dass Abfragen der Tastatur und Darstel-lungen auf dem Bildschirm ebenso wie das Lesen und Schreiben von Dateien auf einfacheWeise möglich sind. Speziellere Fragen und Einzelheiten werden bis zum Kapitel 9 zu-rückgestellt.

1.10.1 Standardein- und -ausgabeEin Programm empfängt einen Strom von Eingabedaten, verarbeitet diese Daten undgibt einen Strom von Ausgabedaten aus. Unter »Strom« (englisch stream) wird eine Folgevon Bytes verstanden, die nacheinander vom Programm interpretiert beziehungsweise

Page 100: Der C++-Programmierer - ciando.com · Breymann Der C++-Programmierer Bleiben Sie auf dem Laufenden! Unser Computerbuch-Newsletter informiert Sie monatlich über neue Bücher und Termine.

1.10 Einfache Ein- und Ausgabe 99

erzeugt werden. Die Ein- und Ausgabe einzelner Zeichen auf dem Bildschirm (Konsole)oder in eine Datei kostet wegen der vielen damit verbundenen Schritte Rechenzeit. Umdiese Rechenzeit zu vermindern, werden Ein- und Ausgabe im Allgemeinen gepuffert.Damit ist gemeint, dass die Zeichen zunächst in einen besonderen Speicherbereich, Puf-fer genannt, geschrieben werden. Bei der Eingabe wird die eingetippte Zeile (= die imPuffer gespeicherten Zeichen) erst beim Drücken der ENTER -Taste dem Programm über-geben. Bei der Ausgabe läuft es ähnlich: Ist der Puffer gefüllt oder wird das Programmbeendet, werden alle noch im Puffer verbliebenen Zeichen ausgegeben. Eine ungepuffer-te Ausgabe bedeutet somit, dass sie sofort ausgeführt wird. In C++ sind einige Ein- undAusgabekanäle vordefiniert:cin Standardeingabe (Tastatur, gepuffert)cout Standardausgabe (Bildschirm, gepuffert)cerr Standardfehlerausgabe (Bildschirm, nicht gepuffert)clog Standardfehlerausgabe (Bildschirm, gepuffert)

Eingabe

Der Operator >>, der uns in anderem Zusammenhang schon als Bit-Verschiebeoperatorbegegnet ist, sorgt bei der Eingabe dafür, dass automatisch die nötigen Umformatierun-gen vorgenommen werden. int zahl; cin >> zahl; bewirkt, dass eine Folge von Zif-fernzeichen bis zu einem Nicht-Ziffernzeichen eingelesen und in die interne Darstellungeiner int-Zahl umgewandelt wird. Die Auswertung durch den >>-Operator hat bestimmteEigenschaften:

Führende Zwischenraumzeichen (englisch whitespace) werden ignoriert. Zwischen-raumzeichen sind Leerzeichen, Tabulatorzeichen ’\t’, Zeilenrücklauf ’\r’, Zeilensprung’\v’, Seitenvorschub ’\f’ und die Zeilenendekennung ’\n’. In der ASCII-Tabelle aufSeite 933 sind es die Zeichen 0x20 und 0x09 bis 0x0d in Hexadezimalschreibweise.Zwischenraumzeichen werden als Endekennung genutzt.Andere Zeichen werden entsprechend dem verlangten Datentyp interpretiert.

Sollen Zwischenraumzeichen nicht ignoriert werden, ist die Funktion get() zu verwen-den, die zum Einlesen einzelner Zeichen, also nicht von Zahlen, verwendet werden kann:

// einzelnes Zeichen einlesenchar c;cin.get(c);

Der nach außen hin nicht sichtbare Ablauf einer Tastaturabfrage mit »cin >>« bestehtaus mehreren Schritten, wobei angenommen wird, dass noch nichts auf der Tastatureingegeben worden ist:1. Aufforderung an das Betriebssystem zur Zeichenübergabe.2. Eingabe der Zeichen auf der Tastatur (mit Korrekturmöglichkeit durch die Backspace-

Taste). Die Zeichen werden vom Betriebssystem der Reihe nach in einem besonderenSpeicherbereich abgelegt, dem Tastaturpuffer.

3. Abschluss der Eingabe mit der ENTER -Taste. Damit wird das ’\n’-Zeichen als Zei-lenendekennung im Tastaturpuffer abgelegt, und der Puffer wird durch das Betriebs-system an C++ übergeben.