Dirk Louis
C/C++ new reference
Markt+Technik Verlag
Die Deutsche Bibliothek CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich.
Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Software-Bezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie zum Schutz vor Verschmutzung ist aus umweltverträglichem und recyclingfähigem PE-Material.
10 9 8 7 6 5 4 3 2 1 04 03 02 01
ISBN 3-8272-6121-X © 2001 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH. Martin-Kollar-Straße 1012, D81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Helfer Grafik Design, München Lektorat: Erik Franz,
[email protected] Herstellung: Anja Zygalakis,
[email protected] Satz: reemers publishing services gmbh, Krefeld (www.reemers.de) Druck und Verarbeitung: Media-Print, Paderborn Printed in Germany
Inhaltsverzeichnis
Inhaltsverzeichnis Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
Die Programmiersprachen C und C++. . . . . . . . . . . . . . . . . . . . . . . . . . . . Leistungsmerkmale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der ANSI-Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schreibkonventionen in C/C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aufbau eines C/C++-Programms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typische Komponenten von C/C++-Programmen . . . . . . . . . . . . . . . . . . Größere Programme und Modularisierung . . . . . . . . . . . . . . . . . . . . . . . . Objektorientiertes Programmieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwendung der Standardbibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmfluß . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmerstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14 15 15 17 17 20 26 28 29 31 33 34
Sprachkonzepte und Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
Elemente der Sprache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichensatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Trigraphsequenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schlüsselwörter und Symbole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigene Bezeichner: Definition und Deklaration . . . . . . . . . . . . . . . . . . . . Variablen und Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Variablen und Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die elementaren Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speicherbelegung der Integer-Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speicherbelegung der Gleitkommatypen . . . . . . . . . . . . . . . . . . . . . . . . . Vorzeichen integraler Datenypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41 41 43 43 46 48 48 50 51 52 53
5
Inhaltsverzeichnis
Variablendeklaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gültigkeitsbereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Namensbereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sichtbarkeit und Verdeckung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lebensdauer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammengesetzte Datentypen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammengesetzte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrdimensionale Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten (Strings) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bitfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeiger und Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spezielle Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Referenzen (nur C++) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typdefinitonen und Typumwandlungen . . . . . . . . . . . . . . . . . . . . . . . . . . Typdefinitionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standardkonvertierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typumwandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Literale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . #define – symbolische Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . const – konstante Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vorzeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arithmetische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . In- und Dekrementoperator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zuweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vergleichende Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Logische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenzugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bitweise Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Streamoperatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Operatoren new und delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typumwandlung und -identifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . Sonstige Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Synonyme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Priorität und Assoziativität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung von Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmsteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmsteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
54 58 59 63 64 65 65 66 67 70 72 74 77 79 81 81 84 87 89 89 90 92 94 94 94 96 98 99 99 101 102 103 104 106 106 109 113 116 117 119 125 128 128 129 132 132
Inhaltsverzeichnis
Die if-Bedingung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die if-else-Verzweigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . if-else-Ketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . switch-Verzweigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die for-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die while-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die do-while-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abbruchbefehle für Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsdeklaration und -definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Funktion main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenaustausch zwischen Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Rückgabewert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsargumente und Vorgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen und der Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen mit beliebig vielen Argumenten . . . . . . . . . . . . . . . . . . . . . . . Inline-Funktionen (nur C++) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Spezifizierer für Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung von Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladung von Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladen, Überschreiben, Verdecken . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen und OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kapselung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Statische Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstante Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klasseninstanzen als Datenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lokale und verschachtelte Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Statische Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstante Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriffsspezifizierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff innerhalb der Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff von außerhalb der Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriff auf Basisklassenelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Kopierkonstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Destruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standardmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der this-Zeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
132 133 134 135 137 138 139 141 142 143 143 145 147 149 150 150 153 154 157 158 159 160 162 163 163 164 164 166 167 168 169 170 170 172 173 174 175 176 178 179 181 183 184 186 187 187
7
Inhaltsverzeichnis
Instanzbildung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung und Polymorphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung versus Einbettung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zugriffsbeschränkung bei der Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . Zugriffsrechte für einzelne Elemente auflockern . . . . . . . . . . . . . . . . . . . . Vererbung und Konstruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nicht vererbbare Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Polymorphie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überschreibung und virtuelle Methoden . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung und Destruktor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abstrakte Methoden und Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Basisklassenzeiger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . RTTI – Laufzeittypidentifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrfachvererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Virtuelle Basisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden in Klassentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Instanziierung und Spezialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implizite Instanziierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Explizite Instanziierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Explizite Spezialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Präprozessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quellcode einfügen und ersetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Makros definieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Argumente in Makros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Direktiven zur bedingten Kompilierung . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenkettenbildung und Grundsymbolverbindung . . . . . . . . . . . . . . . . Sonstige Direktiven und Symbole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exception-Behandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exception-Behandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exceptions auslösen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exceptions abfangen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Handler-Bereich festlegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verwandte Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C-Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wide characters und Multibyte-Zeichen . . . . . . . . . . . . . . . . . . . . . . . . . Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatierte und unformatierte E/A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Streams in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Streams in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pufferung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
188 189 189 191 192 194 194 197 197 199 201 201 202 203 204 205 207 207 209 210 211 212 213 215 216 217 219 220 221 222 224 225 226 226 230 232 233 233 235 235 237 238 239 239 240 242 246
Inhaltsverzeichnis
Die C-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 Die Header-Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . assert.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ctype.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . errno.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ios646.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . limits.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . locale.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . math.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . setjmp.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . signal.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdarg.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stddef.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdio.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdlib.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . string.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . time.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . wchar.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . wctype.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Funktionen der C-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . .
247 247 248 248 249 250 251 251 252 253 254 255 255 255 257 259 260 262 263 264
Die C++-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 Übersicht über die Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Header-Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bitset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . complex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . fstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . functional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iomanip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iosfwd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . istream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . numeric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
329 330 330 332 333 334 335 335 336 338 338 340 340 341 342 343 344 344 346 347 347 348 349
9
Inhaltsverzeichnis
queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . sstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stdexcept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . streambuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . typeinfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . utility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . valarray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Container-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Allgemeine Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bitset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . map und multimap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . priority_queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . set und multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Iteratoren-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . reverse_iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Insert-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Stream-Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unterstützung eigener Implementierungen . . . . . . . . . . . . . . . . . . . . . . . . Die Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Stream-Klassen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ios_base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_streambuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_istream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_stringbuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_istringstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ostringstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_stringstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_filebuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ifstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_ofstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_fstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die String-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
349 350 351 351 352 352 353 354 354 354 356 357 357 360 362 365 367 372 373 375 378 379 382 382 384 385 387 390 392 400 400 402 404 405 407 409 411 411 413 414 415 415 417 418 419 420
Inhaltsverzeichnis
Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . char_traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . basic_string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klassen zur lokalen Einstellung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Hilfsklassen für die einzelnen Kategorien . . . . . . . . . . . . . . . . . . . . . . Die numerischen Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . complex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . valarray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Teilmengen von valarray-Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . numeric_limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen und Funktionen zur Exception-Behandlung . . . . . . . . . . . . . . . . . exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abgeleitete Exception-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen zur Exception-Behandlung . . . . . . . . . . . . . . . . . . . . . . . . . . Laufzeittypidentifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . type_info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Struktur pair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionsobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Speicherallokation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . allocator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . auto_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen und Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
420 420 421 425 425 427 428 443 443 444 447 452 453 453 454 456 458 458 459 459 459 464 464 465 466
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
11
C/C++ new reference Übersicht Grundlagen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
Sprachkonzepte und Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
Die C-Standardbibliothek. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
247
Die C++-Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
329
13
Grundlagen
Grundlagen Die Programmiersprachen C und C++ Im Gegensatz zu anderen Hochsprachen wie Pascal oder Basic, die ursprünglich als reine Lehrsprachen konzipiert wurden, verdankt C seine Entstehung dem Umstand, daß man bei Bell Laboratories eine höhere Programmiersprache für die Implementierung eines neu entwickelten Betriebssystems suchte. Bis dato programmierte man das gesamte Betriebssystem, das später als UNIX bekannt wurde, nämlich in Assembler. Um sich die Arbeit zu erleichtern, bastelte man an einer eigenen Programmiersprache, die möglichst effizient, gut zu portieren und leicht zu compilieren sein sollte. Brian W. Kernighan und Dennis M. Ritchie waren es, die diese Bemühungen zum Erfolg führten und gleichzeitig eine der leistungsfähigsten Programmiersprachen überhaupt schufen: die Programmiersprache C. In der Folgezeit fand C schnell weite Verbreitung und wurde aufgrund seiner Qualitäten und trotz der Schwierigkeiten, denen sich gerade Anfänger bei der Erlernung dieser Sprache gegenüber sehen, immer beliebter. Mit der Zeit wandelten sich die Anforderungen an eine professionelle Programmiersprache. Neben der Erzeugung schneller und kompakter Codes trat die Wartbarkeit und Wiederverwertung bestehender Codes immer weiter in den Vordergrund, und damit auch das Interesse an objektorientierten Konzepten. Mitte der Achtziger begann Bjarne Stroustrup C um objektorientierte Konzepte zu erweitern. Die Betonung liegt dabei auf dem Wort »Erweiterung«, denn die Kompatibilität zu C war bei der Entwicklung der neuen Programmiersprache ein vordringliches Designmerkmal. Eine der ersten Stationen auf dem Weg zur neuen Programmiersprache wurde daher noch als »C mit Klassen« bezeichnet. 1985 kam dann eine Version heraus, die bereits zurecht als objektorientierte Programmiersprache gelten kann, und die unter dem Namen C++ bekannt wurde. Mittlerweile ist C++ voll ausgereift und ebenso erfolgreich wie C. Ein Grund hierfür ist sicherlich die (nahezu) vollständige Abwärtskompatibilität zu C, die dem C-Programmierer den Einstieg in die objektorientierte Programmierung wesentlich erleichtert und die Verwertbarkeit bereits existierenden C-Codes garantiert.
14
Leistungsmerkmale
Im Gegensatz zu rein objektorientierten Sprachen wie Modula oder Smalltalk besteht damit allerdings auch die Gefahr, C++ lediglich als weniger strikte C-Version einzusetzen. Programmieren in C++ ist daher nicht automatisch mit objektorientierter Programmierung gleichzusetzen.
Leistungsmerkmale C wurde ursprünglich als High-Level-Sprache zur Systemprogrammierung, namentlich der Entwicklung des Betriebssystems UNIX, konzipiert. Insofern war C nur ein untergeordnetes Hilfsmittel, und C-Programme sollten einfach zu kompilieren und schnell sein. Das Ergebnis war eine Sprache, die sich durch ●
ihren geringen Umfang an Sprachelementen,
●
die schnelle Laufzeit ihrer Programme,
●
die Unterstützung modularer Programmierung und
●
ihrer guten Portabilität bei gleichzeitiger Systemnähe
auszeichnete. Heutzutage sieht die Situation so aus, daß Rechenzeit immer billiger und der Programmieraufwand immer kostspieliger wird. Ziel der objektorientierten Programmierung mit C++ ist es daher, Quelltext zu schaffen, der sich durch ●
einfache und sichere Verwendung,
●
hohe Wiederverwertbarkeit,
●
einfache Wartbarkeit,
●
gute Verständlichkeit und Lesbarkeit
auszeichnet.
Der ANSI-Standard Der Erfolg einer Programmiersprache hängt nicht nur von der Sprache selbst, sondern auch von der Verfügbarkeit passender Compiler (oder Interpreter) und unterstützender Bibliotheken ab. Letztere waren für die Verbreitung von C sogar von besonderer Bedeutung, da es in C für viele zentralen Aufgaben (Ein- und Ausgabe, Stringbearbeitung, dynamische Speicherverwaltung) keine in der Sprache verankerte Elemente gibt. Die Abhängigkeit von Standardbibliotheken bedeutet aber auch zusätzliche Abhängigkeit von den Compiler-Entwicklern. Um diesem Einfluß entgegenzuwirken und zu verhindern, daß jeder C-Compiler-Entwickler seinen eigenen C-Dialekt definiert, trat im Jahre 1983 im American National Standard Institute (ANSI) ein Komitee aus
15
Grundlagen
Compiler-Herstellern, Software-Entwicklern und Hardware-Produzenten zusammen, das einen Standard für die Programmiersprache C erarbeiten sollte. Der 1989 ratifizierte Standard (Referenznummer ISO/IEC 9899:1990) definiert sowohl Syntax und Semantik der Sprache wie auch die Zusammensetzung der Standardbibliothek. Programmierer, die sich an die vom Standard vorgegebenen Regeln halten und keine plattformspezifischen Funktionen verwenden (beispielsweise Programmierung mit DOS-Interrupts), können dank des ANSI-Standards sicher sein, daß sie ihr Programm von jedem ANSI-kompatiblen Compiler erstellen lassen können. Dies bedeutet aber auch, daß man ein Programm auf jedes beliebige System (Computer/ Betriebssystem) portieren kann, sofern es nur einen passenden ANSI-kompatiblen Compiler für dieses System gibt. 1995 wurde die C-Standardbibliothek um eine Sammlung von Funktionen zur Programmierung mit sogenannten Wide Characters – Zeichen, die durch 16- und 32Bit-Zeichencodes codiert werden – ergänzt. Bei den meisten dieser Funktionen, die in den neuen Headern wchar.h und wctype.h deklariert sind, handelt es sich um Adaptionen bestehender C-Funktionen an den Zeichentyp wchar_t. Die Erweiterungen sind im Amendment ISO/IEC 9899:1990/Amd.1:1995(E) zusammengefaßt. Auch für C++ gibt es mittlerweile einen ANSI-Standard. Die wichtigsten Neuerungen gegenüber dem bis dato geltenden Quasi-Standard von Bjarne Stroustrup dürften in der Einführung von Namensbereichen als frei definierbare globale Gültigkeitsbereiche und der Standardisierung der C++-Laufzeitbibliothek zu sehen sein. Die Ausführungen und Beschreibungen in diesem Buch richten sich nach dem neuen C++-Standard. Im Praxisteil dieses Buches finden Sie viele Abschnitte, die zeigen, wie man die Klassen der C++-Laufzeitbibliothek bei der täglichen Programmierarbeit nutzen kann. Obwohl die Abwärtskompatibilität zu C bei der Entwicklung von C++ höchste Priorität hatte, wurden einige wenige der von C übernommenen Konzepte und Elemente in ihrer Semantik geändert (siehe Anhang). Sollte sich ein C-Programm nach der Kompilation mit einem C++-Compiler anders als zuvor verhalten, sollte man prüfen, ob das Programm von diesen Änderungen betroffen ist.
Bezugsquellen C- und C++-Standard können Sie online beim ANSI-Institut oder IHS (Information Handling Service) anfordern: www.ansi.org www.ihs.de
Kopien der Standards können auch aus der Schweiz ISO/IEC Copyright Office Case Postale 56 CH-1211 Genève 20 Schweiz
16
Schreibkonventionen in C/C++
oder aus München bezogen werden: IHS Information Handling Services Tel. 089 / 89526999 Den abschließenden C++-Draft kann man sich derzeit noch aus dem Internet herunterladen: ftp://ftp.maths.warwick.ac.uk/pub/c++/std/cd2/
Schreibkonventionen in C/C++ Bei der Festlegung der Namen von Bezeichnern, das sind Variablen-, Funktions-, Klassen-, Typen- und Makronamen, dürfen folgende Zeichen benutzt werden: ●
alle Buchstaben des englischen Alphabets (a bis z, A bis Z)
●
die Ziffern 0 bis 9
●
der Unterstrich »_«
●
●
●
Der Name eines Bezeichners muß mit einem Buchstaben oder dem Unterstrich beginnen. Die Verwendung einer Ziffer als ersten Buchstaben eines Bezeichners ist nicht erlaubt. Die deutschen Umlaute und andere Sonderzeichen dürfen also nur in Kommentaren und in Zeichenketten vorkommen. Die maximale Länge der Bezeichner ist nicht vorgeschrieben, jedoch müssen nach ANSI C mindestens die ersten 31 Zeichen zur Unterscheidung herangezogen werden.
Warnung Die Sprache C unterscheidet zwischen Groß- und Kleinschreibung. Die Bezeichner var1 und Var1 können also für zwei verschiedene Variablen benutzt werden.
Aufbau eines C/C++-Programms #include <stdio.h> int main() { /* Anweisungen */ return 0; }
17
Grundlagen
Beschreibung C/C++-Programme bestehen aus einer Ansammlung von Deklarationen und Definitionen von Datentypen, Variablen, Funktionen und Klassen (sowie einigen speziellen Direktiven an den Präprozessor). Anweisungen, die festlegen, was ein Programm macht, findet man nur innerhalb von Funktionsdefinitionen (in C++ auch in den Methoden (Elementfunktionen) der Klassen). Jedes C/C++-Programm muß eine main()-Funktion definieren, mit deren Ausführung das Programm beginnt.
Anwendung Im einfachsten Fall besteht ein C/C++-Programm aus einer oder mehreren include-Direktiven, die den Zugriff auf bestimmte Funktionen und Klassen der C/C++-Standardbibliotheken ermöglichen, sowie
●
der obligatorischen Funktion main(), in der alle Anweisungen zur Ausführung des Programms stehen,
●
aus Variablen, die deklariert werden, um Werte zu repräsentieren und zwischenzuspeichern,
●
aus Anweisungen, in denen die Werte in den Variablen bearbeitet werden.
●
Beispiel Um ein einfaches Programm aufzusetzen, das zwei Zahlen multipliziert und das Produkt der beiden Zahlen ausgibt, könnte man folgendermaßen vorgehen: Um zwei ganzzahlige Werte im Programm verwalten zu können, deklariert man zwei Variablen des Datentyps int (Datentyp für ganzzahlige Werte). Für das Ergebnis kann man eine dritte Variable definieren. int main() { int zahl1, zahl2; int ergebnis;
Mit Hilfe des Zuweisungsoperators kann man den Variablen Werte zuweisen: int main() { ... zahl1 = 120; zahl2 = 4;
/* Werte zuweisen */
Zur Multiplikation von int-Werten verwendet man den *-Operator. Das Ergebnis der Berechnung weist man direkt der dritten Variablen zu. ergebnis = zahl1 * zahl2;
18
Aufbau eines C/C++-Programms
Um Werte auszugeben, kann man sich einer speziellen Funktion namens printf() bedienen, die Teil der C-Laufzeitbibliothek ist. printf("Das Produkt der Zahlen ist : %d\n", ergebnis);
Bevor man eine Funktion aufruft, muß die Funktion allerdings dem Compiler per Deklaration bekanntgemacht worden sein. Die Deklarationen für die Funktionen der Laufzeitbibliothek sind in einer Reihe von Header-Dateien zusammengefaßt. Nachdem man sich erkundigt hat, in welcher Header-Datei die gewünschte Funktion deklariert ist, bindet man die Header-Datei mittels einer #include-Anweisung am Anfang des Programms ein. #include <stdio.h>
Warnung Leser, die mit C/C++ für Windows programmieren wollen, seien darauf hingewiesen, daß Windows-Programme statt der main()-Funktion die Funktion WinMain() verwenden. Je nach verwendetem Compiler und Klassenbibliothek kann WinMain() selbst wieder durch eine andere Funktion (beispielsweise OwlMain() für Borland C++ mit OWL) oder eine Klasseninstanz (beispielsweise ein globales Objekt der MFCKlasse CWinApp in Visual C++) ersetzt sein. Konsolenprogramme, die unter Windows ausgeführt werden (sprich im Fenster der MS-DOS-Eingabeaufforderung ablaufen und über keine eigene Windows-Oberfläche verfügen), verwenden aber wie reine DOS- oder UNIX-Programme die main()Funktion.
Beispiel Das folgende Programm kann nach der Kompilation von der Konsole (unter Windows die MS-DOS-Eingabeaufforderung) Ihres Betriebssystems aus aufgerufen und ausgeführt werden. #include <stdio.h> int main() { int zahl1, zahl2; int ergebnis;
/* Faktoren */ /* Produkt */
zahl1 = 120; zahl2 = 4; ergebnis = zahl1 * zahl2;
/* Werte zuweisen */ /* Wert berechnen */
printf("Das Produkt der Zahlen ist : %d\n", ergebnis); return 0; }
19
Grundlagen
Verweise Siehe nachfolgende Abschnitte für weitere einführende Informationen zum Aufbau von C/C++-Programmen. Siehe Kapitel zur Sprache für detaillierte Informationen zu Variablen, Datentypen, Operatoren, Funktionen.
Typische Komponenten von C/C++-Programmen /* Das erste C-Programm */ #include <stdio.h>
Kommentar Präprozessor-Direktive
Funktion
Anweisungs -block
int main() { printf("Hello World\n"); return 0; }
Anweisungen
Typische Komponenten eines C/C++-Programms
Beschreibung Die Funktionalität eines Programmes wird bestimmt durch die Daten, die es verarbeitet, und die Operationen, die es auf den Daten ausführt. Insofern könnte man meinen, ein Programm bestünde nur aus Variablendefinitionen und Anweisungen. Dem ist aber nicht so. Der Compiler unterscheidet noch ganz andere Elemente.
20
Typische Komponenten von C/C++-Programmen
Aus Sicht des Compilers besteht ein Programm aus: Definitionen Deklarationen Anweisungen Präprozessor-Direktiven Kommentaren
Definitionen und Deklarationen Bei Ausführung des Programms müssen der Code und die Daten des Programms in den Arbeitsspeicher des Computers geladen werden. Welcher Code und welche Daten zu einem Programm gehören, legen Sie beim Aufsetzen des Quelltextes selbst fest. Der Code des Programms besteht aus den in Maschinencode übersetzten Anweisungen aus dem Quelltext. In C/C++ dürfen Anweisungen nur in Funktionsdefinitionen stehen. Indem Sie eine Funktion definieren, beispielsweise die main()-Funktion des Programms, teilen Sie dem Compiler mit, daß er für die Funktion Speicher reservieren und dafür Sorge tragen soll, daß der Code der Funktion bei Aufruf des Programms an die entsprechende Stelle im Speicher geladen wird. Für die Verwaltung der Daten definieren Sie Variablen. Hinter den Variablen stehen Speicherbereiche im RAM, die der Compiler für Sie reserviert. In den Variablen können Sie die Daten Ihres Programms speichern. Durch eine Definition führt man neue Elemente (Funktionen, Variablen, Datentypen) in ein Programm ein. Gleichzeitig wird das Element im Zuge der Definition mit einem Bezeichner verbunden (Variablenname, Funktionsname, etc.), der im Programmquelltext verwendet wird, um auf das Element zuzugreifen. Da mit der Definition von Funktionen und Variablen die Bereitstellung von Speicherbereichen einhergeht, müssen diese Elemente eindeutig sein, d. h., sie dürfen nur einmal im ganzen Programm definiert sein (sonst gäbe es Bezeichner, die mit mehreren Speicherbereichen assoziert wären). Andererseits fordert der Compiler, daß alle Elemente, die man verwendet, ihm vorab bekanntgemacht werden müssen. Bevor man eine Variable verwendet, muß man dem Compiler mitteilen, welchen Datentyp die Variable hat, bevor man eine Funktion aufrufen kann, muß man dem Compiler mitteilen, welche Parameter die Funktion übernimmt und was für einen Rückgabetyp sie zurückliefert. Die Bekanntmachung der Elemente bezeichnet man als Deklaration. Grundsätzlich ist jede Definition auch eine Deklaration. Es gibt aber auch die Möglichkeit, Elemente zu deklarieren, ohne daß man die Elemente neu deklariert. Für Variablen stellt man dabei der »Definition« das Schlüsselwort extern voran, für Funktionen gibt man nur den Funktionskopf (Rückgabetyp, Name, Parameterliste) ohne Anweisungsteil an.
21
Grundlagen
Verweise Siehe Elemente der Sprache.
Variablen und Konstanten Feste Werte, die sich im Laufe des Programms nicht ändern oder nur einmal benötigt werden, kann man direkt in Form von Literalen in den Quelltext schreiben. Für veränderliche Werte definiert man Variablen. Der Compiler weist der Variablen einen Speicherbereich zu, in den man im Laufe des Programms verschiedene Werte abspeichern kann. Eine Variable kann aber immer nur Werte eines Datentyps aufnehmen (mit Ausnahme der unions). Dies liegt daran, daß unterschiedliche Daten (Buchstabe, ganzzahliger Wert, Gleitkommawert) unterschiedlich viel Speicher benötigen. Auch die Codierung der Werte in Binärdarstellung für die Ablage im RAM ist für die einzelnen Datentypen unterschiedlich. Aus diesem Grund gehört jede Variable einem Datentyp an, der bei der Definition der Variablen vom Programmierer angegeben wird. int var1; double var2; char var3; var1 = 3; var2 = 3.14; var3 = 'c';
// // // // // //
Deklaration einer Variablen für ganzzahlige Werte Deklaration einer Variablen für Gleitkommawerte Deklaration einer Variablen für Zeichen Zuweisung einer ganzzahligen Konstanten Zuweisung einer Gleitkommakonstanten Zuweisung einer Zeichenkonstanten
Verweise Siehe Abschnitt, Kategorie Variablen und Datentypen.
Anweisungen
;
Der eigentliche Code des Programms. Die Anweisungen werden vom Compiler in Maschinenbefehle umgesetzt, die vom Prozessor des Computers bei Ausführung des Programms nacheinander abgearbeitet werden. Anweisungen enden stets mit einem Semikolon. ●
● ●
●
22
Anweisungen dürfen nur innerhalb der Anweisungsblöcke von Funktionen stehen. Jede Programmanweisung muß mit einem Semikolon abgeschlossen werden. Mehrere Anweisungen können – durch Semikolons getrennt – in einer Zeile stehen. Außer Anweisungen werden auch Deklarationen mit einem Semikolon abgeschlossen.
Typische Komponenten von C/C++-Programmen var1 = 3 * var2; printf("Hallo"); return 0;
// // // // //
Zuweisung eines Wertes an eine Variable Aufruf einer Funktion Schlüsselwort zur Beendigung einer Funktion
Anweisungsblöcke
{}
In C/C++ werden Anweisungen in Anweisungsblöcken zusammengefaßt. Ein Anweisungsblock beginnt mit einer öffnenden geschweiften Klammer und endet mit einer schließenden geschweiften Klammer. Anweisungsblöcke können ineinander verschachtelt werden. Der äußerste Block ist dabei stets der Definitionsblock einer Funktion. Innerhalb des Anweisungsblocks der Funktion kann man weitere Blöcke definieren, beispielsweise zur Implementierung einer Schleife oder zur Verzweigung des Programmablaufs in Abhängigkeit von einer if-Bedingung . void func() { long loop = 1;
// Funktion // Anweisungsblock zu Schleife
while (loop
24
Typische Komponenten von C/C++-Programmen
Kommentare
/* */ und //
Kommentare sind Textpassagen, die vom Compiler ignoriert werden. Sie erlauben dem Programmierer, Anmerkungen in den Quelltext aufzunehmen – meist zur Erklärung des nebenstehenden oder nachfolgenden C/C++-Codes. C
Kommentare beginnen in C mit der Zeichenkombination Schrägstrich – Stern »/*« und enden mit der Zeichenkombination Stern – Schrägstrich »*/«. Die dazwischenstehenden Zeichen werden vom Compiler ignoriert. Dies gilt auch für das Zeilenendezeichen, so daß sich Kommentare auch über mehrere Zeilen erstrecken dürfen. Sie dürfen jedoch nach dem ANSI-Standard nicht verschachtelt werden. (Einige Compiler erlauben optional die Verschachtelung von Kommentaren.) /* Dies ist ein Kommentar, der sich über mehrere – genauer gesagt zwei Zeilen erstreckt */ /* Das folgende Programm gibt einen Gruß auf den */ /* Bildschirm Ihres Computers aus */ #include <stdio.h> int main() { printf("Hallo!\n"); /* Aufruf einer Funktion */ return 0; } C++
In C++ kann die Auskommentierung auch durch doppelte Schrägstriche »//« erfolgen. Der nachfolgende Kommentar reicht bis zum Zeilenende. In C++ können beide Konzepte zur Kommentierung unabhängig voneinander verwendet werden. /* Das folgende Programm gibt einen Gruß auf den */ /* Bildschirm Ihres Computers aus */ #include <stdio.h> int main() { printf("Hallo!\n"); // Aufruf einer Funktion return 0; }
Tip In C++ bietet es sich an, innerhalb von Anweisungsblöcken nur //-Kommentare zu verwenden und die Klammern /* und */ zum Auskommentieren größerer Blöcke beim Debuggen einzusetzen.
25
Grundlagen
Größere Programme und Modularisierung Beschreibung Beim Aufsetzen größerer Programme empfiehlt es sich, den Code zu modularisieren, statt diesen Anweisung für Anweisung untereinander in die main()-Funktion zu schreiben. Die Modularisierung und Strukturierung des Codes sieht so aus, daß man einerseits Anweisungen und Code in Form von Funktionen und Klassen (nur C++) organisiert, andererseits den Quelltext selbst auf mehrere Dateien verteilt.
Aufteilung in Funktionen und Klassen Größere Programmieraufgaben löst man üblicherweise, indem man Teilaufgaben formuliert und diese in Form eigener Funktionen löst. Ein Programm, das zwei Daten einliest, auf der Grundlage der Daten eine Berechnung ausführt und dann das Ergebnis ausgibt, könnte drei Teilaufgaben definieren: ●
Daten einlesen
●
Berechnung durchführen
●
Daten ausgeben
Für jede dieser Teilaufgaben könnte man eine eigene Funktion definieren (wobei man für die Ein- und Ausgabe in einfachen Fällen auch direkt die entsprechenden Funktionen der Laufzeitbibliothek verwenden kann). Die Aufteilung in Funktionen hat den Vorteil, daß ●
der Quelltext übersichtlicher ist,
●
der Quelltext einfacher zu debuggen und zu warten ist.
Letzeres gilt insbesondere, wenn eine der Teilaufgaben mehrfach auszuführen ist. Statt jedes Mal die zugehörigen Anweisungen einzutippen, ruft man nur die entsprechende Funktion auf. Der Code wird nur einmal – in der Funktionsdefinition – aufgesetzt. Stellt sich heraus, daß die Funktion fehlerhaft ist oder erweitert werden muß, kann dies zentral in der Funktionsdefinition geschehen. In C++ kann Code nicht nur in Funktionen, sondern auch in Klassen (Datentypen, in denen Variablen und Funktionen zusammengefaßt werden) organisiert werden.
Aufteilung in mehrere Quelltextdateien Umfangreichen Code kann man auf mehrere Quelltextdateien verteilen. Dies ist beispielsweise angebracht, wenn man eine Sammlung von nützlichen Funktionen in einer eigenen Datei verwahren will (meist der erste Schritt zu einer eigenen Bibliothek), oder wenn man zu mehreren an einem größeren Programmprojekt arbeitet. Aufgabe des Compilers und Linkers ist es dabei, die einzelnen Quelltextdateien zu einem Programm zusammenzufassen.
26
Größere Programme und Modularisierung
Alle Quelltextdateien, die zusammen in einem Schritt vom Compiler übersetzt und zu einer Objektdatei kompiliert werden, bezeichnet man als Modul oder Übersetzungseinheit. Eine Übersetzungseinheit besteht aus einer Implementierungsdatei (Extension .c oder .cpp) und allen Quelltextdateien, deren Inhalt direkt oder indirekt per #include-Anweisung eingebunden werden.
Beispiel Für ein Programm, das zwei Zahlen von der Tastatur einliest und sowohl das Produkt als auch den Mittelwert der beiden Zahlen berechnet und ausgibt, könnte man beispielsweise zwei spezielle Funktionen zur Berechnung des Produkts und des Mittelwerts aufsetzen und diese dann in der main()-Funktion aufrufen. (Für zwei so simple Berechnungen wie das Produkt und den Mittelwert zweier Zahlen, würde man in der Praxis allerdings keine eigene Funktion aufsetzen. Doch geht es hier nur darum, das Prinzip der Modularisierung zu verdeutlichen.) #include <stdio.h> int produkt_berechnen(int z1, int z2) { int ergebnis; ergebnis = z1 * z2; return ergebnis;
/* Produkt berechnen */ /* Ergebnis zurückliefern */
} double mittelwert_berechnen(int z1, int z2) { double ergebnis; ergebnis = (z1 + z2)/2.0; return ergebnis;
/* Mittelwert berechnen */ /* Ergebnis zurückliefern */
} int main(int argc, char **argv) { /* Variablen deklarieren */ int zahl1, zahl2; int produkt; double mittelwert; /* Zahlenwerte einlesen */ printf("Geben Sie zwei Zahlen zwischen 0 und 100 ein: \n\n"); scanf("%d %d", &zahl1, &zahl2); fflush(stdin); /* Ergebnisse berechnen */ produkt = produkt_berechnen(zahl1, zahl2); mittelwert = mittelwert_berechnen(zahl1, zahl2);
27
Grundlagen /* Ergebnisse ausgeben */ puts("\n"); printf("Produkt = %d\n", produkt); printf("Mittelwert = %f\n", mittelwert); return 0; }
Verweise Siehe Abschnitt zur Sprache für detailliertere Informationen zu Funktionen und Klassen.
Objektorientiertes Programmieren Objektorientiert zu programmieren bedeutet nicht einfach, sich die Annehmlichkeiten von C++ gegenüber C zunutze zu machen oder die Klassenbibliotheken statt der C-Bibliotheken zu benutzen. In diesem strengeren Sinne kann man das folgende Programmbeispiel, in dem statt der C-Funktion printf() die Klasseninstanz cout und der überladene zahl1 >> zahl2; /* Daten ausgeben */ cout > 3 printf("~4 return 0; }
= %d\n", 80 >> 3 ); = %d\n", ~4);
Das Programm erzeugt folgende Ausgabe: a A 25 3 80 ~4
& 223 | 0x20 ^ 12 > 3
= = = = = =
A a 21 12 10 -5
Verweise Siehe Streamoperatoren
Streamoperatoren Beschreibung In C++ sind die Operatoren >> und int main() { int zaehler; for(zaehler = 0; zaehler < 10; zaehler++) printf("Der Wert von zaehler beträgt: %d\n", zaehler); return 0; }
Verweise Siehe Abbruchbefehle Siehe while-Schleife
Die while-Schleife Beschreibung Die while-Schleife ist die allgemeinste Form der Schleife, mit der man einen Anweisungsblock in Abhängigkeit von einer Schleifenbedingung mehrfach ausführen lassen kann.
Anwendung Die while-Schleife gleicht in der Syntax der if-Bedingung, nur daß die Bedingung nicht kontrolliert, ob der Anweisungsblock überhaupt ausgeführt wird, sondern wie oft er ausgeführt wird.
139
Sprachkonzepte und Syntax
Ansonsten verfügen aber auch while-Schleifen – genau wir for-Schleifen – häufig über Schleifenvariablen, die in die Schleifenbedingung eingehen und innerhalb des Anweisungsblocks der Schleife verändert werden – nur, daß Initialisierung und Veränderung der Schleifenvariable nicht wie bei der for-Schleife in den Schleifenkopf integriert werden können. Die while-Schleife wird aber auch häufig eingesetzt, wenn der Schleifenabbruch nicht von einer Schleifenvariablen, sondern von anderen Ereignissen (z. B. Benutzereingaben) abhängt. Die Schleife wird dann üblicherweise mittels einer break-Anweisung verlassen.
Syntax while (Bedingung) Anweisung; ●
●
Bedingung: Die Bedingung, die ausgewertet wird, muß einen Booleschen Wert ergeben. Hier kann beispielsweise eine Ganzzahl, eine arithmetische Operation, ein Vergleich oder der Aufruf einer Funktion mit entsprechendem Rückgabetyp stehen. Anweisung: Das Element Anweisung kann eine einzelne Anweisung sein oder aus einem Anweisungsblock bestehen. Eine einzelne Anweisung muß immer mit einem Semikolon (dem Anweisungsbegrenzer) beendet werden. Wenn ein Anweisungsblock ausgeführt werden soll, muß dieser in geschweifte Klammern eingeschlossen werden.
Beispiele Die while-Schleife ist die allgemeinste Form der Schleife. So kann man beispielsweise die folgende for-Schleife for (i=1; i int main() { int zahl; do { printf("Geben Sie eine Zahl ein. "); printf("99 um Programm zu beenden.\n"); scanf("%d", &zahl); if (zahl < 0) continue; printf("Die eingelesene Zahl "); printf("war: %d\n", zahl); } while (zahl != 99); return 0; }
Vorzeitiges Abbrechen einer Schleife mit break: int main() { int zahl; for (zahl = 3; zahl < 15; zahl++) { if (zahl == 8) break; printf("zahl hat folgenden Wert: %d\n", zahl); } return 0; }
Verweise Siehe for-Schleife Siehe while-Schleife
Funktionen Funktionen Beschreibung C-Programme bestehen aus einer beliebigen Anzahl von Funktionen, deren Namen innerhalb der Konventionen von C (siehe Grundlagenteil) frei gewählt werden dürfen.
143
Sprachkonzepte und Syntax
Anwendung Sinn einer Funktionsdefinition ist es, einen Anweisungsblock, der eine bestimmte Aufgabe erledigt (beispielsweise die Ausgabe auf Konsole, die Berechnung des Sinus, etc.), mit einem Namen zu verbinden. Wird dann irgendwo im Programm die Funktion über ihren Namen aufgerufen, wird die Programmausführung mit der ersten Anweisung aus dem Anweisungsteil der Funktion fortgesetzt. Nach Abarbeitung der Funktion springt die Programmausführung in die Zeile des Funktionsaufrufs zurück. Die Aufteilung des Quelltextes in Funktionen ermöglicht es, redundanten Code zu vermeiden, häufig benötigte Funktionen zu sogenannten Bibliotheken zusammenzufassen und die Programme modularer und damit wartungsfreundlicher zu gestalten. Zum Datenaustausch mit der aufrufenden Funktion kann eine Funktion eine beliebige Anzahl Parameter und einen Rückgabwert definieren. Funktionen können an beliebiger Stelle im Quelltext definiert werden (nicht aber innerhalb anderer Funktionen). Bevor man eine Funktion aufrufen kann, muß sie aber dem Compiler bekannt sein. Dies erreicht man, indem man die Funktionsdefinition an den Anfang der Quelltextdatei setzt oder indem man die Funktion an beliebiger Stelle definiert und lediglich eine Vorwärtsdeklaration der Funktion an den Programmanfang setzt. Letzteres System liegt auch der Verwendung von Header-Dateien für Bibliotheken zugrunde.
Beispiele #include <stdio.h> /* Deklarationen für printf, scanf, fflush */ double fakul(int zahl); /* Vorwärtsdeklaration zu fakul() */ int main() { int zahl; double fakultaet; printf("Geben Sie die Zahl ein: "); scanf("%d",&zahl); fflush(stdin); fakultaet = fakul(zahl); printf("\nDie Fakultaet von %d ist %lf\n", zahl, fakultaet); return 0; } double fakul(int zahl) { int loop; double fakultaet = 1; for(loop=zahl; loop>=1; loop--) { fakultaet *= loop; } return fakultaet; }
144
Funktionsdeklaration und -definition
Funktionsdeklaration und -definition Beschreibung Mit der Funktionsdeklaration werden der Rückgabetyp, der Name und die Parameter festgelegt. Eine Funktionsdefinition enthält zusätzlich den Funktionskörper, der in geschweiften Klammern die Anweisungen umfaßt.
Anwendung Bei der Funktionsdefinition wird ein Anweisungsblock mit einem Funktionsnamen verbunden, so daß beim Aufruf des Funktionsnamens im Programmcode der Anweisungsblock der Funktion ausgeführt wird. Zudem wird durch Angabe der Parameter und des Rückgabetyps der Funktion festgelegt, wie die Funktion Daten beim Aufruf übernimmt und bei ihrer Rückkehr zurückliefert.
Syntax Typ func_name (PARAMETERLISTE) { FUNKTIONSKöRPER } ●
●
●
●
Typ: Mit der Typangabe wird festgelegt, welchen Rückgabewert die Funktion hat. Arrays und Funktionstypen sind als Rückgabetyp nicht zulässig, wohl aber Zeiger auf diese Objekte. func_name: Der Name der Funktion. Dieser Name muß in C, nicht aber in C++, eindeutig sein (siehe Überladung). PARAMETERLISTE: In einer Definition stehen hier durch Kommata getrennt die einzelnen Parameter mit ihren Typen. In einer Deklaration reicht die Angabe der Parametertypen, die der Compiler zur Typenüberprüfung und zur Auflösung überladener Namen braucht (C++). In C++ können Vorgabeargumente an die Parameter vergeben werden. FUNKTIONSKÖRPER: Der Funktionskörper ist ein Anweisungsblock, der in geschweifte Klammern eingeschlossen wird und bei Aufruf der Funktion ausgeführt wird.
145
Sprachkonzepte und Syntax
Warnung ●
●
●
● ●
●
●
●
●
●
●
Funktionen haben standardmäßig die Speicherklasse extern. Darüber hinaus kann man Funktionen auch als static deklarieren. Die standardmäßige extern-Deklaration von Funktionen bewirkt, daß die Funktionen externe Bindung haben (es sei denn, es gibt im Quelltext eine vorangehende static-Deklaration der Funktion). Alle extern-Deklarationen einer bestimmten Funktion beziehen sich daher über Dateigrenzen hinweg auf ein und dieselbe Definition der Funktion. Auf diese Weise ist es möglich, über eine Vorwärtsdeklaration in einer Quelltextdatei eine Verbindung zu einer Funktion herzustellen, die in einer anderen Quelltextdatei oder einer Bibliothek definiert ist . Deklariert man eine Funktion als static, hat die Funktion interne Bindung, d. h., der Name der Funktion ist nur innerhalb der eigenen Quelltextdatei bekannt. Auf diese Weise kann man in den verschiedenen Quelltextdateien eines Programmes gleichnamige Funktionen definieren (aber natürlich nur eine pro Datei), ohne daß es zu Linker-Fehlern wegen Mehrfachdefinitionen kommt. Funktionen, die keinen Wert zurückgeben sollen, erhalten die Typangabe void. Funktionen, für die kein Rückgabetyp angegeben wurde, bekommen in C automatisch den Rückgabetyp int zugewiesen (in C++ ist dies ein Fehler). Werden in einer Funktionsdeklaration, die nicht Teil einer Definition ist, keine Parameter spezifiziert, wird dies unter C als Funktion mit beliebig vielen Parametern interpretiert, in C++ als Funktion ohne Parameter. Durch Angabe des Schlüsselworts void statt einer Parameterliste kann in C eindeutig eine Funktion ohne Parameter deklariert werden. Neben dem überflüssigen Schlüsselwort auto ist register die einzige Speicherklassenangabe, die für Parameter vergeben werden kann. Dadurch wird beim Aufruf der Funktion, sofern möglich, das so deklarierte Argument in ein Prozessorregister geladen. Die Angabe ... am Ende der Parameterliste bedeutet, daß noch beliebig viele Argumente übergeben werden können. Funktionsname und die Parameterliste bilden zusammengenommen die Signatur der Funktion. Innerhalb des Funktionskörpers können eigene Variablen definiert werden, die, wenn nicht anders angegeben, die Speicherklasse auto besitzen und lokalen Gültigkeitsbereich haben. Diese lokalen Variablen werden beim Aufruf der Funktion auf dem Stack erzeugt und beim Verlassen der Funktion automatisch wieder entfernt. Soll der Wert einer lokalen Variablen auch nach dem Verlassen der Funktion erhalten bleiben, kann die Speicherklasse static benutzt werden. Meist wird dies benutzt, um die Werte lokaler Variablen von Funktionsaufruf zu Funktionsaufruf weiterzureichen.
146
Die Funktion main()
●
●
In C++ kann man Funktionen als inline deklarieren, um den Compiler anzuweisen, wenn möglich, die Funktionsaufrufe nicht durch einen Sprung in den Anweisungsteil der Funktion, sondern direkt durch Anweisungen, die mit der Ausführung der Funktion identisch sind, zu ersetzen. Dies ist allerdings nur eine Empfehlung an den Compiler. In C++ kann man mit Hilfe des Schlüsselwortes throw bei der Funktionsdeklaration eine Liste der Exceptions angeben, die in der Funktion ausgelöst werden dürfen. Treten in der Funktion Exceptions anderer Typen auf, wird eine unexpected-Exceptions ausgelöst.
Beispiele int main(); int func1(char* s, int n); void func2();
Vorgabeargumente in C++: int
func3(double par, char* s = "Vorgabe", int n = 2);
Überladene Funktionen in C++: void func(int n); int func(int n, int m); int func(float f);
Warnung Die alte auf Kernighan/Ritchie zurückgehende Form der Funktionsdefinition, bei der die Parameter unter dem Funktionsnamen definiert wurden, ist in C obsolet und in C++ nicht mehr möglich. // func_name(a,b) // int a, double b; // { ...
Verweise Siehe Klassen, Methoden
Die Funktion main() Beschreibung Der ANSI-Standard schreibt vor, daß eine der Funktionen in einem C/C++-Programm den Namen main() tragen muß. Diese Funktion ist die Eintrittsfunktion eines Programms, der nach dem Start des Programms automatisch die Kontrolle übergeben wird. Zusätzlich soll sie die Möglichkeit bieten, Kommandozeilenargumente zu übernehmen.
147
Sprachkonzepte und Syntax
Anwendung Der ANSI-Standard sieht folgende Definitionen vor: int main() { } int main(int argc, char *argv[]) {
}
Die meisten Compiler erlauben aber auch void als Rückgabewert: void main() { } void main(int argc, char *argv[]) { }
main() ohne Argumente
main()
Programme, die keine Argumente aus dem Programmaufruf übernehmen, definieren main() ohne Parameter. int main () { } void main ( ) { }
main() und ihre Parameter
main(int, char**)
Der ANSI-Standard sieht auch vor, daß der Funktion main() optional zwei Argumente übergeben werden können. Die Namen für diese Argumente sind frei wählbar, jedoch haben sich die Namen argc und argv eingebürgert. void main(int argc, char *argv[]) { ●
●
}
argc: (Abkürzung für »argument count«) ist der übliche Name für das erste Argument der main()-Funktion. Der Datentyp von argc ist int. Der Wert von argc entspricht der Anzahl der Zeichenketten in dem Array, auf das argv zeigt. Per Definition kann argc nie einen negativen Wert annehmen. argv: (Abkürzung für »argument vector«) ist der übliche Name für das zweite Argument der main()-Funktion. argv zeigt auf ein Array mit Zeigern vom Typ char. Die Zeichenketten werden vom Lademodul des Systems an das Programm übergeben.
Die Anzahl der Parameter wird durch das Argument argc festgelegt. Per Definition zeigt argv[0] (also der erste Zeiger) immer auf den Namen des Programms, während argv[argc] ein NULL-Zeiger ist. Die einzelnen Parameter befinden sich in argv[1] bis argv[argc-1].
Beispiele #include <stdio.h> #include <stdlib.h> int main( int argc, char *argv[]) { int i; printf( "\nBefehlszeilen-Argumente:\n" ); for( i = 0; i < argc; i++ ) printf( " argv[%d] %s\n", i, argv[i] );
148
Datenaustausch zwischen Funktionen return( EXIT_SUCCESS ); }
Nachstehend sehen Sie die Ausgabe des Programms, wenn der Name des Programms MAIN.EXE ist, und es mit folgender Befehlszeile aufgerufen wurde: C:\CC>main eins zwei drei Befehlszeilen-Argumente: argv[0] C:\CC\MAIN.EXE argv[1] eins argv[2] zwei argv[3] drei
Verweise Siehe Grundlagenteil, Aufbau eines C/C++-Programms
Datenaustausch zwischen Funktionen Beschreibung Prinzipiell besteht ein C-Quelltext nur aus Deklarationen und Definitionen. Anweisungen finden sich nur in Funktionsdefinitionen. Damit Funktionen zusammenarbeiten können, müssen sie Informationen, sprich Daten, austauschen. Dazu gibt es in C verschiedene Konzepte.
Anwendung Parameter
Parameter sind spezielle Variablen einer Funktion, denen beim Aufruf der Funktion Werte zugewiesen werden. Werden als Parameter Zeiger definiert, kann die Funktion auf diesem Weg auch Daten zurückliefern.
Rückgabewert
Ein einzelner Wert, den die Funktion an den Aufrufer zurückgibt.
Globale Variablen
Eine weitere Möglichkeit, Daten zwischen Funktionen auszutauschen, läuft über die globalen Variablen. Dieser Weg ist sehr einfach, hat aber den Nachteil, daß die Unabhängigkeit und Abgeschlossenheit der Funktion dadurch beeinträchtigt wird.
Statische Variablen Sollte es erforderlich sein, daß eine lokale Variable nicht nach dem Verlassen der Funktion gelöscht werden darf, damit ihr Wert beim nächsten Aufruf der Funktion noch erhalten ist, wird die Variable als static deklariert.
149
Sprachkonzepte und Syntax
Verweise Siehe nachfolgende Abschnitte
Der Rückgabewert Beschreibung Funktionen können das Ergebnis ihrer Berechnung über den Rückgabewert zurückliefern. Dazu wird der Typ des Rückgabewerts im Funktionskopf spezifiert. Mittels der Anweisung return (ergebnis);
wird der Wert dann an die aufrufende Funktion zurückgeliefert.
Anwendung Es gibt zwei Möglichkeiten, das Konzept des Rückgabewertes sinnvoll zu nutzen: ●
●
Das Ergebnis der Funktion wird einer Variablen der aufrufenden Funktion zugewiesen. Die Funktion meldet zurück, ob sie ihre Aufgabe erfolgreich gelöst hat, oder ob in der Funktion ein Fehler aufgetreten ist.
Warnung Funktionen, die keinen Wert zurückliefern, erhalten den Rückgabetyp void. Eine solche Funktion kann return nur ohne Wert aufrufen: return; .
Verweise Siehe Parameter Siehe Kategorie Programmsteuerung, Abbruchbefehle für Schleifen
Parameter Beschreibung Die Parameter dienen dazu, Werte von der aufrufenden Funktion an die aufgerufene Funktion zu übergeben. Parameter sind stets lokale Variablen, deren Gültigkeitsbereich der äußerste Block der Funktion ist. Zur Übergabe von Daten an Parameter benutzt C standardmäßig ein Verfahren, das man »call by value« nennt.
150
Parameter
Wert kopieren
call by value
Dies bedeutet, daß beim Funktionsaufruf der Wert des übergebenen Arguments in die lokale Variable kopiert wird. Dieses Konzept stellt sicher, daß als Argument übergebene Variablen nicht von den aufgerufenen Funktionen verändert werden können:
Beispiele #include <stdio.h> void change(int param) { printf("\tParam nach Funktionsaufruf: %d\n",param); param = param*param; printf("\tParam nach Berechnung: %d\n",param); } int main() { int var = 10; printf("var vor Funktionsaufruf: %d\n",var); change(var); printf("var nach Funktionsaufruf: %d\n",var); return 0; }
liefert folgende Ausgabe: var vor Funktionsaufruf: 10 Param nach Funktionsaufruf: 10 Param nach Berechnung: 100 var nach Funktionsaufruf: 10
Adresse kopieren
call by reference
Das Pendant heißt »call by reference«. Dabei wird nicht der Wert einer Argumentvariablen übergeben, sondern ihre Adresse! Da die Funktion in diesem Fall statt einer Kopie eine Referenz auf das Aufrufargument erhält, kann sie dieses verändern. Referenzparameter sind daher neben dem Rückgabewert ein weiteres Mittel, um Daten von der aufgerufenen Funktion an die aufrufende Funktion zurückzugeben. Für »call by reference«-Übergaben gibt es folgende Möglichkeiten: Parameter
Argument
Zeiger:
Typ *
ptr
typ *
&var
(Zeiger vom Typ typ*) (Adresse einer Variablen vom Typ typ)
Referenz, nur C++:
151
Sprachkonzepte und Syntax
Parameter
Argument
typ &
var
(Variable vom Typ typ)
Beispiele #include <stdio.h> void change1(int param) { param = param*param; } void change2(int *param) { *param = *param * *param; } void change3(int ¶m) { param = param*param; } int main() { int var = 10; change1(var); change2(&var); change3(var);
printf("var : %d\n",var); printf("var : %d\n",var); printf("var : %d\n",var);
return 0; }
liefert folgende Ausgabe: var : 10 var : 100 var : 10000
Warnung Gelegentlich werden Zeiger übergeben, ohne daß eine Änderung des übergebenen Wertes erwünscht wäre. Dies kann zum Beispiel bei der Übergabe von Arrays der Fall sein, die immer als Zeiger übergeben werden, oder wenn Zeiger auf größere Datenstrukturen übergeben werden, um das Kopieren zu sparen. In solchen Fällen kann durch den Spezifizierer const in der Parameterdeklaration die entsprechende Variable vor dem Überschreiben geschützt werden.
Tip In C++ werden statt Zeigern häufig Referenzen zur effizienten Übergabe von Argumenten an Funktionen benutzt.
Verweise Siehe Rückgabewert
152
Funktionsargumente und Vorgabe
Funktionsargumente und Vorgabe Beschreibung In C++ können in der Funktionsdeklaration Vorgabeargumente vergeben werden. Parametern, für die Vorgabeargumente deklariert wurden, brauchen beim Funktionsaufruf keine Argumente mehr übergeben zu werden.
Anwendung Auf diese Weise kann der Aufruf für häufig übergebene Argumente vereinfacht werden.
Warnung ●
●
●
●
Auf einen Parameter mit Vorgabeargument dürfen keine Parameter ohne Vorgabeargumente mehr folgen. Bei einer Redeklaration dürfen weitere Vorgabeargumente angegeben werden, es dürfen jedoch keine bereits deklarierten Vorgabeargumente redeklariert werden. Als Vorgabeargumente können auch Ausdrücke oder Variablen deklariert werden. Es dürfen jedoch keine lokalen Variablen verwendet werden. Die Reihenfolge, in der die Funktionsargumente ausgewertet werden, ist nicht festgelegt und kann somit von Compiler zu Compiler verschieden sein.
Beispiele int void char* char* int
func1(int n, int m = 3); func2(char *s = "Vorgabe"); func3(int x, int y=4); func3(int x = 2, int y); func4(int a = 0, int b);
// gueltige Redeklaration // Fehler
153
Sprachkonzepte und Syntax
Funktionen und der Stack Beschreibung Der Stack ist ein dynamisch wachsender und schrumpfender Speicherbereich, der vom Compiler verwaltet wird. Hier legt der Compiler alle Informationen ab, die er für die Protokollierung und Verwaltung der Funktionsaufrufe benötigt. Immer wenn Sie eine Funktion aufrufen, wird der Stack um einen neuen Datenblock erweitert. In diesem Datenblock werden die Parameter der Funktion, die lokalen Variablen der Funktion und einige interne Informationen (von denen die Rücksprungadresse zum Code der aufrufenden Funktion die wichtigste ist) abgelegt. Diesen Datenblock nennt man auch den Stack-Frame oder Stackrahmen der Funktion.
Stack-Aktionen ●
●
Solange eine Funktion abgearbeitet wird, bleibt ihr Stack-Frame auf dem Stack liegen. Werden aus einer Funktion heraus weitere Funktionen aufgerufen, werden deren Stack-Frames auf den Stack-Frame der jeweils zuvor aufgerufenen Funktion draufgepackt.
154
Funktionen und der Stack
● ●
Wird eine Funktion beendet, wird ihr Stack-Frame vom Stack entfernt. Der Stack ist also immer das genaue Abbild der zur Zeit noch in Ausführung befindlichen Funktionen. Die Stapelreihenfolge der Funktionen zeigt an, welche Funktion von welcher anderen Funktion aufgerufen wurde.
Stack-Aufbau Für einen von oben nach unten wachsenden Stack gilt: ●
●
● ●
Die unterste Funktion ist die aktuelle Funktion, deren Anweisungen gerade abgearbeitet werden. Die darüberliegende Funktion hat die unterste Funktion aufgerufen und wartet nun, daß die aufgerufene Funktion beendet wird. Die oberste Funktion ist die Eintrittsfunktion main(). Die einzelnen Stack-Frames von oben nach unten gelesen zeigen an, über welche Funktionen die aktuelle Funktion aufgerufen wurde.
Der genaue Aufbau der Stack-Frames hängt von dem Rechner ab, für den das Programm kompiliert wurde (die Abbildung ist also nur als eine Art allgemeines Modell zu verstehen). Sie können der Stack-Verwaltung Ihres Rechners aber ein wenig nachspüren, indem Sie sich der Debug-Tools Ihres Compilers bedienen (Stack-Anzeige) oder sich die Adressen der Parameter und lokalen Variablen ausgeben lassen.
Beispiele #include <stdio.h> #include <stdlib.h> func(int par1, int par2, int par3) { int lokal1, lokal2, lokal3; printf("%d. Aufruf von func\n",par1); printf("\t Adresse von par3 = %p\n",&par3); printf("\t Adresse von par2 = %p\n",&par2); printf("\t Adresse von par1 = %p\n",&par1); printf("\t Adresse von lokal1 = %p\n",&lokal1); printf("\t Adresse von lokal2 = %p\n",&lokal2); printf("\t Adresse von lokal3 = %p\n",&lokal3); if(par1. Diese werden benötigt, um die Argumente zu übernehmen und auszuwerten.
Beispiele double mittel(int anzahl, ...) double summe = 0; va_list ap; int loop;
{
157
Sprachkonzepte und Syntax va_start(ap, anzahl); for(loop=1;loop b ? a : b); } const char* max(const char* s1, const char* s2) { return (strcmp(s1,s2) ? s1 : s2); } int max(int a, char* s) { return ( a > strlen(s) ? a : strlen(s)); }
162
Überladung von Methoden
Überladung von Methoden Beschreibung Methoden werden ebenso überladen, wie oben für Funktionen im Dateibereich ausgeführt wurde. Besonderheiten ergeben sich jedoch für die Überladung ererbter Methoden, da Basisklasse und abgeleitete Klasse unterschiedliche Klassenbereiche darstellen.
Warnung ● ●
●
Ererbte Funktionen können in abgeleiteten Klassen nicht überladen werden. In der abgeleiteten Klasse deklarierte Funktionen verdecken gleichnamige Funktionen der Basisklassen, die weiterhin über den Bereichsoperator aufrufbar sind. Soll eine Überladung simuliert werden, müssen die Versionen der Basisklasse zuerst überschrieben werden.
Beispiele class basis { public: void func(char * s); void func(char c); }; class abgeleitet : public basis { public: void func(int wert); } int main() { class abgeleitet abg; abg.func("Test"); // Fehler abg.func('c'); // Fehler abg.func(3.2); // wird als 3 interpretiert abg.basis::func("Test"); return 0; }
Überladen, Überschreiben, Verdecken Überladene Funktionen ●
befinden sich im gleichen Gültigkeitsbereich.
●
sind anhand ihrer Parameter unterscheidbar.
●
dienen zur Anpassung einer Funktion an verschiedene Parametertypen.
163
Sprachkonzepte und Syntax
Überschriebene Methoden ●
liegen in verschiedenen Gültigkeitsbereichen, wobei die überschriebene Methode in einer Basisklasse und die überschreibende Methode in einer von der Basisklasse abgeleiteten Klasse definiert ist.
●
haben identische Namen und Parametertypen.
●
werden üblicherweise als virtual deklariert.
●
dienen der Implementierung polymorphen Verhaltens in Klassenhierarchien.
●
sind über den Gültigkeitsbereichoperator weiterhin erreichbar.
Verdeckte Methoden ●
liegen in verschiedenen Gültigkeitsbereichen, wobei die überschriebene Methode in einer Basisklasse und die überschreibende Methode in einer von der Basisklasse abgeleiteten Klasse definiert ist.
●
haben nicht-identische Namen und Parametertypen.
●
sind über den Gültigkeitsbereichoperator weiterhin erreichbar.
Klassen Klassen Beschreibung C++ basiert auf der Sprache C. Die vollständige Aufwärtskompatibilität garantiert, daß C++ die Syntax von C vollkommen einschließt. Objektorientiertes Programmieren ist daher nicht gleichzusetzen mit Programmieren in C++, sondern beginnt erst mit der Verwendung der objektorientierten Konzepte, um die C++ den C-Standard erweitert. Die wichtigsten dieser Konzepte (Kapselung, Vererbung, Polymorphie, Templates) gehen zurück auf den neuen Datentyp class.
Anwendung Klassen dienen dazu, Datenelemente und Elementfunktionen (Methoden) in einem Datentyp zu kapseln. Sie gestatten es, verschiedene Zugriffsberechtigungen für die einzelnen Elemente zu vergeben, und können durch Vererbung Klassenhierarchien aufbauen.
164
Klassen
Syntax klassentyp klassenname { ELEMENTLISTE } instanzen; ●
● ●
[: BASISKLASSEN]
klassentyp: Als Klassentyp können neben dem Schlüsselwort class auch struct und union verwendet werden. Die Verwendung der Schlüsselwörter struct und union beruht darauf, daß in C++ Strukturen und Unions spezielle Klassen darstellen. Dies erleichtert die Verwendung von C-Modulen in C++-Programmen, sollte den Programmierer aber nicht dazu verführen, beide Konzepte zu vermischen. Es ist empfehlenswert, Klassen stets als class zu deklarieren und Unions und Strukturen nur gemäß der C-Konvention zu verwenden. klassenname: Name der zu deklarierenden Klasse. :BASISKLASSEN: Durch Kommata getrennte Liste von Klassen, deren Elemente übernommen (geerbt) werden. Der Zugriff der abgeleiteten Klasse auf die ererbten Elemente kann durch die Spezifizierer public, protected und private modifiziert werden. Ein Eintrag in dieser Liste hat folgende Syntax: [virtual] [public, protected, private] basisklassenname
●
●
ELEMENTLISTE: In der Elementliste werden Datenelemente und Methoden der Klasse deklariert und unter Umständen auch schon definiert. Der Zugriff auf die Elemente kann durch die Spezifizierer public, protected und private geregelt werden. Alle Elemente haben ihre Klasse als Gültigkeitsbereich. instanzen: Hinter der abschließenden Klammer können durch Kommata getrennt bereits Instanzen, also Variablen der Klassentyps, eingerichtet werden.
Warnung ●
●
Eine Basisklasse muß deklariert sein, bevor sie zur Vererbung herangezogen werden kann! Ein Zugriffsspezifizierer gilt für alle folgenden Klassenelemente bis zum Auftreten eines neuen Zugriffsspezifizierers.
●
Zugriffsspezifizierer können nicht kombiniert werden.
●
Zugriffsspezifizierer dürfen in der Klassendeklaration mehrfach auftauchen.
●
Klassenelemente am Anfang der Deklaration, für die kein Zugriffsspezifizierer angegeben wurde, gelten automatisch als private.
Beispiele class beispiel { int wert; public: beispiel() {wert = 1;}; protected:
// private // Konstruktor
165
Sprachkonzepte und Syntax int gebe_wert_aus() {return wert;}; private: int loesche_wert(); };
Verweise Siehe Variablen und Datentypen, Strukturen Siehe Variablen und Datentypen, Unions Siehe Vererbung und Polymorphie
Klassen und OOP Beschreibung Das Konzept, welches hinter den Klassen und allgemein der objektorientierten Programmierung steht, beruht auf der alltäglichen Erfahrung, daß wir die Objekte der realen Welt nach zwei Maßstäben beurteilen. Einmal nach Merkmalen wie Form und Farbe, zum anderen nach bestimmten »Verhaltensweisen«, die ein Objekt aufweist, und die beispielsweise festlegen, wie man mit ihm umzugehen hat. In der objektorientierten Terminologie entsprechen die Merkmale den Datenelementen und die Verhaltensweisen den Methoden (Elementfunktionen).
Beispiel Objekte, die gleiche Merkmale und Verhaltensweisen aufweisen, können in einer Klasse zusammengefaßt werden. So könnte eine Klasse videorecorder folgendermaßen aussehen: class videorecorder { /* Merkmale */ char *hersteller; int anzahl_videokoepfe; boolean zeitlupe, longplay, zweikanalton; public: /* Verhaltensweisen */ void anschalten(); void abspielen(); void aufnahme(); void in_zeitlupe_abspielen(); void ausschalten(); };
Bei der Bildung einer Variablen der Klasse – im folgenden als Instanzbildung bezeichnet – werden den Merkmalen Werte zugewiesen. Auf diese Weise entsteht durch Spezialisierung eine Instanz, die nur noch einen besonderen Videorecorder repräsentiert. (Ein weiteres Beispiel wäre die Klasse Mensch und ihre Instanzen, nämlich jeder einzelne von uns – die Individuen. Eine Geburt wäre folglich eine Instanzbildung.)
166
Kapselung
Verweise Siehe Kapselung Siehe Instanzbildung
Kapselung Beschreibung Die Zusammenfassung von Datenelementen und Methoden wird als Kapselung bezeichnet. Zudem verbinden sich mit dem Begriff noch zwei weitere Designkriterien: Information hiding und Abgeschlossenheit
Information hiding Klassen sollten vornehmlich über ihre Methoden angesprochen werden. Diese Funktionen bilden die Schnittstelle zwischen Klasse und Programm. (Die genaue Festlegung der Schnittstelle erfolgt über die Vergabe von Zugriffsspezifizierern.) Nach der Implementierung der Klasse ist es für den Programmierer wichtig, daß er die Klasse nur noch über ihre Schnittstelle einzusetzen braucht, ohne sich weitere Gedanken um deren Implementierung machen zu müssen. Für die fehlerlose Bearbeitung der Funktionsaufrufe und das korrekte Verhalten der Klasse, trägt diese selbst Sorge. Wurde zum Beispiel eine Klasse videorecorder implementiert, um Videorecorder über den Computer anzusteuern, braucht der Programmierer nur noch eine Instanz der Klasse zu bilden und kann danach die Instanz einsetzen wie einen realen Videorecorder: my_recorder.anschalten(); my_recorder.in_zeitlupe_abspielen(); my_recorder.ausschalten();
Die Ansteuerung des Videorecorders über die parallele Schnittstelle oder die Kontrolle, ob der Videorecorder überhaupt über Zeitlupe verfügt, nimmt die Klasse selbst vor, d. h.,sie versteckt solche Details vor dem Programmierer und entlastet ihn dadurch.
Abgeschlossenheit Aus der Forderung des Information hidings ergibt sich das Kriterium der Abgeschlossenheit. Die Schnittstelle einer Klasse sollte möglichst vollständig sein, in dem Sinne, daß die Klasse auch sinnvoll eingesetzt werden kann. Eine Klasse videorecorder, die über keine Funktion in_zeitlupe_abspielen() verfügt, wäre von vornherein zur Bedienung von Videorecordern mit Zeitlupe ungeeignet.
167
Sprachkonzepte und Syntax
Umgekehrt sollten in einer Klasse nur Daten und Funktionen enthalten sein, die die Objekte der Klasse auch wirklich kennzeichnen. Eine Methode berechne_bruch() würde in der Klasse videorecorder keinen Sinn machen.
Verweise Siehe Praxisteil, Kategorie Klassen
Datenelemente Beschreibung Variablen, die innerhalb einer Klasse definiert werden, stellen die Datenelemente der Klasse dar.
Anwendung Wird eine Instanz von der Klasse gebildet, wird der Instanz Speicher für die Datenelemente zur Verfügung gestellt. Jede Instanz hat also ihre eigene Kopie der Datenelemente, die man daher auch Instanzvariablen nennt. Gleichzeitig wird die Instanzbildung dazu genutzt, die Datenelemente zu initialisieren. Diese Aufgabe übernimmt der Konstruktor. Klassen stellen einen eigenen Gültigkeitsbereich dar, d. h., die Instanzvariablen verfallen mit ihrer Instanz (wofür der Destruktor Sorge trägt), und Namen aus anderen Gültigkeitsbereichen werden verdeckt. Neben der Deklaration von Datenelementen bekannter Typen ist es auch möglich, innerhalb einer Klasse eine lokale Typdefinition vorzunehmen.
Warnung Datenelemente ● ●
●
können als public, protected oder private deklariert werden. werden bei der Instanzbildung eingerichtet und zusammen mit der Instanz aufgelöst. können als static, const, volatile oder mutable deklariert werden.
Beispiele class demo { int anzahl; int *feld; public: char *str; ...
168
// private Daten
// public Daten
Statische Datenelemente
Verweise Siehe Konstruktor und Destruktor Siehe Zugriffsspezifizierer
Statische Datenelemente Beschreibung Statische Datenelemente werden bei der Instanzbildung nicht kopiert. Statt dessen greifen alle Instanzen auf die gleiche Variable zu.
Anwendung Statische Elemente müssen außerhalb der Klasse, im umgebenden namespace (oder Dateibereich), definiert werden und können nicht nur über Instanzen, sondern auch über den Klassennamen angesprochen werden:
Warnung Statische Elemente können nicht als auto, register oder extern deklariert werden.
Beispiele class Demo { public: static int instanzenzaehler; Demo() {instanzenzaehler++;} ~Demo() {instanzenzaehler--;} }; int Demo::instanzenzaehler = 0; int main() { Demo lok; cout * oder über Friends der Klasse zugegriffen werden.
Zugriff über Instanz
.,->
Der Zugriff unterliegt den Einstellungen durch die Modifizierer public, protected und private, d. h. Elemente, die in der Klasse als protected oder private deklariert sind (dies gilt auch für ererbte Elemente), können auf diese Weise nicht angesprochen werden. class demo { int wert1; // standardmäßig private int func1(); public; int wert2; int func2(); }; int main() { class demo d, *ptr_d; int dummy; dummy = d.wert1; // Fehler dummy = ptr_d->func1(); // Fehler dummy = d.wert2; dummy = ptr_d->func2(); }
179
Sprachkonzepte und Syntax
Zugriff über Zeiger auf Klassenelemente
.*,->*
Auch hier gilt, daß nur auf public-Elemente zugegriffen werden kann. Wie die Syntax zur Definition, Initialisierung und Dereferenzierung von Zeigern auf Klassenelemente aussieht, entnehmen Sie bitte dem Abschnitt Operatoren, Datenzugriff.
Zugriff über Friends
friend
Manchmal ist es sinnvoll, die strengen Regeln der Zugriffsberechtigungen durch public, protected und private zu umgehen und einer beliebigen Funktion (also nicht bloß Klassenfunktionen) Zugriff auf protected oder private Elemente zu erteilen. Das Schlüsselwort friend ermöglicht dies. Um eine Friend-Funktion einzurichten, bedarf es zweierlei: ●
Erstens muß die Funktion in der Klasse, auf die sie Zugriff haben soll, als friend deklariert werden, und
●
Zweitens muß eine Referenz oder Instanz der Klasse an die Funktion übergeben werden.
#include using namespaces std; class demo { int wert; friend int func(demo&); // Friend-Deklaration }; // Friend-Funktion int func(demo& objekt) { return objekt.wert; } int main() { class demo d; cout