Sie sind auf Seite 1von 69

Das C++ Kompendium

INHALTSVERZEICHNIS

1. DATENTYPEN ........................................................................................................................................................ 5
1.1 GANZZAHLIGE DATENTYPEN ...................................................................................................................... 5
1.2 GLEITKOMMATYPEN .................................................................................................................................... 5
1.3 KONSTANTEN .............................................................................................................................................. 5
1.4 VARIABLEN ................................................................................................................................................. 6
1.5 EIGENE TYPDEFINITIONEN ........................................................................................................................... 7
1.6 AUFZÄHLUNGSTYPEN .................................................................................................................................. 7
1.7 KOMMENTARE ............................................................................................................................................. 8
2. EIN- UND AUSGABE ........................................................................................................................................ 9
2.1 DATENSTROMAUSGABE MIT „COUT“ ........................................................................................................... 9
2.2 KONVENTIONELLE AUSGABE ..................................................................................................................... 11
2.3 DATENSTROM-EINGABE MIT „CIN“ ............................................................................................................ 14
2.4 KONVENTIONELLE EINGABE ...................................................................................................................... 15
3. OPERATOREN ................................................................................................................................................ 18
3.1 VERGLEICHSOPERATOREN ......................................................................................................................... 18
3.2 LOGISCHE OPERATOREN ............................................................................................................................ 19
3.3 BITOPERATOREN ........................................................................................................................................ 20
3.4 ZUWEISUNGSOPERATOREN ........................................................................................................................ 21
3.5 INKREMENT UND DEKREMENT ................................................................................................................... 22
3.6 BEDINGUNGSOPERATOR ............................................................................................................................ 22
3.7 SEQUENZ-, SIZEOF-, ADRESSOPERATOR UND KLAMMERN ......................................................................... 22
3.8 TYPUMWANDLUNGEN ................................................................................................................................ 23
4. KONTROLLSTRUKTUREN ......................................................................................................................... 25
4.1 AUSWAHLANWEISUNGEN .......................................................................................................................... 25
4.2 WIEDERHOLUNGSANWEISUNGEN ODER LOOPS: „WHILE“, „FOR“, UND „DO WHILE“ ................................... 26
4.3 SPRUNGANWEISUNGEN .............................................................................................................................. 28
5. ZEIGER UND REFERENZEN ....................................................................................................................... 29
5.1 ZEIGER....................................................................................................................................................... 29
5.2 REFERENZEN.............................................................................................................................................. 31
6. ARRAYS ........................................................................................................................................................... 32
6.1 EINDIMENSIONALE ARRAYS ...................................................................................................................... 32
6.2 MEHRDIMENSIONALE ARRAYS .................................................................................................................. 34
6.3 ZEIGERARRAYS .......................................................................................................................................... 35

C++ KOMPENDIUM 2
Rev. 1.01
7. SPEICHERVERWALTUNG .......................................................................................................................... 36
7.1 SPEICHERVERWALTUNG MIT „NEW“ .......................................................................................................... 36
7.2 SPEICHERFREIGABE MIT “DELETE” ............................................................................................................ 37
7.3 ZEIGER AUF ZEIGER ................................................................................................................................... 37
7.4 DYNAMISCHE SPEICHERVERWALTUNG ...................................................................................................... 38
8. FUNKTIONEN ................................................................................................................................................. 39
8.1 DEFINITION UND AUFRUF .......................................................................................................................... 39
8.2 DEKLARATION VON FUNKTIONEN (PROTOTYPEN) ..................................................................................... 39
8.3 GÜLTIGKEITSBEREICHE ............................................................................................................................. 39
8.4 LEBENSDAUER VON VARIABLEN................................................................................................................ 40
8.5 RÜCKGABEWERTE: DIE “RETURN”-ANWEISUNG ........................................................................................ 40
8.6 PARAMETER ............................................................................................................................................... 41
8.7 FUNKTIONSNAMEN ÜBERLADEN ................................................................................................................ 42
8.8 DIE FUNKTION “MAIN” .............................................................................................................................. 42
8.9 INLINE-FUNKTIONEN ................................................................................................................................. 43
8.10 ZEIGER AUF FUNKTIONEN .......................................................................................................................... 43
9. KLASSEN ......................................................................................................................................................... 44
9.1 DEFINITION VON KLASSEN ........................................................................................................................ 44
9.2 OBJEKTE EINER KLASSE............................................................................................................................. 45
9.3 INITIALISIERUNG UND KONSTRUNKTION VON OBJEKTEN .......................................................................... 47
9.4 KONSTANTE/STATISCHE OBJEKTE ............................................................................................................. 49
9.5 FRIEND-FUNKTIONEN UND FRIEND-KLASSEN ............................................................................................ 50
9.6 SPEZIELLE K LASSEN .................................................................................................................................. 51
10. ABGELEITETE KLASSEN ............................................................................................................................... 52
10.1 MIT “PUBLIC” ABGELEITETE KLASSEN....................................................................................................... 52
10.2 MIT “PROTECTED“ ABGELEITE KLASSEN ................................................................................................... 52
10.3 MIT “PRIVATE” ABGELEITETE KLASSEN..................................................................................................... 53
10.4 REDEFINITION VON ZUGRIFFSRECHTEN/KONVERTIERUNG......................................................................... 53
10.5 NICHTVERERBBARE KLASSENELEMENTE ................................................................................................... 54
10.6 VIRTUELLE FUNKTIONEN ........................................................................................................................... 54
10.7 ABSTRAKTE KLASSEN/REIN VIRTUELLE FUNKTIONEN ............................................................................... 55
10.8 MEHRFACHVERERBUNG ............................................................................................................................. 55
11. OPERATORFUNKTIONEN .............................................................................................................................. 56
11.1 OPERATORFUNKTIONEN FÜR UNÄRE OPERATOREN ................................................................................... 56
11.2 OPERATORFUNKTIONEN FÜR BINÄRE OPERATOREN .................................................................................. 56
11.3 DER ZUWEISUNGSOPERATOR “ = ” ............................................................................................................ 57
11.2 DER INDEX-OPERATOR “ [ ] “ .................................................................................................................... 57
11.3 DER FUNKTIONSAUFRUF-OPERATOR “ ( ) “ ............................................................................................... 58
11.4 DER PFEILOPERATOR “ -> “ ....................................................................................................................... 58
11.5 DIE OPERATOREN NEW UND DELETE ......................................................................................................... 58
11.6 DER CAST-OPERATOR/KONVERTIERUNGSOPERATOREN............................................................................ 59
12. STREAMS ............................................................................................................................................................ 60
12.1 ALLGEMEINE EIN- UND AUSGABE.............................................................................................................. 60
12.2 EIN- UND AUSGABE VON DATEN ................................................................................................................ 61
12.3 EIN- UND AUSGABEOPERATIONEN MIT SPEICHERBEREICHEN..................................................................... 63
13. SPRACHERWEITERUNGEN ........................................................................................................................... 65

C++ KOMPENDIUM 3
Rev. 1.01
Allgemeine Hinweise:

Die in diesem Dokument angegebenen Verfahren sind zu Amateur- und Lernzwecken bestimmt.
Sie werden deshalb ohne Rücksicht auf die gegenwärtige Patent- oder Copyrightlage mitgeteilt.

Die Markennamen der in diesem Dokument erwähnten Firmen unterliegen generell


Warenzeichen-, Marken- oder patentrechtlichem Schutz.

Die angegeben Verfahren wurden mit großer Sorgfalt zusammengestellt. Jedoch sind Fehler nicht
völlig auszuschließen. Der Verfasser sieht sich demnach gezwungen, jegliche Haftungsansprüche
oder juristische Verantwortung für Folgen, die aus fehlerhaften Angaben resultieren, zurück zu
weisen. Der Autor ist weiterhin für jede Mitteilung von Fehlern dankbar.

© Copyright 1998 – 2007, „Das C++ Kompendium“, http://cplusplus.opensourcebook.de, Nathan Bailey.

C++ KOMPENDIUM 4
Rev. 1.01
1. Datentypen

Ganzzahlige Datentypen

• Ganzzahlige Datentypen sind: char, int, short int, long int, und bool.
• Short int und int, bzw. long int und int sind jeweils äquivalent
• Benutze „signed“ und „unsigned“ für mit oder ohne Vorzeichen (wenn eine Angabe ohne
Spezifikation des Vorzeichens erfolgt, dann wird automatisch „signed“ angenommen –
also ist z.B. „signed short“ dasselbe wie „short“)
o char: zur Speicherung von Zahlen bzw. Zeichen, gewöhnlich im ASCII-Code von
0 bis 255, z.B. char a = 65 = 01000001 = 1byte = 8bit. Beachte: „signed char“
nicht unbedingt gleich „char“!
o unsigned char: Kann die Werte von 0 bis 255 im ASCII-Code annehmen.
o signed char (mit vorzeichen), läuft im ASCII-Code von -128 bis 127. Die
Unterscheidung zwischen signed und unsigned char wird ab Beträgen über 127
wichtig!
o short (größer als „char“, aber kleiner als „int“) hat den Speicherbereich von 2
Bytes.
o int: (größer als short, aber kleiner als long), besitzt 2 Bytes für short oder 4 Bytes
für long.
o long besitzt Werte von 0 bis 232
o bool kann die zwei Werte „wahr“ oder „falsch“ annehmen, „1“ bzw. „0“.

1.2 Gleitkommatypen

• Gleitkommatypen sind: float, double, und long double.

o float: zwischen 4 Bytes und double:


o double: zwischen float und long double, gewöhnlich 8 Bytes;
o long double: gewöhnlich 10 oder 12 Bytes

1.3 Konstanten

• Konstanten: Daten, deren Wert unveränderlich ist. Unterscheidung in unbenannte (mit


Wert und Typ, aber ohne Namen) und benannte.

o unbenannte Konstanten: Dazu zählen Ganzzahlige, Gleitkomma-, Zeichen- und


Zeichenketten-Konstanten.
 Ganzzahlige: oktale (0 bis 7, stets mit 0 beginnend), dezimale (1 bis 9,
keine führende Null), oder hexadezimale (Präfix „0x“ oder „0X“).
C++ KOMPENDIUM 5
Rev. 1.01
 Zahlenwerte sind jeweils längenabhängig mit „int“, „unsinged int“, „long“,
o.Ä. darstellbar. Mit den Suffixen „l“ (long) oder „u“ (unsigned int/long)
ist der Typ explizit festlegbar.

o Gleitkomma-konstanten: sind wahlweise mit oder ohne


Vorkommateil/Nachkommateil; Exponent wird mit „e“ oder „E“ bezeichnet: z.B.
„0.46e-1;“ Sie sind grundsätzlich vom Typ double, außer explizit durch „f“ (float)
oder „L“ bzw. „l“ (long double) andersartig gekennzeichnet.
o Zeichenkonstanten: werden in Hochkommata angegeben. Sie haben den Typ
„char“.
o Escape-Sequenzen: beispielsweise sind Hochkommata nicht darstellbar, außer
durch Voranstellung von \, für Hochkommata also \’’. Der Backslash maskiert
nicht darstellbare Zeichen für Compiler – auch der Backslash selbst muss maskiert
werden! Weitere Beispiele für Escape-Sequenzen sind:
 Tabulator: \t
 Signalton: \a
 Zeilenvorschub: \n
 Seitenvorschub: \f

o Stringkonstanten: werden eingebettet in „“ (deshalb muss „ oder “ mit einem


Backslash maskiert werden).
 Stringkonstanten zu verketten geschieht über mehrere Zeilen mit „“. In
jeder Zeile und mit ; abschließen, oder mit \ , und in der letzen Zeile mit ;
 Im Speicher wird jeder String „unsichtbar“ mit \0 beendet.

o Benannte Konstanten werden mit der Syntax

const datentyp name_der_konstante = init_Wert;

eingebunden, also beispielsweise mit

const int x = 1;

ein Wert zugewiesen.

1.4 Variablen

• Variablen sind Daten, deren Wert Veränderungen unterliegen kann.


o Variablen besitzen vier Merkmale: Wert, Typ, Adresse (Lokalisation im
Speicher), und ihren Namen.
o Sie werden mit folgender Syntax eingebunden: „typ name;“ , z.B. „double x, y, z;“
o Ausgabe der Speicher-Adresse der Variable „a“ beispielsweise mittels „cout <<
&a;“
o Initialisierung: mittels der Initialisierung wird der Variablen ein Wert
zugeweisen, beispielsweise durch

C++ KOMPENDIUM 6
Rev. 1.01
int a = 1;

oder

double x = 2;

o Eine Initialisierung ist aber im Vergleich zu Zuweisung „a=1“ etwas


grundsätzlich Anderes; sie findet nur einmal bei der Definition am Anfang statt.
Sie dient zur Reservierung von Speicher für den angegeben Datentyp.

1.5 Eigene Typdefinitionen

• Eigene Typdefinitionen sind mittels folgender Syntax möglich: „typedef definition“; z.B.
„typedef unsigned long ulong;“ definiert „ulong“ als alternative Typbezeichnung von
„usigned long“.
• Anderes Beispiel: „typedef int Matrix [4][4];“ vereinbart „Matrix“ als Synonym für „int
[4][4];“ Nun ist „Matrix m;“ gleichbedeutend mit „int m [4][4];“

1.6 Aufzählungstypen

• Ein Aufzählungstyp ist ein ganzzahliger Datentyp, der mittels des Schlüsselwortes
„enum“ aufgerufen wird. Der Aufruf geschieht gemäß der Syntax:

enum [name_aufzählungstyp] { symb_konst1, symb_konst2, …


symb_konst } [variable1, variable2, … ];

Beispielsweise definiert

enum colorA {blau, rot, gelb}farbe;

einen Datentyp „colorA“ mit dem Wertebereich „blau, rot, gelb“ und vereinbart eine
Variable mit dem Namen „farbe“ des Typs „colorA“.
• enum-Variable kann nur Werte aus dem Wertebereich ihres Aufzählungstyps annehmen
(z.B. colorA farbe = blau;)
• Die Paramter „symb_konst“ bekommen Zahlenwerte von 0 an aufsteigend zugeteilt.
Möglich ist auch die eigene Definition mit „= x“ (erster nachfolgender Wert bekommt
x+1 zugewiesen).
• Zuweisungen von enum-Variablen an Variablen, z.B. des Typs „int“, sind zulässig.
Allerdings geschieht keine implizite Konvertierung von Aufzählungstypen in „int“, diese
ist nur explizit erzwingbar.

C++ KOMPENDIUM 7
Rev. 1.01
1.7 Kommentare

• Kommentare, also Erläuterungen im Quellcode, die nicht zum eigentlichen Programm


gehören, sind entweder mit „//“ (einzeilig) anzugeben, wobei man sie mit „\“ auf die
nächste Zeile erweitern kann, oder „/*“ zu beginnen und mit „*/“ zu beenden. Beispiel:

quellcode // Kommentar einzeilig

oder

quellcode /* Kommentar
mehrzeilig */

Allerdings ist die Schachtelung eines Kommentars

quellcode /* Diese Form */ ist /* nicht erlaubt */

nicht möglich.

C++ KOMPENDIUM 8
Rev. 1.01
2. Ein- und Ausgabe

Zur Ein-und Ausgabe werden nicht unmittelbare Sprachelemente von C++, sondern I/O-
Funktionen aus der Ein- und Ausgabebibliothek verwendet (auch „I/0-Stream library“
oder „Stream I/O-Bibliothek“ genannt).

Datenstromausgabe mit „cout“

• Syntax: cout << a; wenn zum Beispiel vorher mit

char a = ’A’;

der Wert von a definiert wurde, gibt

cout << a;

den Wert A aus.

• Verkettung des Transferoperators: statt

cout << x;
cout << y;

ist ebenso

cout << x << y;

oder

cout << x
<< y;
möglich.

• Formatierung: „cout“ gibt Gleitkommawerte nur mit max. sechs Stellen aus.
o Für die Formatierung existieren zwei Gruppen von Funktionen in der I/O-
Bibliothek: Sreams (s. Kap. 12) und Manipulatoren.

• Manipulatoren existieren in großer Zahl zur Modifikation der Ausgabe. Zum Beispiel
gibt es für hexadezimale oder oktale Ausgabearten Funktionen (die ohne
Funktionsklammern geschrieben werden!) wie etwa „hex“ oder „oct“: z.B

cout << hex << 123;

o Achtung: Hexadezimaler Anzeigemodus bleibt auch danach bestehen!


C++ KOMPENDIUM 9
Rev. 1.01
Erst beispielsweise ein „cout << dec;“ setzt Ausgabe wieder auf dezimal zurück.
o setiosflags(Flag_Wert): „setiosflags(128L)“ bewirkt z.B. die Mitausgabe eines
Zahlensystem-Indikator; „setiosflags(512L)“ bewirkt Großbuchstaben bei hex-
Zahlen. „setiosflags“ muss mit Include-Datei „iomanip.h“ im Programmkopf
eingeschlossen werden. Beispiel:

cout << hex << setiosflags (128L) << setiosflags(512L) << 123;

gibt „0X7B“ aus.


o Rücksetzung mit „resetioflags“, z.B. „resetioflags(128L)“.
o Parameter für setioflags: Zweierpotenzen von 20 bis 214, alle vom Typ long.
o Benannte Konstanten können als feste Parameterwerte für setiosflags definiert
werden.
o Zwei Formate für Gleitkommazahlen: „fixed“ (normal mit vor- und sechs
Nachkommastellen), scientific (eine Vorkommastelle, Nachkommastellen,
Zehnerexponent (e±DDD (mit drei Dezimal-stellen)))
o Wenn der Exponent zwischen -4 und 6 liegt, wird die Zahl immer als fixed
ausgegeben.
o Erzwinge beispielsweise eine scientific-Ausgabe mit setiosflags: z.B.

cout << setiosflags(scientific) << 123.456;

gibt 1.23457e+006 aus. Mit resetiosflags(scientific) kann man dieses wieder


rückgängig machen.
o Erzwinge beispielsweise eine Nachkomma-Nullstellen-Anzeige (denn „cout
<<123.00“ würde beispielsweise nur „123“ ausgegeben) mit

cout << setiosflags(showpoint) << 123.00;

o Standardformat: im Standardformat gibt es insgesamt sechs Ausgabestellen. Bei


„fixed“ sind es sechs Nachkommastellen. Daraus folgt:

cout << 1.234567;

gibt mit im Standardmodus 1.23457 (gerundet) und mit fixed 1.234567 aus.
o Erzwinge Stellenanzahl mit Manipulator „setprecision(stellenanzahl)“
(setprecision mit „iomanip.h“ in Programm einbinden, setprecision benötigt Int-
Parameter)
o Erzwinge Vorzeichenausgabe mit setiosflags(1024), bzw. setiosflags (0x400) in
hex.
o Erzwinge größere Breite des Ausgabefeldes mit „setw(mindestbreite)“; bezieht
sich auf jeweilige Zeile, Leerzeichen werden vorangestellt.
o Anderes Füllzeichen für Leerstellen von setw: mit „setfill(zeichen)“, z.B. cout <<
setw(12) << setfill (’#’) << 99 ergibt #######99
o Bestimme Bündigkeit (Ort an dem die Füllzeichen gesetzt werden) mit z. B.
„setiosflags(left)“ (Parameter left = 2, Parameter right = 4, Parameter internal = 8)
o Unformatierte Ausgabe: durch „put“, das eine Methode von cout ist. Sie wird
mit der Syntax „cout.put(Zeichen);“ angegeben.
C++ KOMPENDIUM 10
Rev. 1.01
Hier eine kurze Übersicht über alle Manipulatoren:

0x0001 Zwischenräume überspringen


0x0002 Linksbündig
0x0004 Rechtsbündig
0x0008 Internal
0x0010 Dezimal setzen
0x0020 Oktal setzen
0x0040 Hexadezimal setzen
0x0080 Zahlenbasis anzeigen
0x0100 Dezimalpunkt anzeigen
0x0200 Großbuchstaben (hex/Exponent)
0x0400 Pluszeichen anzeigen
0x0800 Scientific-Format
0x1000 Fixed-Format
0x2000 IO-Puffer leeren
0x4000 IO-Puffer leeren
Dec Dezimal
Oct Oktal
Hex Hexadezimal
Setprecision(int stellenzahl) Gleitkomma-Genauigkeit
Setw(int mindestbreit) Feldbreite
Setfill(int zeichen) Füllzeichen
Setiosflags(long flag) IO-Flags setzten
Resetioflags(long lfag) IO-Flags zurücksetzen

Konventionelle Ausgabe

Für die konventionelle Ausgabe ist es nötig, am Programmanfang die Include-Datei „stdio.h“
mittels „#include(stdio.h)“ einzuschließen.

• Formatierte Ausgabe: folgt der Syntax

printf(formatstring, [ parameter2, parameter3, …]);

Dabei ist mindestens der Parameter „formatstring“ notwendig. Zu beachten ist die
Trennung des Formatstrings von den Parametern mit einem Komma.
• Der Formatstring enthält gewöhnliche Zeichenkette und Formatangaben
• Die Formatangabe besteht aus „%“ und einem Kennbuchstaben, der das aktuell
gebrauchte Format anzeigt, z.B. „%d“ als Formatangabe für ganzzahligen Dezimalwert.
Beispiel:

printf(„die Zahl %d ist dreistellig“, 123);

führt zur Ausgabe „Die Zahl 123 ist dreistellig“. printf besitzt hier zwei Parameter, den
notwendigen Formatstring und den int-Wert 123. Der int-Wert wird mittels %d in den

C++ KOMPENDIUM 11
Rev. 1.01
String „123“ konvertiert. Mit %d sind auch mehrere Zahlenwerte in einen String
einordbar.

o Maskiere das %-Zeichen mit weiteren angestellten %-Zeichen


o Formatstrings können in Teilstrings mit mehreren Zeilen, oder auch beispielsweise
aus einem Zeilenvorschub \n bestehen.

Hier eine Anführung aller Formatangaben:

Formatangabe Ausgabewert
%d Dezimale Zahl typ int
%i Dezimale Zahl Typ int
%hd, %hi Dezimale Zahl Typ short
%ld, %li Dezimale Zahl Typ long
%o Oktale Zahl Typ int
%ho Oktale Zahl Typ short
%lo Oktale Zahl Typ long
%
%x, %X (Großbuchstaben bei der Ausgabe) Hexadezimale Zahl Typ int
%hx, %hX (Großbuchstaben bei der Ausgabe) Hexadezimale Zahl Typ short
%lx, %lX (Großbuchstaben bei der Ausgabe) Hexadezimale Zahl Typ long
%
%u, Dezimale Zahl vom Typ unsigned int
%hu Dezimale Zahl vom Typ unsigned short
%lu Dezimale Zahl vom Typ unsigned long
%c Zeichen

o Wenn „#“ zwischen „%“ und Kennbuchstaben eingefügt wird, wird die
entsprechende Zahlenbasis mit angezeigt (z.B. führt „printf(„%#x“, 123);“ zu
„0x7b“ als Ausgabe)
o Feldbreite und Füllzeichen: variiere Feldbreite mit der dem %-Zeichen
nachgestellten Zahl. Z.B. gibt

printf(„%2d\n“, 1234);

mit zwei Feldern nur die Hälfte der benötigten an, es geschieht als eine
automatische Erweiterung auf vier.

printf(„%8d“, 1234);

gibt die doppelte Breite an, die nicht benötigten Felder werden mit Leerzeichen
vorangestellt. Zur Verwendung anderer Füllzeichen, stelle man die Füllzeichen der
Feldzahlangabe voran:

printf(„%08d“, 1234);

gibt „00001234“ aus.


C++ KOMPENDIUM 12
Rev. 1.01
o Variation der Feldbreite auch mit * und zusätzlicher Parameterangabe möglich,
z.B. „printf(„%*d“, 8, 1234);“ für Feldbreite 8 mit 4 vorangestellten Leerzeichen
 Anzahl der Ausgabeziffern: z.B. für Breite des Feldes (hier 8) und Anzahl
der Ziffern (hier 6): „printf(„AAA\n%8.6d“, 17);“ gibt „AAA000017“ aus.
(Ausgabeziffernanzahl-Angabe auch ohne Angabe zur Breite des Feldes
möglich)
 Mit Anzahl der Ausgabeziffern und Breite des Feldes lässt sich z.B. eine
Tabelle ausgeben
 Bündigkeit: mit – Zeichen zwischen % und Feldbreitenangabe entsteht
Linksbündigkeit.

Hier eine Übersicht über die Formatangaben für Gleitkommawerte:

%f Gleitk.zahl Typ float oder double


%e, %E Gleitk.zahl Typ float oder double
%g, %G Gleitk.zahl Typ float oder double

%LF Gleitk.zahl Typ long double


%Le, %LE Gleitk.zahl Typ long double
%Lg, %LG Gleitk.zahl Typ long double

Erläuterungen:

o %f entspricht dem Format fixed bei Stream-Ausgaben (sechs Nachkommastellen).


o %e entspricht dem scientific-Format.
o Wenn bei %g der Exponent kleiner ist als -4 oder größer als die Stellenanzahl,
erfolgt eine scientific-Darstellung.
o Wenn man vor f, g oder e ein L setzt, erhält man Gleitkommawerte vom Typ long
double.
o Beispiele:
 Mit

printf(„%+f“, 12.34);

erhält man die Ausgabe „+12.340000“ (mit Plus-Zeichen).


 Mit

printf(„%08g“, 1.234);

erhält man „0001.234“ (Füllzeichen sind Nullen)


o Genauigkeit:

printf(„%5.3f\n%10.6f“, 1.2345, 1.2345);

ergibt die Ausgabe

1.235
C++ KOMPENDIUM 13
Rev. 1.01
(hier wird wegen maximal 3 Nachkommastellen gerundet) und die Ausgabe

1.234500

(zwei führende Leerzeichen wegen 10 Stellen und zwei anschließende Nullen


wegen sechs Nachkommastellen)
o mit 0: erreiche so viele Ausgabestellen für Gleitkommzahlen wie nötig, aber ohne
Nachkommastellen und Dezimalpunkt , beispielsweise ergibt

printf(„%0Lf“, 123456789.123);

die Ausgabe

123456789

(wenn man z.B. größere ganzzahlige Werte darstellen will, die das long-Format
überschreiten)
o Formatangaben für Strings mit %s: z.B. „printf(„%-12s“, String)“ resultiert in
einer linksbündigen Ausgabe „String“ mit sechs nachfolgenden Leerezeichen.
o Formatangaben für Adresswerte mit %p und %n: z.B. nach Definition

int = i;

mittels

printf(„%p“, &i);

die Adresse von i ausgeben oder mit

printf(„%X %d“, &i, &i);

die Adresse hexadezimal bzw. dezimal ausgeben


o Unformatierte Ausgabe: Gegenstück zur Stream-Funktion „put“ ist „putchar“.
Syntax: „putchar(Zeichen)“.

Datenstrom-Eingabe mit „cin“

• Syntax des Befehls cin:

cin >> Programmvariable;

Beispielsweise liest

int = i;

C++ KOMPENDIUM 14
Rev. 1.01
und
cin >> i;

einen Wert in die Variable i ein.


o Verkettung: wie bei „cout“ ist auch bei „cin“ eine Verkettung möglich, z.B. für
Eingabe von Werten von i, c, d:

cin >> i >> c >> d;

Trennung bei der Eingabe erfolgt durch Leerzeichen oder Zeilenvorschub.


o Formatierung: Hexadezimale/Oktale Eingabe auf

cin >> a >> b;

mit

0x10

und

020

möglich was beides zur Speicherung von „16“ führt, oder mit Manipulatoren dec, oct
und hex, z.B.

cin >> hex >> a >> oct >> b;

Mit

cout << a << „ “ << b;

würde „16“ ausgegeben werden.


o Unformatierte Ausgabe mit get: Syntax: cin.get() (paramterlos). Beispiel: „char c; c
= cin.get( );“ liest ein Zeichen und speichert es in c.
o cin.get mit Parameter: „char c; cin.get(c);“ liest ein Zeichen ein und speichert es in c.
o Paramterabhängige und –unabhängige Anwendung von cin.get sind äquivalent.

Konventionelle Eingabe

Für die konventionelle Eingabe ist die Datei „stdio.h“ aus der Standardbibliothek am
Programmkopf einzuschließen mittels „#include <stdio.h>;“.

• Formatierte Eingabe: Gegenstück zu „printf“ ist „scanf“, das mit folgender Syntax
verwendet wird:
scanf( formatstring, [adresse1, adresse2, …]);

C++ KOMPENDIUM 15
Rev. 1.01
Beispielsweise definiert man

int x;

und liest dann mittels

scanf(„%d“, &x);

einen Integer-Wert ein und speichert diesen in x.

Hier eine Übersicht über alle möglichen Formatangaben:

Formatangabe Ausgabewert
%d Dezimale Zahl typ int
%i Dezimale Zahl Typ int
%hd, %hi Dezimale Zahl Typ short
%ld, %li Dezimale Zahl Typ long
%
%o Oktale Zahl Typ int
%ho Oktale Zahl Typ short
%lo Oktale Zahl Typ long
%
%x, %X (Großbuchstaben bei der Ausgabe) Hexadezimale Zahl Typ int
%hx, %hX (Großbuchstaben bei der Ausgabe) Hexadezimale Zahl Typ short
%lx, %lX (Großbuchstaben bei der Ausgabe) Hexadezimale Zahl Typ long
%
%u, Dezimale Zahl vom Typ unsigned int
%hu Dezimale Zahl vom Typ unsigned short
%lu Dezimale Zahl vom Typ unsigned long
%c Zeichen

Und eine Tabelle für Formatangaben für Gleitkommawerte:

Formatangabe Ausgabewert
%f Gleitk.zahl Typ float
%lf Gleitk.zahl Typ double
% Lf Gleitk.zahl Typ long double
%e, %E Gleitk.zahl Typ float (scientific)
%ie, %IE Gleitk.zahl Typ double (scientific)
%le, %LE Gleitk.zahl Typ long double (scientific)
%g, %G Gleitk.zahl Typ float (scient/fix)
%lg, %lG Gleitk.zahl Typ double (scient/fix)
%Lg, %LG Gleitk.zahl Typ long double (scient/fix)

Erläuterungen und Beispiele:

C++ KOMPENDIUM 16
Rev. 1.01
o Eingabezeichen zählen mit „%n“ bis zu „%n“ mittels „int scount“; so lässt sich
die Anzahl der eingelesenen Zeichen speichern mittels „scanf(„%ld%n, &entry,
&scount);“
o Eingabewerte überspringen mit „*“: z.B. werden mittels

int field1, int field2;


printf(„Datensatz eingeben: “);
scanf(„%d %*d %d“, &field1, &field3);

zwar drei Eingabewerte gelesen, aber nur der erste und dritte berücksichtigt.
o Feldbreite: beispielsweise nach

int i, j;

und

scanf(„1%d %2d“, &i, &j);

eine Eingabe von

145

machen, was

1 45

als Ausgabe bewirkt.


o Auch reguläre Zeichen sind mit scanf einlesbar: z.B. nach „int hours, mins;“
können reguläre Zeichen mittels „scanf(„%d:%d“, &hours, &mins);“ eingelesen
werden.
o %-Zeichen muss mit %-Zeichen maskiert werden.

• Unformatierte Eingabe: getchar( ). Z.B. c = getchar; liest ein Zeichen ein und speichert
es in c.
o Bei mehreren eingegebenen Zeichen: Puffer wird nicht geleert, Vorsicht!
o Konsoleneingabe (Direktes Lesen von der Tastatur ohne Pufferrückstände, ohne
Bildschirmanzeige) mit „getch“ und „getche“: diese beiden Funktionen benötigen
den Einschluss der Header-Datei „conio.h“ in das Programm.

C++ KOMPENDIUM 17
Rev. 1.01
3. Operatoren

• Operatoren sind in C++ gleichwertig mit Funktionen. Operatoren mit einem Operanden
werden „unär“, solche mit zwei Operanden „binär“, solche mit drei Operanden „ternär“
genannt.
• Operatoren kommen in 17 verschiedenen Prioritätsstufen vor. Operatoren mit höheren
Prioritätsstufen werden vor anderen mit niedrigeren Prioritätsstufen ausgeführt, wie z.B.
in der Mathematik das Mal-Zeichen Priorität vor dem Pluszeichen hat.
• Generell kann man mittels Klammersetzung die Prioritäten umgehen.
• Für unäre Operatoren (Priorität 15), Zuweisungsoperatoren (Priorität 2) und für den
ternären Bedienungsoperator geht die Assoziativität (d.h. die Richtung der Auswertung
bei gleichwertigen Operatoren) von rechts nach links. Bei allen übrigen geht sie von links
nach rechts.

Hier eine Übersicht über einige Operatoren und deren Assoziativität:

Operator Bedeutung Priorität Assoziativität


+ Unäres Plus 15 Rechts nach links
- Unäres Minus 15 Rechts nach links

* Multiplikation 13 Links nach rechts


/ Division 13 Links nach rechts
% Divisionsrest 13 Links nach rechts

+ Addition (binäres Plus) 12 Links nach rechts


- Subtraktion (binäres Minus) 12 Links nach rechts

Vergleichsoperatoren

• Vergleichsoperatoren sind binäre Operatoren.

Hier eine Übersicht über alle binären Operatoren

Operator Bedeutung Priorität Assoziativität


== Gleich 9 Links nach rechts
!= Ungleich 9 Links nach rechts
> Größer 10 Links nach rechts
>= Größer/gleich 10 Links nach rechts
< Kleiner 10 Links nach rechts
<= Kleiner/gleich 10 Links nach rechts

C++ KOMPENDIUM 18
Rev. 1.01
• Ausgabewert aller dieser Operatoren ist entweder „1“ (angewandte Operation besteht)
oder 0 (Operation besteht nicht), d.h. sie haben einen True/False-Rückgabewert.
• Jeder von 0 verschieden Wert ist in C++ als „logisch wahr“ (true) interpretierbar (z.B.
sind 3 oder -157 true, dagegen x*y*0 oder -1+1 beide false);
• Beachte: Bei z.B. cout << (x == y); muss rechter Operand geklammert werden, da
Transferoperator << höhere Priorität hat!

Logische Operatoren

• Logische Operatoren sind entweder unär oder binär.

Hier eine Übersicht über die logischen Operatoren:

Operator Bedeutung Priorität Assoziativität Beispiel


! Logisches 15 Rechts nach !x
NICHT (unär) links
&& Logisches UND 5 Links nach x && y
(binär) rechts
|| Logisches 4 Links nach x || y
ODER (binär) rechts

• Compilerabhängig stehen auch die Schlüsselwörter „not“ für „!“, „and“ für „&&“ und
„or“ für „||“ zur Verfügung.
• Wert der logischen Ausdrücke ist immer entweder „1“ oder „0“ (true oder false)
• Logisches UND: Verknüpfung mit UND (&&) liefert nur dann den Wert 1 (true), wenn
beide Operanden ungleich Null sind, z.B. „3 && 7“, „1 < 2“, „(1 <2) && (3 < 4)“.
• Logische Auswertung von binären Operatoren geschieht immer von links nach rechts –
Wenn der erste Wert mit false bereits feststeht, wird der rechte Wert nicht mehr bestimmt.
• Logisches ODER: für ODER ist ein Ausdruck ist vom Wert 1 (true) nur dann, wenn
mindestens einer der Operanden true ist. Daraus folgt, er ist nur dann falsch, wenn beide
falsch sind. Die Auswertung vollzieht sich von links nach rechts wie bei UND. Wenn der
erste Wert mit true schon feststeht, wird der rechte Wert nicht mehr bestimmt.
• Logisches NICHT: der unäre Operator NICHT liefert den Wert 1, wenn der Operand den
Wert 0 hat und umgekehrt. Beachte: beispielsweise liefern „a=-1;“, „!a>0“ und „!(a>0)“
nicht denselben Wert! Die erste Anweisung liefert „0“, da „!a“ den Wert 1 hat, die zweite
liefert „1“, da „a>0“ Wert 0 hat (Negationsoperator hat höhere Priorität (15) als
Größenoperator (10)).
• Äquivalenzbeziehung: „!a“ liefert unabhängig von a immer denselben Wert wie „a==0“.

C++ KOMPENDIUM 19
Rev. 1.01
Bitoperatoren

Bitoperatoren führen logische Verknüpfungen mit jedem einzelnen Bit von ganzzahligen
Datenobjekten durch oder verschieben sämtliche Bits eines ganzzahligen Datenobjekts um
eine bestimmmte Zahl von Positionen nach links oder rechts („Schiebeoperatoren“).

• Bitlogische Operatoren:

Operator Bedeutung Operatorsymbol Assoziativität Priorität


~ Bitweises NICHT compl Rechts nach links 15
& Bitweises UND bitand Links nach rechts 8
^ Exklusives bitweises xor Links nach rechts 7
ODER
| Inklusives bitweises bitor Links nach rechts 6
ODER

o Bitweises NICHT: „~“ gibt den umgekehrten binären Code einer Zahl aus: aus allen
Nullen werden Einsen und umgekehrt oder aus „5“ wird „-5“. „1“ ist ein gesetztes Bit,
„0“ ist ein ungesetzes.
o Bitweises UND: „&“ gibt genau dann ein gesetztes Bit aus, wenn beide Ausgangsbits
gesetzt sind. Beispielsweise besitzen „char Buchstabe_1=a;“, und „char
Buchstabe2=b;“ einen binären Code – die erste Bitstelle des Resultats ist gesetzt,
wenn beide ersten Bitstellen der Ausgangscodes gesetzt sind, ansonsten ist es
ungesetzt.
o Bitweises ODER: „^“ ist ein sogenanntes nichtexklusives ODER: Ergebnisbit ist 1,
wenn mindestens eines der Ausgangsbits gesetzt ist. Wenn beide nicht gesetzt sind, ist
das Ergebnisbit 0.
o Bitweises ODER: „|“ ist ein exklusives ODER: Das Ergebnisbit ist nur dann gesetzt,
wenn beide Ausgangsbits unterschiedliche Werte besitzen, ansonsten ist es ungesetzt.

• Schiebeoperatoren besitzen zwei ganzzahlige Operanden und verschieben die Bits des
linken Operanden um so viele Stellen nach links oder rechts, wie der rechte Operand
angibt. Die Bits werden also herausgeschoben, und ungesetzte Bits auf der anderen Seite
ergänzt.

o Mit „<<“ wird eine Linkverschiebung (Priorität 11, Assoziativität links nach rechts)
erreicht, mit „>>“ eine Rechtsverschiebung, z.B. verschiebt „12351 << 5“ alle Bits
von 12351 um fünf Positionen nach links. Eine Änderung des Ausgabewerts erfolgt
nur bei Konstanten, nicht aber bei Variablen. Wenn Variablen nach einer
Verschiebung einen neuen Ausgabewert besitzen sollen, führe man beispielsweise

int a = 12351;

und
a = a << 5;

durch. Ein „a << 5;“ genügt nicht, eine explizite Zuweisung ist erforderlich.
C++ KOMPENDIUM 20
Rev. 1.01
o Um Verwechslungen mit dem Transferoperator zu vermeiden, sollten Verschiebungen
immer eingeklammert werden.
o Wenn beim Herausschieben der Bits keine 1-bit Stellen, sondern nur 0-bit Stellen
betroffen sind, entspricht eine Verschiebung um n Positionen nach links einer
Multiplikation mit 2n.
o Mit „>>“ erfolgt eine Rechtsverschiebung von n Bits aus dem Operanden, aber links
wird nicht immer dieselbe Anzahl von Stellen nur mit Nullen aufgefüllt. Nur mit
einem Operanden, der ein unsigned-Objekt ist, wird ausschließlich mit Nullen
aufgefüllt.
o Wenn keine 1-bit Stellen getroffen werden, entspricht die Rechtsverschiebung einer
Division durch 2n.

Zuweisungsoperatoren

• Zuweisungoperatoren sind binäre Operatoren. Hier eine Übersicht über alle


Zuweisungsoperatoren:

Operator Bedeutung Priorität Assoziativität


= Einfache Zuweisung 2 Rechts nach links
+= Zuweisung mit Addition 2 Rechts nach links
-= Zuweisung mit Subtraktion 2 Rechts nach links
*= Zuweisung mit Multiplikation 2 Rechts nach links
/= Zuweisung mit Division 2 Rechts nach links
%= Zuweisung mit Modulo-Operation 2 Rechts nach links
<<= Zuweisung mit Linksverschiebung 2 Rechts nach links
>>= Zuweisung mit Rechtsverschiebung 2 Rechts nach links
&= Zuweisung mit bitweisem UND 2 Rechts nach links
(Schlüsselwort and_eq)
|= Zuweisung mit inkl.bitw. ODER 2 Rechts nach links
(Schlüsselwort or_eq)
^= Zuweisung mit exkl.bitw. ODER 2 Rechts nach links
(Schüsselwort xor_eq)

• Linker Operand muss ein veränderbarer L-Wert („left value“) sein, also ein Objekt, das
einen Bereich im Speicher bezeichnet, z.B. eine Variable (oder const-Objekte, die nicht
modifizierbar sind, aber eine Speicherregion bezeichnen).
• Einfache Zuweisungen geschehen mit “=”.
• Zusammengesetzte Zuweisungen: z.B. ist „a += b“ eine Kurzschreibweise für „a = a
+b“, ebenso ist „a <<=b“ ein kurzes „a = a << b“ (analog für alle anderen Operatoren).
Zuweisungsoperatoren verändern also die Variablen und speichern das Ergebnis in die
Variable ab.

C++ KOMPENDIUM 21
Rev. 1.01
Inkrement und Dekrement

• Der Inkrement-Operator erhöht den Wert eines L-Wertes um 1 (Kurzschreibweise für „a


= a +1“ oder „a +=1“). Entsprechend erniedrigt der Dekrement-Operator den Wert um 1.

Operator Bedeutung Priorität Assoziativität Beispiel


++ Inkrement 15 Rechts nach x++ (Postfix)
links ++x (Präfix)
-- Dekrement 15 Rechts nach x-- (Postfix)
links --x (Präfix)

• Beispiel: „a++“ erhöht den Wert von a um 1, ebenso „++a“; Umgekehrt verringert „a--“
den Wert um 1, ebenso „--a“. Hier ist also die Prä- oder Post-Notation des Operators nicht
von Bedeutung
• Bei „int a = 1“, „int b“ und „b = ++a“ wird vor der Zuweisung von b inkrementiert bzw.
bei „b = --a“ wird vor der Zuweisung an b dekrementiert.
• Bei „int a = 1“, „int b“ und „b = a++“ wird nach der Zuweisung inkrementiert bzw. bei
„b=a-- “ wird nach der Zuweisung an b dekrementiert.
• Nebeneffekte: Inkrement- und Dekrement-Operatoren sind Operatoren mit sogenannten
„side effects“, denn sie verändern den Wert ihres Operanden (im Gegensatz zu logischen
Operatoren) Mögliche Fehlerquellen sollten beachtet werden: Beispielsweise bei „a &&
b++“ wird, wenn a den Wert Null hat, der zweite Operand gar nicht mehr ausgewertet und
somit das Inkrement nicht ausgeführt, oder wenn die Reihenfolge der Auswertung
compilerabhängig ist, beispielsweise bei „printf („%d %d“, ++a, b = a);“

Bedingungsoperator

• Operator „?“ ist als einziger ternär (Priorität 3, Assoziativität von rechts nach links). Er
wertet eine Bedingung aus, von der es abhängt, welchen Wert der gesamte Operator-
Ausdruck erhält Beispiel: „Ausdruck1? Ausdruck2 : Ausdruck3;“
• Hierbei ist „Ausdruck1“ die Bedingung des gesamten Ausdrucks; wenn er „true“ ist, dann
wird Ausdruck2 ausgewertet und Gesamtausdruck erhält den Wert von Ausdruck2; wenn
er „false“ ist, dann wird Ausdruck3 ausgewertet und Gesamtausdruck erhält den Wert von
Ausdruck3.
• Der Bedingungsoperator sollte am besten immer geklammert werden, da er eine geringe
Priorität (3) besitzt.

Sequenz-, Sizeof-, Adressoperator und Klammern

• Der Vollständigkeit halber wird hier noch der Sequenzoperator „ , “ angeführt. Mit dem
Sequenzoperator geschieht eine Zusammenfassung von Anweisungen, z.B. wird

a++;
b = 5;
C++ KOMPENDIUM 22
Rev. 1.01
mit dem Sequenzoperator in eine Zeile gefasst:

a++, b = 5;

o Wert des Gesamtausdrucks ist gleich dem des letzen Operanden. Der
Sequenzoperator hat die niedrigste Priorität 1.

• Der Sizeof-Operator ermittelt Größe eines Ausdrucks oder Datentyps. Er ist unär, hat
Priorität 15, und die Assiozativität geht von rechts nach links.
o Wenn der Operand ein Ausdruck ist, ist die Notation ohne Klammern möglich,
z.B. ist „char x; sizeof(x)“ gleichwertig mit „char x; sizeof x;“ Wenn der Operand
ein Datentyp ist, sollten Klammern gesetzt werden, z.B. „sizeof(char)“.

• Der Adress-Operator „&“ ermittelt die Speicheradresse des Operanden und benötigt
deshalb einen L-Wert. Er hat Priorität 15, die Assiozativität ist von rechts nach links.
• Klammern kommen in folgenden Varianten und Eigenschaften vor:

Operator Bedeutung Priorität Assoziativität Beispiel


() Funktionsklammern 16 Links nach funktionX (y,z)
(function call) rechts
() Objekt-Konstruktion 16 Links nach klasseX (y,z)
(value construction) rechts
[] Array-Indizierung 16 Links nach arrayX [y]
(subscript) rechts
() Explizite 15 Rechts nach (typX) y
Typkonvertierung links typX (y)

Typumwandlungen

• Grundsätzlich benötigen Operatoren Operanden gleichen Typs. Bei unterschiedlichen


Typen müssen sie aneinander angegelichen, also konvertiert werden, entweder implizit
durch den Compiler oder explizit durch eine Anweisung.
• Implizite Typumwandlung geschieht es durch sog. „integral promotion“ oder „usual
arithmetic conversions“; bei integral promotion (Ganzzahlerweiterung) werden char oder
short-Typen in int (bzw. unsigned int)-Typen konvertiert. Konvertierung geht immer auf
„das Maximale“, kleinere werden auf größere Typen umgewandelt. Beispielsweise wird
bei zwei Operanden, einer „long“, einer „int“, der zweite nach „long“ konvertiert. Die
Ausgabe erfolgt dann ebenfalls im Format „long“.
• Typkonvertierung bei Zuweisungen: Mögliche Datenverluste treten auf, wenn größerer
in kleineren Typ konvertiert wird, z.B. bei „int i; double d = 1.23; i = d;“ gehen die
Nachkommastellen verloren. Ebenfalls tritt Datenverlust bei float zu int, long zu float,
double zu float, oder long zu short auf.
• Bei der Konvertierung wird lediglich eine Kopie des Datenwertes erstellt, der alte Wert
bleibt erhalten.

C++ KOMPENDIUM 23
Rev. 1.01
• Beachte bei Berechnungen und Zuweisung die Arithmetik, in der berechnet wird.
Ausschlaggebend ist jeweils der Typ des linken Operanden.
• Explizite Typumwandlung geschieht mit Hilfe des cast-Operators „( )“ mit folgender
Syntax: „(typ) Ausdruck (herkömmliche Notation)“ oder „typ (Ausdruck) (funktionale
Notiation)“. Die Funktionale Notation ist aber nur dann anwendbar, wenn der Typ ein
einfacher Datentypname ist z.B. long, double, o.Ä.

Hier eine Übersicht über alle C++ Operatoren:

Priorität Operator Bedeutung Assoziativität


17 :: Bereichszugriff (unär) Rechts nach links
17 :: Bereichszugriff (binär) Links nach rechts
16 () Funktionsklammern Links nach rechts
16 () Objektkonstruktion Links nach rechts
16 [] Array-Index Links nach rechts
16 . -> Komponentenauswahl Links nach rechts
15 () Typkonvertierung Rechts nach links
15 Sizeof Größe in Bytes Rechts nach links
15 & Adress-Ermittlung Rechts nach links
15 * Verweis/Indirektion Rechts nach links
15 ~ Bitweise Negation Rechts nach links
15 ! Logische Negation Rechts nach links
15 + - Plus, Minus (unär) Rechts nach links
15 ++ -- Inkrement, Dekrement Rechts nach links
15 new, delete Speicherverwaltung Rechts nach links
14 .* ->* Komponentenauswahl Links nach rechts
13 * / % Multiplikation Division Modulo Links nach rechts
12 + - Plus, Minus (binär) Links nach rechts
11 << >> Bit-Verschiebung Links nach rechts
10 < <= > >= Größer(gleich)/Kleiner(gleich) Links nach rechts
9 == != Gleichheit Links nach rechts
8 & Bitweises UND Links nach rechts
7 ^ Bitweises ODER (exklusiv) Links nach rechts
6 | Bitweises ODER (inklusiv) Links nach rechts
5 && Logisches UND Links nach rechts
4 || Logisches ODER Links nach rechts
3 ?: Bedingungsoperator Rechts nach links
2 = += -= *= Zuweisung Rechts nach links
/= %= <<=
>>= &= ^= |=
1 , Sequenzoperator Links nach rechts

C++ KOMPENDIUM 24
Rev. 1.01
4. Kontrollstrukturen

Die Sprache C++ bietet vier verschiedene Varianten von Kontrollstrukturen:


Selektionsanweisungen, Auswahlanweisungen, Iterationsanweisungen, und
Sprunganweisungen.

Auswahlanweisungen

• Auswahlanweisungen werden mit den Schlüsselwörtern if, if else und switch angegeben.
• if hat die Syntax: „if (ausdruck)“. Beispiel:

if(ausdruck)
{
anweisung(en)
}

o Hat „ausdruck“ den Wert true, wird „anweisung(en)“ ausgeführt – wenn nicht,
wird zur nächsten Zeile nach „if“ übergangen.
o Wenn mehrere Anweisungen von if abhängig sein sollen, ist die Klammerung mit
„{ }“ zu benutzen.
• if / else hat die Syntax: „if (ausdruck) … else“. Beispiel:

if(ausdruck)
anweisung(en)1
else
anweisung(en)2

o Sollte „anweisung(en)“ aus mehreren Anweisungen bestehen, sollten []-Klammern


gesetzt werden.
o Wenn „ausdruck“ den Wert true hat, dann wird anweisung(en)1 nach „if“
ausgeführt. Wenn „ausdruck“ den Wert false hat, dann werden anweisung(en)2
nach „else“ ausgeführt.
o Schachtelung von if und else ist möglich nach folgendem Schema

if
else
if
else

o Vorsicht: Wenn beispielsweise die erste Selektionsanweisung eine if else-


Anweisung sein soll und dazwischen eine if-Anweisung geschaltet werden soll,
muss die zweite if-Anweisung geklammert werden, da sonst Compiler else
Anweisung zum zweiten if rechnet. Um Irrtümer zu vermeiden, sollten immer
entsprechende Klammern gesetzt werden.
C++ KOMPENDIUM 25
Rev. 1.01
• switch: „switch“ ermöglicht die Auswahl zwischen beliebig vielen Alternativen Damit ist
„switch“ gleichwertig mit geschachteltem „if else“. „switch“ hat die folgende Syntax:
„switch(ausdruck)“, wobei die Alternativen mit einem vorangestellten „case“
gekennzeichnet werden. Sollte keine der Möglichkeiten von „case“ den Wert true
zurückgeben, wird die Anweisung nach „default“ ausgeführt. Beispiel:

switch(ausdruck)
{
case konst1:
anweisung(en)1
case konst2:
anweisung(en)2

default:
anweisung(en)3
}

o „ausdruck“ nach switch muss ganzzahlig sein. Nach der Reihe wird er mit jeder
der ebenfalls ganzzahligen Konstanten verglichen („case“). Wenn eine
Übereinstimmung vorliegt, wird die entsprechende anweisung(en), hier
anweisung(en)1 oder anweisung(en)2 ausgeführt. Wenn keine vorliegt, werden die
Anweisungen nach „default“ ausgeführt – hier anweisung(en)3. Allerdings muss
die default-Option nicht zwingend vorhanden sein; und sie muss auch nicht
unbedingt die letzte sein.
o Wenn kein Wert übereinstimmt und keine default-Option vorhanden ist, wird zur
nächsten Zeile nach switch übergegangen.
o Beachte: wenn Übereinstimmung vorliegt, werden alle nachfolgenden
Anweisungen, auch die anderer cases, ausgeführt. Deshalb empfiehlt es sich, jede
der mit „case“ bezeichneten Alternativen mit einem „break“ abzuschließen. Dies
führt nach Ausführung der anweisung(en) zum Stopp, damit nicht alle
nachfolgenden Anweisungen, auch die der anderen „cases“, ausgeführt werden.

Wiederholungsanweisungen oder loops: „while“, „for“, und „do


while“

• while hat die Syntax „while(ausdruck)“ mit darauf folgenden Anweisungen.


o Die Anweisungen werden solange ausgeführt, wie die Auswertung des ausdrucks
true ergibt, z.B. gibt

int i = 1;
while (i < 11)
{ cout << i << „ “; i++; }

die Zahlen 1 bis 10 aus. „i“ wird hier als Kontrollvariable bezeichnet
o Endlosschleifen sind mit „while“ möglich. Abbruch einer Endlosschleife
beispielsweise durch: „kbhit( )“ („Keyboard-Hit“, das mit „#include <conio.h>“
C++ KOMPENDIUM 26
Rev. 1.01
am Programmkopf eingeschlossen werden muss). Durch Drücken einer beliebigen
Taste wird das Programm beendet:

if (khbit( ))
exit (0);

o Oder: solange keine Taste gedrückt, sollen Schleifenanweisungen ausgeführt


werden:

while (!kbhit( )) { anweisung(en) }

o Vorsicht bei Endlosschleifen! Sie können zum Absturz des Programms oder des
ganzen Betriebssystems führen! Beende eine Schleife durch Eingabe von Strg+Z
(sog. „End of File“-Zeichen, das im Code mit „EOF“ eingebunden wird).
o „while“ eignet sich vorzüglich, um eine Pufferentleerung zu erreichen. Leere den
Puffer beispielsweise mittels „while (cin.get ( ) != ’konstante’)

• for wird mit folgender Syntax verwendet:

for ( init_wert ; bedingung ; re_init_wert )


anweisung(en)

Beispiel:

for (int i = 0; i<100 ; i++)


anweisung(en)

Bei mehreren Anweisungen, die durch „for“ ausgeführt werden sollen, sind diese in { }-
Klammern zu setzen.
o Wenn die Bedingung für den init_wert den Wert true hat, werden „anweisung(en)“
ausgeführt. Danach wird Bedingung für re_init überprüft. Falls wiederum ein true-
Wert ermittelt wird, werden die „anweisung(en)“ wiederum ausgeführt.
o Beispiel:

int i;
long faculty10 = 1;
for (i = 2; i < 11; i++)
faculty10 *=i;

berechnet den Wert von 10!.


o Eine fehlende Bedingung führt zu Endlosschleife, da immer „true“ ausgewertet
wird.
o Beachte: Für die bisherigen Kontrollstrukturen ist kein Semikolon am Ende
notwendig

• do while hat eine Syntax nach folgendem Schema:

do
C++ KOMPENDIUM 27
Rev. 1.01
anweisung(en)
while (ausdruck);

Hier ist ein Semikolon am Ende notwendig!


o mit „do while“ werden anweisung(en) auf jeden Fall einmal ausgewertet, danach
abhängig vom Rückgabewert der while-Schleife.

Sprunganweisungen

Sprung oder Kontrolltransferanweisungen sind: „break“, „continue“, „goto“, und „return“.

• break hat die einfache Syntax „break;“ damit wird z.B. eine if-Schleife beendet.
• continue: kommt ausschließlich in Wiederholungsanweisungen vor. Mit „continue“
bricht ein einzelner Schleifendurchgang ab und setzt sich mit dem nächsten Durchgang
fort.
• goto: springt an eine mit einem Label gekennzeichnete Stelle. „goto“ hat die Syntax:
„goto label;“ Ein Label kann beliebig definiert werden, beispielsweise „anfang:“, wenn
das Programm an einem gewissen Punkt zu der mit „anfang“ markierten Stelle
zurückspringen soll. Dabei wird das Label mit der Syntax „label:“ eingeschlossen – es
wird einfach vor die Stelle im Code gesetzt, zu der gesprungen werden soll.
• return: siehe Punkt 8.5.

C++ KOMPENDIUM 28
Rev. 1.01
5. Zeiger und Referenzen

Bei Zeigern und Referenzen spricht man von abgeleiteten Datentypen, die keine elementaren
Datentypen sind, da sie in Abhängigkeit zu Daten elementaren Typs stehen.

Zeiger

• Ein Zeiger ist ein Datenobjekt (konstant oder variabel), das die Adresse eines anderen
Objektes speichert bzw. darstellt.
o Beispielsweise ist der Adressoperator „&“ ein Zeiger.
o Zeiger oder Adresskonstanten verweisen immer auf dieselbe Speicherstelle,
Adressvariablen dagegen auf Adressen verschiedener Objekte.

• Zeigervariablen haben die Syntax:

datentyp_des_verweisobjekts * name_des_zeigers;

Das * - Zeichen bedeutet soviel wie „ist Zeiger auf“.


o Initialisierung: z.B.

short x;
short *zx = &x;

Hier ist „zx“ ist ein Zeiger auf „x“, der mit der Adresse von x initialisiert wird.
o Mittels neuer Zuweisungen können neue Werte für Zeigervariablen festgelegt
werden.
o Zeiger können mittels logischer Operatoren verglichen werden.

• Derefernzierte Zeiger: ist

short a, b;
short *zs = &a;

definiert, kann die Zuweisung

b=a;

durch

b = *zs ;

ersetzt werden. Beispielsweise erhält a mittels „a = 123“ oder „*zs = 123“ den Wert 123.
Hier wird aus der Adresse des Datenobjekts das Datenobjekt selbst gemacht.

C++ KOMPENDIUM 29
Rev. 1.01
o Dadurch kann man, wenn z.B. „*p = &i“ und „*q = &k“ definiert worden sind,
mittels „cout << *p + *q“ den Wert der Summe von i und k berechnen und
anzeigen lassen. Hier ersetzen die Zeiger die eigentlichen Objekte.
• Ein Konstanter Zeiger verweist nur auf bestimmte Stelle im Speicher mittels des
Schlüsselworts „const“ – deshalb muss ein konstanter Zeiger wegen den Definitionsregeln
für konstante Objekte initialisiert werden.
o Beispiel:

int *const p = &a;

(nicht: „const int *p = &a“. Dies würde einen Zeiger für beliebige konstante Datenobjekte
vom Typ int ergeben!)
o Die Zuweisung „*p = 123“ wäre in diesem Beispiel äquivalent zu „a = 123“.
Konstante Zeiger kann man nicht einem neuen Objekt zuordnen.

• Nullzeiger: unter der Adresse 0 werden nie Daten gespeichert – gewöhnlich wird er nur
zur Ausgabe eines Fehlers verwendet.
• Generische Zeiger: Falls eine Adresse gespeichert werden soll, ohne die Typangabe
vorher festzulegen, ist ein generischer Zeiger gefragt. Dieser hat die Syntax: „void*“, z.B.

int a; double b;
void *vz;

„void *vp“ kann jetzt sowohl als

vz = &a;

als auch als

vz = &b;

gesetzt werden.
o Wenn allerdings dem generischen Zeiger „*vz“ ein Wert zugewiesen werden soll,
muss eine explizite Typangabe erfolgen, in die *vz konvertiert wird, was z.B.
mittels

*(double*)vz = 1.001;

erreicht werden kann. Jetzt sind allerdings keine Zuweisungen von void* an
andere Zeigertypen mehr möglich, außer mittels einer expliziten Konvertierung,
z.B.

intpointer = (int*)voidpointer;

C++ KOMPENDIUM 30
Rev. 1.01
Referenzen

Der Zugriff auf ein Datenobjekt kann unter einem anderen Namen geschehen als dem Namen
des Objekts selbst, und zwar mittels Referenzen. Referenzen haben deshalb auch dieselbe
Syntax wie Variabeln:

datentyp &referenz_name = ausdruck;

• Referenzen müssen initialisiert werden, weil sie einen konstanten Alias-Namen für das
Objekt festlegen – bei Variablen muss der Initialisierungswert ein L-Wert sein.
• Zeiger auf Referenzen und Referenzen auf Referenzen sind nicht möglich.
• Beispiel:

int i;
int &ref = i;

definiert eine Referenz „ref“ auf i.


• Bei Referenzen auf Konstanten muss der Initialisierungswert nicht unbedingt ein L-Wert
sein, z.B. legt

const int a = 2;
const int &ref_a = a;

eine Referenz „ref_a“ auf die int-Konstante „a“ fest.

C++ KOMPENDIUM 31
Rev. 1.01
6. Arrays
• Array, zu Deutsch „Vektor“, „Feld“ oder „Tabelle“, ist ein zusammenhängender Bereich
im Speicher, dessen einzelne Bytes zu einer logischen Einheit zusammengefasst werden.
• Die Elemente von Arrays sind die elementaren Datentypen, außerdem Zeiger, Klassen
oder Aufzählungstypen, nicht aber Referenzen oder Funktionen (aber Zeiger auf
Funktionen).

Eindimensionale Arrays

• Das klassische eindimensionale Array hat die Syntax:

typ_der_arrayelemente name_des_arrays [anzahl_der_arrayelemente];

beispielsweise „long tabelle1 [10]“. Das Array heißt hier „tabelle1“, und besteht aus 10
Elementen des Typs „long“.
• Initialisierung geschieht z.B. mittels

long tabelle1 [10] = { Wert1, Wert2, …, Wert10 };

Für eine Initialisierung größerer Arrays ist ein Loop empfehlenswert.


• Die Indizierung der Elemente beginnt bei 0, nicht bei 1! Im obigen Beispiel läuft
deshalb die Indizierung von 0 bis 9, und nicht wie man zunächst annehmen könnte von 1
bis 10.
• Da die Elemente des Arrays gewöhnliche Variablen sind, lassen sich alle üblichen
Operationen mit ihnen durchführen, z.B. Zuweisung (tabelle1[0] = 1234;) oder Dekretion
(tabelle1[1]--;), oder für die Ausgabe aller Elemente mit einer Schleife

int i;
for ( i = 0; i < 10 i++)
cout << tabelle1[i] << „ “;

• Eingabe: vor allem bei größeren Arrays empfiehlt sich eine Eingabe mit einer Schleife,
z.B. wenn die Array-Werte über Tastatur eingegeben werden sollen:

for ( i = 0; i < 10 i++)


cin >> tabelle1[i];

oder aus einem anderen, bereits bestehenden Array (hier „tabelle0“) kopiert werden
sollen:

for ( i = 0; i < 10 i++)


tabelle1[i] = tabelle0[i];

C++ KOMPENDIUM 32
Rev. 1.01
• Arrays und Zeiger: Der Name eines Arrays ist bereits ein konstanter Zeiger auf den
Arrayanfang – der Compiler übersetzt ihn als einen dereferenzierten Zeiger.
o Arrayname als Zeiger: „&arrayname[0]“ und „arrayname“ sind Zeiger auf die
Anfangsadresse des Arrays. Beispielsweise kann mittels „short *zeigername =
arrayname“ ein Zeiger mit der Anfangsadresse initialisiert werden.
o Indizierte Zeiger: Wird ein Zeigername beispielsweise mit der Anfangsadresse
initialisiert, kann man mittels des Zeigernamen anstatt des Arraynamen auf das n-
te Element des Arrays zugreifen ().

Hier eine Übersicht über die Äquivalenzen zwischen Zeiger- und Indexausdrücken („a“
bezeichnet das Array, „pa“ den Zeiger)

&a[0]  A
&a[n]  a+n
&a[n]  pa+n *
&a[n]  &pa[n] *
&pa[n]  pa+n *
a+n  pa+n *

a[n]  *(a+n)
a[n]  *(pa+n) *
a[n]  pa[n] *
*(a+n)  *(pa+n) *
*(a+n)  pa[n] *
pa[n]  *(pa+n)

* falls pa die Anfangsadresse von a enthält

• Strings: Arrays aus Strings (also Arrays vom Typ „char“) werden dadurch geformt, dass
das letzte Element des Arrays „\0“ ist. Wenn das letzte Element nicht „\0“ ist, dann ergibt
sich kein Array aus Strings, sondern lediglich ein Array aus Zeichen
o Compiler übersetzt allgemein einen jeden String dadurch, dass er z.B. bei „cout
<< „Text“;“ ein Array mit \0 als letztem Element anlegt.
o Wenn ein Operator oder eine Funktion auf ein char-Array trifft, wird im
Gegensatz zu anderen Datentypen stets der Inhalt und nicht die Adresse des
Arrays ausgegeben (außer natürlich dies geschieht explizit, z.B. mittels „cout <<
&array/zeiger“ oder „printf(„%p“, „String“);“.
o Die Initialisierung von char-Arrays (also die Erzeugung von Stringvariablen)
geschieht entweder mit „char a [] { einzelne Zeichen und \0 in Hochkommata }“
oder kürzer mit char a[anzahl_der_zeichen +1] = „hier_der_string“; (auch möglich
mittels Zeiger: „char *a = „String“; a ist dann allerdings eine Variable!)
o Zeichenweise Ein/-Ausgabe ist die wohl gebräuchlichste Methode, um ein char-
Array zu füllen. Sie geschieht beispielsweise mittels

while ((array[i] = cin.get( ) != ENTER)

wodurch so lange einen String eingelesen wird, bis „ENTER“ gedrückt wird.

C++ KOMPENDIUM 33
Rev. 1.01
o Alternativ kann man auch auch explizit „char a[64];“ und „cin >> a;“ angeben.
Dadurch liest man maximal 63 Zeichen in das Array a ein. Ein- und Ausgabe
können natürlich auch konventionell durchgeführt werden mittels „scanf(„%s“,
a);“ und „printf(„%s“, a);“.
o Vorsicht: Nach Angabe eines Zwischenraumzeichens (z.B. Leerzeichen) beendet
cin (bzw. scanf) die Eingabe!
o Kopieren von char-Arrays: Sind zwei char-Arrays a und b mit n Elementen
gegeben, und eine Kontrollvariable „int i“ definiert, kann mittels

for ( i = 0; i < n; i++)


a[i] = b[i];

der Inhalt von b nach a übertragen werden. (Strings lassen sich aber auch sehr
einfach mittels „strcpy“ duplizieren, das mit der Include-Datei „string.h“ am
Programmkopf eingeschlossen werden muss. Syntax:
„strcpy(adresse_des_zielarrays, adresse_des_quellarrays)“, z.B. strcpy(a,b); Soll
nur ein Teil kopiert werden: strcpy(a,b[4]); kopiert ab dem 5. Zeichen in b nach a.
o Strings können auch verkettet werden mittels der Funktion „strcat“, die folgende
Syntax aufweist: „strcat(adresse_des_zielarrays, adresse_des_quellarrays)“. Damit
wird das Quellarray wird an das Zielarray angehängt.
o Strings können verglichen werden mit „strcmp“: Derjenige String ist größer,
dessen erstes nicht mit dem anderen String identisches Zeichen einen größeren
Codewert in der Zeichentabelle (ASCII-Code) des Rechners besetzt. (z.B.
„abcdef“ größer als „abcDef“ und „abcDefg“). „strcmp“ hat die Syntax
„strcmp(adresse_array1, adresse_array2)”.
o Die Stringlänge kann ermittelt werden mit „strlen“, das folgende Syntax hat:
„strlen(array_adresse)“.

Mehrdimensionale Arrays

• Mehrdimensionale Arrays (die man sich graphisch als Tabellen oder Tabellen-Quader
vorstellen kann) bestehen aus zwei oder mehr ineinander geschachtelten Arrays, wobei
die unterste Ebene elementare Datentypen enthält.
• Die Syntax folgt dem eindimensionalen Fall:

typ_der_arrayelemente name_des_arrays [eintrag1] [eintrag2] … [eintragn];

Beispielsweise definiert „short b [4] [4]“ ein Array aus 4 x 4 = 16 Elementen.


• Indizierung des Arrays geschieht gemäß der Syntax: „name_des_arrays [ index1 ] … [
indexn ]. Das bedeutet, dass bei zweidimensionalen Arrays die Formel: „name_des_arrays
[Zeile] [Spalte]“ gilt. Grundsätzlich werden Arrays von hinten beginnend lesen, da bei der
höchsten Ebene mit der Inidizierung begonnen wird: wenn beispielsweise ein Array
mittels „short a[4][3];“ definiert wurde, bezeichnet a[0][3] das erste („[0]“) Element des
vierten („[3]“) eindimensionalen Teilarrays von a.
• Die Eingabe geht zeilenweise vor sich, z.B.

C++ KOMPENDIUM 34
Rev. 1.01
int zeile, spalte;
for (zeile=0, zeile < anzahl_zeilen, zeile++)
for(spalte=0, spalte < anzahl_spalten, spalte++)
cin >> a [zeile][spalte];

Oder man weise beispielsweise allen Elementen der 2.Zeile einen gemeinsamen Wert zu:

for(spalte=0, spalte < anzahl_spalten, spalte++)


cin >> a [1][spalte] = wert;

• Es ist möglich, den Indexausdruck als Zeigerausdruck zu formulieren: „a[i1][i2][i3]“


entspricht „*(*(*a+i1)+i2)+i3)“.
• Initialisierung: bei einzelner Angabe der Initialisierungswerte, z.B durch „long
array[2][2] = {1,2,3,4};“ wird wiederum zeilenweise eingelesen. Theoretisch würde hier
auch die Angabe „long array[ ][2] = { 1,2,3,4}“ genügen, da der Compiler daraus die
nötige Zeilenanzahl automatisch bestimmt.
• String-Arrays werden wie im eindimensionalen Fall behandelt.

Zeigerarrays

• Zeigerarrays sind Arrays, deren Elemente Zeiger auf Datenobjekte sind, z.B. ist „int
*zi[10]" ein Array aus zehn Zeigern auf Datenobjekte des Typs int. Wird nun „int i = 1;“
und „zi[0] = &i;“ definiert, so verweist zi[0] danach auf i.
• Ein „cout << *zi[0];“ gibt den Wert von i (1) aus, andererseits gibt „cout << zi[0];“ bzw.
„cout << &zi[0];“ die Adresse von i aus.
• Daraus folgt die Schreibweise für Array aus Zeigern: „char *c[3] = { „monday“,
„tuesday“, „wednesday“};“ . Diese Syntax ist effektiver als die gewöhnliche
zweidimensionale Array-Schreibweise. Der Zugriff, der z.B. über „cout << c[0]“ erfolgt,
ändert sich aber nicht.
• Zuweisungen können ebenfalls wie bei zweidimensionalen Arrays gemacht werden:
„c[0][0] = ’M’;“ macht aus „monday“ „Monday“.
• Vorsicht: Wenn Werte für Zeigerarray direkt von der Tastatur eingelesen werden sollen,
weiß der Compiler vorher nicht, wie viele Zeichen es pro Element werden! Dies ist eine
häufige Fehlerquelle!

C++ KOMPENDIUM 35
Rev. 1.01
7. Speicherverwaltung

• Man spricht von aktiver oder dynamischer Speicherverwaltung, wenn z.B. die Größe
eines Arrays nicht von vornherein klar ist, sondern erst zur Laufzeit eines Programms
bestimmt wird. Mit Hilfe der Funktionen „malloc“ und „new“ kann der Speicherplatz
während des Ablaufs des Programms verwaltet (allokiert) werden. Ebenfalls kann
während der Programmlaufzeit Speicherplatz wieder frei gegeben werden, wenn er nicht
mehr benötigt wird, mit Hilfe der Funktionen „delete“ oder „free“.

Speicherverwaltung mit „new“

• Um einzelnen Objekten Speicher zuzuweisen, kann man die Funktion „new“ benutzen,
deren Syntax sich folgendermaßen gestaltet:

zeiger = new datentyp [ (initialisierungs_ausdruck) ];

Beispiel:

int *x;
x = new int;

Hier wird ein Zeiger x definiert, im Speicher allokiert und zugewiesen. Der Zeigertyp und
der allokierte Datentyp müssen von gleicher Art sein
• Initialisierung: Weise der Allokationsanweisung einen Initialisierungswert zu: z.B. durch
„short *y = new short(3);“. Ein „cout << *y“ würde in diesem Beispiel „3“ ausgeben.
• Arrays zu allokieren erreicht man mittels der Syntax

zeiger = new datentyp [ elementezahl ];

z.B. durch „int *array = new int[5];“ Dadurch wird Speicherplatz für 5 int-Objekte
allokiert, und das Array wird mit der Adresse des ersten Elements initialisiert.
o Damit muss die Anzahl der Elemente des Arrays nicht notwendigerweise mehr
eine Konstante sein. Beispielsweise kann durch eine zusätzliche Anweisung
mittels einer Variablen die Anzahl determiniert werden.

• Bei der Allokation von dynamischen Arrays (deren Element-Anzahl nicht von Anfang
an feststeht) können keine Initialisierungswerte angebeben werden, z.B. ist „int *array =
new int [5] (1,2,3,4,5);“ nicht möglich.
• Bei mehrdimensionalen dynamischen Arrays, die durch die übliche Syntax:

zeiger = new datentyp [elementzahl1] [elementzahl2] …;

C++ KOMPENDIUM 36
Rev. 1.01
eingefügt werden, darf nur die erste Arraydimension variabel sein! Alle anderen
hingegegen müssen konstant bleiben. Der Datentyp der Zeigerrückgabe ist der der
obersten Ebene des Arrays.
• Der Zugriff auf mehrdimensionale dynamische Arrays ist identisch mit dem der
gewöhnlichen mehrdimensionalen Arrays, mit einer Ausnahme: der verwendete Zeiger ist
jetzt statt des Arraynamens eine reguläre Zeigervariable, z.B. array [0] [0] = 2;
• Sollte ein Fehler bei der Allokation auftreten (wenn z.B. nicht genügend Speicher für
das Array frei ist), wird der Nullzeiger vom Programm zurückgegeben. Es empfiehlt sich,
zur Fehlerkontrolle eine Zeile folgenden Musters einzuschließen:

if ( array == 0) cout << „Fehler bei der Allokation!“;

Speicherfreigabe mit “delete”

• Man kann Speicherplatz, der mit „new“ allokiert wurde, mit „delete“ wieder frei geben
(sog. Deallokation). Die Syntax ist denkbar einfach: „delete zeiger;“
• „delete“ kann nicht zur Löschung eines Zeigers auf ein konstantes Datenobjekt verwendet
werden.
• Um ganze Arrays löschen, bediene man sich der Syntax „delete [ ] zeiger;“

Zeiger auf Zeiger

• Beispielsweise für dynamisches Array aus Zeigern, die auf Zeichenketten verweisen, wird
wie für jedes andere dynamische Array auch, ein Zeiger auf einen Zeiger benötigt.
• Die Syntax für einen solchen Zeiger lautet:

datentyp_des_verweisobjekts **name_des_zeigers;“

Beispielsweise würde nach der Definition

int i = 1, *zi = &i;

ein Zeiger auf einen Zeiger folgendermaßen aussehen:

int **zzi = &zi;

Damit sind jetzt **zzi, *zi, und i äquivalent.


• Zeiger auf Zeiger bei Arrays: Seien beispielsweise „char 1[] = „abc““ und „char 2[] =
„def““ definiert, sowie „char *z1[2] = {1,2}“. Jetzt verweisen z1[0] bzw. z1[1] auf 1 bzw.
2.
• Die Allokation dynamischer Arrays mit doppeltem Zeiger folgt dem Muster:

char **namen = new char*[anzahl_der_einträge];

C++ KOMPENDIUM 37
Rev. 1.01
Dynamische Speicherverwaltung

• C++ hält einige Funktionen zur dynamischen Speicherverwaltung bereit, wie etwa
„malloc“ und „calloc“ (die zur Allokation dienen), „realloc“ (das eine Reallokation
bewirkt) und „free“ (das eine Freigabe des Speicherplatzes nach sich zieht). Diese
Funktionen müssen mit „stdlib.h“ in den Programmkopf eingeschlossen werden.
• Malloc/calloc: haben folgende Syntax:

zeiger = (typ_des_zeigers) malloc (anzahl_bytes);

Dabei ist „anzahl_bytes“ vom Typ „unsigned int“, und die Adresse des allokierten Blocks
wird in der Form eines generischen Zeigers „void*“ gespeichert, wodurch eine explizite
Konvertierung in den Typ des Zeigers auf der linken Seite notwendig wird.
o Beispiel:

int *i = (int*) malloc(10*sizeof(int));

Hier geschieht zunächst eine Zeigerdefinition, dann eine explizite Konvertierung


in die Form „int“, dann wird eine Speicherallokation für 10 int-Objekte
durchgeführt, und schließlich erfolgt eine Initialisierung mit der Adresse des
Blocks.

• calloc: bei dieser Funktion werden alle Elemente mit dem Wert 0 initialisiert. „calloc“
besitzt folgende Syntax:

zeiger = (typ_des_zeigers) calloc (elementezahl, elementgröße);

o Beispiel:

int *i;
i = (int*) calloc(10, sizeof(int));
.
• realloc wird zur Änderung des Speicherplatzes bzw. der Speichergröße verwendet.
„realloc“ folgt der Syntax:

zeiger = (typ_des_zeigers) realloc (zeiger, blockgröße);

Wenn als Zeiger der Nullzeiger („NULL“) angegeben wird, verhält sich realloc genau wie
malloc.
• free: Mit „free“ wird eine Freigabe des von anderen Funktionen allokierten
Speicherplatzes erreicht. Dies geschieht gemäß der Syntax: „free(zeiger);“

C++ KOMPENDIUM 38
Rev. 1.01
8. Funktionen

Definition und Aufruf

• Funktionen bestehen aus ihrem Namen, aus einer Anzahl von Parametern, aus
Anweisungen (was mit den Parametern geschehen soll), und aus einem Rückgabewert.
Sie haben allgemein folgende Syntax:

typ_des_rückgabewerts name_der_funktion ([typ1, name1, typ2, name2, …])


{ anweisung(en) }

Angaben in eckigen Klammern sind hier optional.


• Beispiel:

void funktion (int a, int b);


{a+b; }

Diese Funktion hat den Namen „funktion“, keinen Rückgabewert (void), zwei Parameter
(int a, b), und eine Anweisungen (a+b).

Deklaration von Funktionen (Prototypen)

• Die Deklaration einer Funktion hat eine identische Syntax wie ihre Definiton; nur sind
jetzt die Typangaben optional, und die Deklaration ist mit einem Semikolon
abzuschließen. Eine Deklaration wird erforderlich, wenn sich der Aufruf der Funktion vor
ihrer Definition befindet.
• Funktionen können lokal und global deklariert werden. Diese Einteilung bestimmt dann
auch ihren Gültigkeitsbereich.
• Deklaration mittels Include-Dateien: Schreibe Header-Datei (*.h) für
Funktionsdeklarationen, und schließe diese dann mittels #include „Pfad:\headerdatei.h“.
(Wenn die Header-Datei im Default-Ordner für include-Dateien gespeichert wird, ist eine
Pfadangabe nicht nötig).
• Wenn die Anweisungen/Definition der Funktion in einer anderen Datei stehen, binde man
den Code der Funktion in ein neues Programm durch eine explizite Angabe beim
Kompilieren ein, z.B. beim MS-Compiler durch: „cl programm.cpp
einzubindende_funktion.cpp“.

Gültigkeitsbereiche

• Es existieren verschiedene Gültigkeitsbereiche („scopes“) für Datenobjekte: Block, Datei,


und Klasse.
o Block: Die Gültigkeit endet nach Ende des Blocks – sie ist lokal.
C++ KOMPENDIUM 39
Rev. 1.01
o Datei: Gültigkeit auch außerhalb aller Funktionen und Blöcke, die Funktion ist
global gültig.
o Klasse: Die Funktion ist innerhalb der Klasse gültig.

• Zugriff auf überdeckte Variablen (gleichnamige Variablen mit verschiedenen


Gültigkeitsbereichen innerhalb eines Programms) ist beispielsweise mittels expliziter
Zeigerdefinition auf eine der Variablen möglich, oder mit Hilfe des
Bereichszugriffsoperators „::“. Z.B. gibt „::globaler_Name;“ den globalen Wert einer
Variable aus.
• Dateiübergreifende Gültigkeitsbereiche: werden mittels der Syntax

extern datentyp_der_variablen name_der_variablen;

definiert, z.B. „extern int a;“. Die Deklaration einer globalen Variable a muss vor einem
Zugriff auf a erfolgen – d.h. zuerst muss eine Definition im Programm erfolgen, dann eine
extern-Deklaration im anderen Programm angeführt werden, damit auch im zweiten
Programm der Zugriff auf die Variable möglich wird.
• Für dateiübergreifend gültige Konstanten muss „extern“ bereits bei der Definition
hinzugefügt werden.
• Eine Explizite Beschränkung des Gültigkeitsbereiches einer Variablen auf ein
Programm ist mittels „static“ bei der Definition der Variablen möglich (z.B. „static int a =
1;“)

Lebensdauer von Variablen

• Die Lebensdauer von Variablen kann mittels dreier Optionen bestimmt werden. Mit
„static“ ist die Variablendefinition gültig für die Dauer des Programms, mit „auto“ oder
„register“ nur für den ihr zugewiesenen Programmteil.
• auto ist die default-Einstellung. Die Variablendefinition ist gültig für die Ausführung des
Programms, danach wird die Variable gelöscht.
• register: ist identisch mit auto, außer dass die Variable nicht im RAM, sondern im
Prozessorregister gespeichert wird, was einen schnelleren Zugriff ermöglicht.
• static: Gültigkeitsbereich ist der Block, die Lebensdauer der Variable ist global.
• Globale Variablen sind implizit „static“.

Rückgabewerte: die “return”-Anweisung

• Mit „return“ erfolgt eine explizite Beendigung der gegenwärtigen Anweisung und eine
Rückkehr zu aufrufender Funktion.
• Funktionen mit Rückgabewert haben die Syntax „return ausdruck;“ oder „return
(ausdruck);“ Wenn z.B. ein Programm „int rueckgabe( )“ definiert wurde, gibt „return
(int1);“ einen Rückgabewert zurück. Beachte: die Typen müssen übereinstimmen, hier
muss beispielsweise „int1“ vom Typ „int“ sein.

C++ KOMPENDIUM 40
Rev. 1.01
• Referenzen, also Adressangaben, können ebenfalls als Rückgabewerte verwendet
werden. Der Rückgabewert ist dann eine Referenz auf die Variable und keine Kopie ihres
Wertes, sondern mittels der Referenz ein direktes Äquivalent für sie. Deshalb kann ihr
Wert, und damit auch der Rückgabewert im späteren Verlauf vom Programm modifiziert
werden.

Parameter

• Parameter sind die Bestandteile, die innerhalb einer Funktion für eine Rechenanweisung
verwendet werden. Wenn eine Funktion keine Parameter hat, bleiben die runden
Klammern bei Definition bzw. der Deklaration leer, oder enthalten das Schlüsselwort
„void“.
• Wenn Parameter eingeschlossen werden, werden sie dadurch definiert und haben
Gültigkeit bis zum Funktionsende. Dabei gibt es zwei Möglichkeiten, wie die Parameter
dem Programm übergeben werden, die Parameterübergabe per Wert, und die
Parameterübergabe per Adresse.
• Die Parameterübergabe per Wert („pass by value”) ist die default-Einstellung in C++.
• Paramterübergabe per Adresse („pass by reference”) unterteilt sich wiederum in zwei
Optionen, die Übergabe mit einem Zeiger, und die Übergabe mit einer Referenz.
o Zeiger-Übergabe: z.B. bei „funktion1(wert1, wert2, &z);“ werden die Werte von
wert1, wert2 und die Adresse von z an den Aufruf der Funktion „funktion1“
übergeben. Allerdings muss bei der Definition der Funktion der ein Zeiger
definiert worden sein, damit die Adresse gespeichert werden konnte und der
Zugriff mittels des Adressoperators „&“ möglich wird. Z.B. kann mit „*pz = &z;“
ein Zeiger definiert und mit der Adresse von s initialisiert werden. Dann ist anstatt
„funktion1(wert1, wert2, &z);“ auch funktion1(wert1, wert2, pz); möglich,
obwohl diese Methode im Grunde eine Übergabe per Wert entspräche.
o Referenzen-Übergabe: Beispielsweise übergibt „void funktion1(long wert3, long
wert4, long &z)“ die Werte „wert3“ und „wert4“ per Wert und s per Referenz an
funktion1(wert1, wert2, z). Im Allgemeinen ist diese Methode bequemer als die
Übergabe per Zeiger, weil eventuelle Modifikationen an z am Originalwert von z
durchgeführt werden, und nicht nur an einer Kopie.
• Arrays als Paramter: Arrays werden, wenn sie Parameter einer Funktion sind, immer
per Referenz übergeben. Damit betreffen eventuelle Modifikationen nicht eine Kopie des
Arrays, sondern das Original. Nur der Arraynamen ist als Paramter anzugeben.
Äquivalent sind z.B. „void funktion1(short *arr)“ (Formalparamter in Zeigersyntax) oder
„void funktion1(short arr[ ])“ oder „void funktion1(short arr[10])“.
o Arraygröße: Anzahl der Ausgabelemente kann mit zusätzlichen Parameter
angegeben werden. Beispiel: Sei mit „void show(short *arr, int n)“ eine Funktion
zur Anzeige der Arrayelemente definiert, mit n als Anzahl der Ausgabelemente.
Hat arr beispielsweise 4 Elemente, und sollen das 3. und 4. Element ausgegeben
werden: „show (&arr[2], 2);“.
o Mehrdimensionale Arrays: Adressangabe bezieht sich – nicht wie sonst! – auf die
oberste Arrayebene. Allgemein wird also mittels einer Defintion

typ array_name [ebene_1] … [ebene_n]


C++ KOMPENDIUM 41
Rev. 1.01
und einem Aufruf

funktion1(array_name)

ein Zeiger der Form „typ *[ebene_2] … [ebene_n]“ notwendig. Beispiel: bei „int
arr[3][4]“ und „void zero(int (*arr)[4])“ ist „arr“ ein Zeiger auf 4-elementige int-
Arrays. Die Klammerung ist hier wegen der hohen Priorität von [] notwendig.

• Konstante Parameter: Bei konstanten Parametern ist die Übergabe per Wert
unproblematisch – allerdings können bei Übergabe per Referenz Konvertierungsprobleme
auftreten. Man bestimme deshalb einen zugehörigen Formalparameter mit „const“, z.B.
void funktion1(const char x[]).
• Default-Paramter: Der Wert eines Aktualparamters kann bereits bei der
Funktionsdefintion angegeben werden – z.B. „double funktion(double x = 1.000)“ .Wenn
jedoch die Funktion mit anderem Wert, z.B. „funktion(5.000)“ aufgerufen wird, wird der
default-Wert ignoriert. Anzahl und Position von default-Angaben sind von rechts
beginnend und nach links aufstockend anzugeben.

Funktionsnamen überladen

• Wenn eine Operation in gleicher Art und Weise mit einer unterschiedlichen Anzahl von
Objekten oder verschiedenen Typen von Objekten ausgeführt werden soll, kann ein
Funktionsname mehrere Funktionen bezeichnen, z.B. „int funktion(int *a, int b)“ und
„double funktion(double *a, int b)“.
• Beachte: Die Rückgabewerte und die Formalparameter, die in einer Funktion verwendet
werden, gehören nicht zur Funktion selbst; wenn sich also zwei Funktionen nur in Bezug
auf diese unterscheiden, können sie nicht mit demselben Namen bezeichnet werden, weil
sie für den Compiler identisch sind.
• Funktion kann sich auch selbst aufrufen (z.B. mit einer if-Schleife. Wenn die Schleife
abbricht, endet die Rekursion und das Programm kehrt zum ersten nicht-rekursiven
Aufruf zurück).

Die Funktion “main”

• Die Hauptroutine „main“ darf nicht überladen oder aus dem Programm heraus aufgerufen
werden.
• Kommandozeilen-Parameter sind Werte, die das Programm verwenden soll, und die
schon beim Aufruf von „main“ eingegeben werden können, nicht erst bei der Laufzeit des
Programms. Kommandozeilen-Parameter sind aktuell, die zugehörigen Formalparameter
für main sind mit „int argc“ und „char *argv[]“ konventionell vereinbart. Der Compiler
legt implizit Zeichenarrays für Kommandozeilen-Parameter an. Dabei bezeichnet „argc“
die Anzahl der eingegebenen Parameter und „char *argv[]“ das Zeiger-Array für diese.
Der erste Zeiger dieses Zeigerarrays, „argv[0]“, verweist dabei auf den Programmnamen,

C++ KOMPENDIUM 42
Rev. 1.01
der zweite argv[1] auf den ersten „echten“ Kommandozeilenparamter, der letzte
argv[argc] auf den Nullzeiger.

Inline-Funktionen

• Inline-Funktionen kombinieren die Schnelligkeitsvorteile von Makros mit einer


geringeren Fehlergefahr im Quelltext. Sie werden mittels folgender Syntax angegeben:

inline typ_rückgabewert name_funktion(paramterliste) { [anweisungen] };

• Inline-Funktionen können wie Makros expandiert werden, d.h. der Compiler ersetzt einen
Funktionsaufruf durch den entsprechenden Quelltext. Deshalb müssen Inline-Funktionen
vor ihrem Aufruf definiert werden, eine Deklaration an gewünschter Stelle reicht nicht
aus! Zur Vermeidung von Fehlern kann man eine Inline-Funktion auch in eine eigene
Header-Datei schreiben und mittels einer #include-Anweisung einschließen.

8.10 Zeiger auf Funktionen

• Die Funktionsadresse einer Funktion beinhaltet die Adresse auf den ausführenden Code-
Teil, d.h. die Anweisungen der Funktion. Bei einer Funktion f stellen „f“ und „&f“ beide
einen konstanten Zeiger auf den Anfang des Funktionscodes dar, z.B. stellt „int
(*f)(double d)“ einen Zeiger f auf Funktionen mit einem double-Parameter und
Rückgabewert int dar (hier sind die Klammern wegen der Setzung der Priorität
notwendig).
• Wenn die Funktion dann noch intialisiert wird (z.B. durch „int (*f)(double d) = g;“), kann
mittels g(wert) oder (*f)(wert) oder f(wert) die Funktion aufgerufen werden.

C++ KOMPENDIUM 43
Rev. 1.01
9. Klassen

Eine Klasse ist eine informatische Modellbildung eines gesamten Objektes mit allen seinen
Parametern. Eine Klasse ist ein „Bauplan“ für Objekte.

Definition von Klassen

• Eine Klasse wird gemäß der folgenden Syntax definiert: Zuerst wird ein Schlüsselwort
angeführt (entweder „class“, „struct“, oder „union“), dann folgt der Name der Klasse, und
daraufhin (optional) die Spezifiktion der Basisklasse.
• Beispiel: „class ersteKlasse {} x;“ definiert eine Klasse „ersteKlasse“ und ein Objekt x
dieser Klasse (alternativ: „class ersteKlasse {}; ersteKlasse x;“)
• Anonyme Klassen: „class { … } a;” definiert eine Klasse ohne Namen, aber mit Objekt
„a“.
• Basisklassen: Es ist möglich, dass eine Klasse das Modell einer anderen ist. Geht Klasse
2 aus Klasse 1 hervor, wird 1 als Basisklasse von 2 bezeichnet (siehe Punkt „Vererbung“)
• Spezifikation von Datenelementen, die die Klasse verwalten soll: Wenn z.B. eine Klasse
mittels „class A { double 1; double 2;};“ definiert wurde, wird ihren Elementen noch kein
Speicherplatz zugewiesen – sie können deshalb noch nicht initialisiert werden.
• Eine Klasse darf sich nicht selbst als Element beinhalten (aber sie darf einen Zeiger auf
sich selbst beinhalten)
• Elementfunktionen: Für Funktionen, die innerhalb einer Klasse verwendet werden
sollen, ist entweder eine Definition der Funktion innerhalb der Klasse oder eine
Deklaration innerhalb und die entsprechende Definition außerhalb notwendig.
o Innerhalb einer Klasse definierte Funktionen sind automatisch vom Typ
„inline“.
o Außerhalb einer Klasse definierte (und in der Klasse deklarierte) Funktionen
müssen eine Referenz auf ihre Verwendung in der Klasse beinhalten, mittels der
Syntax

klassen_name::funktions_name

, also beispielsweise

void KlasseA::funktion(double x) { anweisungen; }

Man spricht hier von einem „qualifiziertem Namen“ der Funktion.


o Außerhalb einer Klasse definierte Funktionen sind nicht automatisch vom Typ
„inline“.
o Elementfunktionen haben direkten Zugriff auf alle Klassenelemente, es ist also
keine explizite Parameterübergabe notwendig.
• Typendefinitionen sind innerhalb von Klassen möglich: z.B. durch „typedef char
string_array [10];“ Nun ist „string_array“ ein Synonym für „char [10]“.
C++ KOMPENDIUM 44
Rev. 1.01
• Klassendefinitionen innerhalb von Klassen sind problemlos möglich.
• Gültigkeit von Definitionen von Objekten sind generell auf die jeweilige Klasse
beschränkt. Ein Zugriff auf ein Objekt von außerhalb der Klasse ist dennoch möglich, und
zwar mittels des Zugriffsspezifizierer „public“, der vor die Definition des Objekts gesetzt
wird, und dem Bereichszugriffoperator „::“, der für den Zugriff verwendet wird.
Allgemein kann man mit einem Zugriffsspezifizierer die Regeln des Zugriffs auf
Objekte der Klasse festlegen, mittels der Schlüsselworte „private“, „public“ oder
„protected“. Wenn keine Spezifizierungs-Angabe erfolgt, wird automatisch „private“
gesetzt.
o private: Der Zugriff auf private-Elemente ist nur den Elementfunktionen der
jeweiligen Klasse erlaubt oder den Funktionen, die als „friends“ der Klasse
deklariert werden.
o protected: Der Zugriff auf protected-Elemente ist für Elementfunktionen (und
„friend“-Funktionen) sowie Elementfunktionen abgeleiteter Klassen (und deren
„friend“-Funktionen) möglich.
o public: Mittels „public“ wird ein globaler Zugriff möglich.

• Um Zugriff auf private-Klassen zu erhalten, ist es möglich, ein „public-interface“ zu


setzten, d.h. einen ganzen public-Abschnitt zu definieren, z.B. eine oder mehrere
Elementfunktion(en).
• Lokale Klassen sind innerhalb einer Klasse oder einer Elementfunktion definierte
Klassen. Der Gültigkeitsbereich ihrer Definitionen ist wie bei Variablen auf diesen Block
beschränkt. Lokale Klassen haben keinen Zugriff auf Variablen der Elementfunktion oder
der übergeordneten Klasse, jedoch auf deren statische Variablen. Elementfunktionen von
lokalen Klassen müssen innerhalb ihrer Klasse definiert werden (wodurch sie implizit als
„inline“-Funktionen festgelegt werden).
• Die Deklaration von Klassen folgt der Syntax: „class name_der_klasse;“ Damit können
bereits vor der Definition Zeiger und Referenzen auf die Klasse festgelegt werden, jedoch
können keine Objekte der Klasse erzeugt werden (was eine vorhergehende Definition
erfordern würde).

Objekte einer Klasse

• Die Definition neuer Objekte einer Klasse geschieht analog zur Definition von Variablen;
z.B. definiert „name_der_klasse a, b, c[5];“ zwei Objekte a und b des Typs
„name_der_klasse“ und ein Array aus fünf name_der_klasse-Objekten.
• Bei jeder Neudefinition eines Objektes einer Klasse wird – im Gegensatz zur Definition
innerhalb der Klasse – Speicherplatz für das Objekt allokiert.
• Zuweisungen sind mittels einer einfachen Gleichsetzung „objekt1=objekt2“ möglich. Die
Übergabe erfolgt per Wert. Objekte müssen von gleichem Klassentyp sein (außer: bei
elementaren Datentypen erfolgt eine implizite Konvertierung).
• Zugriff auf Klassenelemente: Der Zugriff ist nur innerhalb ihres Gültigkeitsbereiches
ohne weiteres möglich.
• Zugriff von außerhalb ist mittels des Punktoperators möglich, nach folgendem Muster:
„objektname.datenelement_name“ bzw. „objektname.aufruf_elementfunktion“.
Allerdings bleibt die Möglichkeit des Zugriffs von außerhalb unberührt vom
C++ KOMPENDIUM 45
Rev. 1.01
Zugriffstatus, der für das betreffende Element in der Klassendefinition festgelegt wurde.
Außerdem ist der Zugriff auf „private“-Elemente der Klasse nicht möglich.
• Zugriff auf Objektarrays ist mittels des Punktoperators innerhalb folgender Syntax
möglich:

objektarray_name[index].datenelement_bzw_Elementfunktion_name;

• Wenn ein Element einer Klasse 1 wiederum Objekt einer anderen Klasse 2 ist, sind die
Punktoperatoren hintereinander zu schalten. Beispiel: 1_objekt.2_objekt.2_element
• Grundsätzlich: Wenn viele Klassen innerhalb eines Programms verwendet werden,
empfiehlt sich die Definition der Klassen mittels einer eigenen Include-Datei. Die
Funktionen einer Klasse, die nicht vom Typ „inline“ sind, werden gewöhnlich in Klasse
nur deklariert und in einer eigenen cpp-Datei definiert (in dieser ist dann die
Klassendefinitions-Datei einzuschließen).
• Pfeiloperator: beispielsweise allokiert „Klasse1 *z = new Klasse1“ ein Klasse1-Objekt,
definiert einen Zeiger Klasse1, und initialisiert z mit der Adresse des Objekts. Nun kann
z.B. dem Element „element_1“ dieses Objekts mittels (*z).element_1 = 1.234; ein Wert
zugewiesen werden. Eine Klammerung von *z ist hier wegen den Prioritäten notwendig.
Eine Alternative Schreibweise ist mit dem Pfeil-Operator „->“ möglich, mittels der
Syntax: „zeiger->element“, z.B. „z->element_1 = 1.234;“
• Zeiger auf Datenelemente haben die Syntax:

typ_des_elements name_der_klasse:: *name_des_zeigers;

Beispielsweise definiert „double Klasse1:: *z;“ einen Zeiger, der nur auf Elemente des
Typs double der Klasse „Klasse1“ zeigen kann.
• Bei Initialisierungs- bzw. Zuweisungsoperationen ist wiederum der
Bereichszugriffoperator „::" notwendig, z.B.:

z = &Klasse1::element_1;

lässt z auf element_1 der Klasse „Klasse1“ zeigen. Alternativ kann auch die Definition
und die Initialisierung zusammengefasst werden, z.B.:

double Klasse1 :: *z = &Klasse1::element_1;

• Mittels „objekt.*elementzeiger“ lässt sich dann auf das gewählte Objekt zugreifen, z.B.
kann man nach einer Definition eines Objektes der Klasse „Klasse1“, z.B. „Klasse1
class1;“ mittels „class1.*z = 1.23;“ auf z zugreifen. Alternativ ist auch hier wieder der
Pfeiloperator möglich, in der üblichen Syntax: „zeiger_auf_objekt ->* elementezeiger“.
• Zeiger auf Elementfunktionen haben die Syntax:

typ_rückgabewert (name_klasse::*name_zeiger) (paramterliste).


Zum Beispiel ist „void(Klasse1::*z)(double) z“ ein Zeiger auf Klasse1-Funktion ohne
Rückgabewert und einem Parameter (Klammerung ist auch hier wieder wegen den
Prioritäten notwendig). Eine Initialisierung erfolgt beispielsweise mittels

C++ KOMPENDIUM 46
Rev. 1.01
void(Klasse1::*z)(double) = Klasse1::funktion1;

Dies initialisiert z mit der Adresse der Funktion „funktion1“.


o Ein Aufruf der Elementfunktion erfolgt mittels

(objekt.*elementzeiger) (parameterliste)

oder

(zeiger_auf_objekt ->* elementzeiger) (parameterliste)

Beispiel: „(Klasse1.*z)(1.23);“ ruft die Funktion „funktion1“ über Zeiger den


Zeiger z auf, was äquivalent zu „Klasse1.funktion1(1.23);“ ist.

• this-Zeiger: der „this-Zeiger“ ist ein Zeiger für die Objekte, mit denen eine
Elementfunktion operiert. Jede Elementfunktion einer Klasse enthält diesen Zeiger
implizit als verborgenen Formalparameter.
o Eine explizite Verwendung des this-Zeigers wird nötig, wenn z.B.
Parameternamen Elementnamen überdecken sollten, oder Funktionsaufrufe mittels
Punktoperator verkettet werden sollen.

Initialisierung und Konstrunktion von Objekten

• Initialisierungslisten: Wenn eine Klasse nur public-Elemente, aber weder Konstruktoren,


noch Basisklassen, noch virtuelle Funktionen besitzt, dann können mit in „{ }“
eingeschlossenen Werten die Objekte dieser Klasse initialisiert werden.
o Bei Arrays: Enthält die Initialisierungliste für ein Array weniger Elemente, als bei
der Definition verlangt wurde, werden Nullen ergänzt.
o Mit bereits initialisierten Objekten können andere Objekte derselben Klasse per
Zuweisung initialisiert werden. Achtung: dies sind sog. „flache“ Kopien, die nur
die Zeiger, aber nicht die Werte kopieren.
o Das Gegenteil ist eine „tiefe“ Kopie, bei der Datenbereich dupliziert wird, und die
Adresse des Datenduplikats in den Zielzeiger übertragen wird. Dies wird mit der
Hilfe von Konstruktoren erreicht.

• Konstruktoren sind Elementfunktionen, die Klassenobjekte konstruieren, sie


initialisieren, und den nötigen Speicherplatz allokieren.
o Wenn Konstruktor nicht explizit vom Programmierer definiert wird, legt Compiler
implizit einen Standardkonstruktor an.
o Der Name des Konstruktors ist stets mit dem der Klasse identisch. Konstruktoren
besitzen keine Rückgabewerte (deshalb auch keine Typspezifizierung). Bestimmte
Elemente können vom Rumpf in den Kopf eines Konstruktors verlagert werden (s.
unten).
o Ein Konstruktor kann wie bei anderen Elementfunktionen außerhalb der Klasse
definiert, und innerhalb deklariert werden. Zum Beispiel ist

C++ KOMPENDIUM 47
Rev. 1.01
class abc ... int a; int b; public: abc(int c, int d);

eine Konstruktordeklaration, hingegen

abc::abc(int c, int d) { a = c; b = d; };

eine Konstruktordefinition.
o Wie üblich werden aktuelle Paramter von links nach rechts in die formalen
Parameter übertragen. Ebenfalls besitzt der Konstruktor wie eine normale
Elementfunktion einen this-Zeiger.
o Beispielsweise definiert „Klasse1 x(2,4);“ ein Klasse1-Objekt x und übergibt die
Parameter 2 und 4 an den Konstruktor, der sie in die Datenelemente von x
überträgt.
o Alternativ kann der Konstruktor auch explizit aufgerufen werden, gemäß der
Syntax: „name_konstruktor (liste_aktuelle_paramter)“.
o Konstruktoren sind wie alle anderen Klassenelemente den Zugriffsspezifizierern
(private, public, protected) unterworfen, und haben dementsprechend ihren
Bereich, in dem sie aufgerufen und verwendet werden können.
o Der Konstruktor benötigt beim Aufruf die exakte Anzahl der Parameter, außer
wenn die Parameter bei der Definition mit (beliebigen) default-Werten initialisiert
wurden. Dann kann z.B. nur ein Parameter beim Konstruktoraufruf angegeben
werden.
o Konstruktornamen sind überladbar.
o Der Standardkonstruktor initialisiert nur statische und globale Objekte implizit mit
Null. Lokale (nicht-statische) Objekte erhalten keinen definierten Anfangswert
durch den Standardkonstruktor. Dies geschieht mittels Aufruf des
Standardkonstruktors ohne Parameterangabe.
o Der Standardkonstruktor wird nicht erzeugt, wenn die Klasse einen
benutzerdefinierten Konstruktor besitzt.
o Kopierkonstruktoren: beispielsweise kann „Klasse1 a;“ eine Zuweisung
„Klasse1 b = a;“ erfolgen. Hier wird z.B. eine Kopie über den Standardkonstruktor
gemacht.
o Konvertierungskonstruktoren wandeln ein Objekt in eine Klasse um.
Kopierkonstruktoren sind mit einem einzelnen Parameter aufrufbar (wobei alle
eventuellen anderen Parameter default-Werte besitzen müssen). Der erste
Formalparameter ist von einem anderen Typ als die dem
Konvertierungskonstruktor eigene Klasse (sonst wäre er ein Kopierkonstruktor).
o Initialisierungslisten für Konstruktoren können als Alternative zu
Initialisierung im Rumpf verwendet werden. Sie sind notwendig, wenn
Datenelemente Konstanten, Referenzen, oder Klassenobjekte sind. Sie werden mit
Hilfe folgender Syntax angegeben:

element_1 (init_ausdruck) [ , element_2 (init_ausdr), ... ]

(Eckige Klammern signalisieren hier optionale Elemente).


o Elementobjekte: Zur Initialisierung eines Elementobjekts wird ein Konstruktor
der Klasse verwendet, die den Typ des Elementobjekts bildet. Dieser Konstruktor
wird ausgeführt, bevor der Konstruktor der Klasse ausgeführt wird, die das
C++ KOMPENDIUM 48
Rev. 1.01
Elementobjekt enthält. Falls der Konstruktor Parameter benötigt, werden diese in
der Initialisierungsliste des Konstruktors der Klasse angegeben, die das
Elementobjekt enthält.
o Objektarrays: Für die Konstruktion und Initialisierung eines Arrays aus Objekten
einer Klasse ist folgendes zu beachten: Ist ein Standardkonstruktor definiert, kann
die Initialisierungsliste entfallen. Ist ein Konstuktor mit genau einem Parameter
definiert, kann für jedes zu initialisierende Objekt des Arrays der Parameter in der
Liste angegeben werden. Ist der Konstruktor mit mehreren Parametern definiert,
ist expliziter Aufruf des betr. Konstruktors in der Initialisierungsliste für jedes zu
initialisierende Objekt notwendig.
o Beispiel: „Klasse1 b[4] = { „00:30“ }“ (impliziter Aufruf eines char-
Konstruktors), „Klasse1 (2,30)“ (expliziter Aufruf eines int-int-Konstruktors) }.
Die fehlenden Elemente b[2] und b[3] werden hier mittels des
Standardkonstruktors (der definiert sein muss) mit 0 initialisiert.
o Für dynamische Arrays sind keine Initialisierungslisten erlaubt.

• Destruktoren werden zum Löschen von Konstruktoren verwendet, um Speicherplatz


wieder frei zu geben.
o Destruktoren haben die Syntax:

name_klasse::~name_klasse ( ) { [Anweisungen] }

Eckige Klammern signalisieren hier optionale Elemente.


Falls die Konstruktordefinition außerhalb erfolgt ist. Wenn sie innerhalb steht, ist
die Qualifizierung mit „name_klasse::“ nicht notwendig.
o Destruktor hat keinen Parameter (außer dem this-Zeiger) – der Name des
Dekonstruktors kann deshalb nicht überladen werden.
o Eine Klasse kann über nur einen Destruktor verfügen.
o Destruktoraufrufe sind fast immer implizit. Dies geschieht, wenn Lebensdauer
eines Objekts der Klasse abgelaufen ist (lokale auto-Objekte, static-Objekte, auf
new-Objekte mittels delete).

Konstante/statische Objekte

• Klassen können auch als konstante Objekte definiert werden, z.B. „const Klasse1 a(1,0);“.
o Zu beachten ist, dass konstante Objekte initialisiert werden müssen, und nicht neu
zugewiesen werden können; außerdem können keine Elementfunktionen
aufgerufen werden.

• Konstante Elementfunktionen greifen nicht schreibend auf ihre Datenelemente zu;


entsprechende Fehlermeldungen treten bei Zuweisungs- oder Veränderungsversuchen auf.
• Statische Klassenelemente sind solche, die von mehreren Klassenobjekten benutzt
werden können (aber keinem in eigener Ausführung bereit stehen, sowie alle üblichen
Klassenelemente). Das Schlüsselwort für solche Objekte ist „static“.
o Die Lebensdauer von static-Elementen beginnt bei der Definition und hält bis zum
Programmende vor. Ein static-Element gehört nicht zu einem Objekt, sondern zur
C++ KOMPENDIUM 49
Rev. 1.01
Klasse. Im Unterschied zu gewöhnlichen globalen Variablen ist sein
Gültigkeitsbereich nur über die Klasse erstreckt.
o Statische Elemente sind nur in globalen Klassen erlaubt. Sie werden in der
betreffenden Klasse mit dem Schlüsselwort „static“ deklariert, und werden
außerhalb ohne „static“ mittels des Bereichszugriffoperators „::“ und
qualifiziertem Namen angegeben.
o Beispiel: Innerhalb der Klassendefinition: „static int element1“; (Deklaration),
außerhalb „int klasse1::element1;“ (Definition und implizite Initialisierung mit
Null).
o Der Zugriff gestaltet sich genauso wie bei gewöhnlichen Datenelementen. Eine
Spezifizierung des Gültigkeitsbereiches ist mittels „private“, „public“, oder
„protected“ möglich.
o Alternativ kann man natürlich auch über einen vorher definierten Zeiger auf das
Klassenelement zugreifen.

• Statische Elementfunktionen: analog zu Datenelementen wird innerhalb der


Klassendefinition bei der Deklaration bzw. Definition der Elementfunktion das
Schlüsselwort „static“ als Präfix gewählt, z.B. „static void f( )“ . Außerhalb wird „static“
nicht benötigt.
o Statische Elementfunktionen besitzen keinen this-Zeiger – und damit keinen
direkten Zugriff auf nichtstatische Elemente der Klasse. Der Zugriff ist nur
indirekt mittels Zeiger möglich.

• Typmodifikation: mutable: Sei Klasse K konstant, dann sind auch ihre Elemente
konstant. Wenn aber trotzdem ein Element veränderbar sein soll, muss das Schlüsselwort
„mutable“ vor der Definition hinzugefügt werden: Beispiel: „mutable int xb;“ als Objekt
der Klasse K.

Friend-Funktionen und friend-Klassen

• Wenn beispielsweise der Zugriff einer Funktion auf private-Elemente einer Klasse von
außerhalb gewährleistet sein soll, ohne die Elemente der Klasse als „public“ zu
spezifizieren, kann das Schlüsselwort „friend“ innerhalb der Klasse für die
Funktionsdeklaration verwendet werden.
• Beispiel: innerhalb der Klassendefinition erfolgt die Deklaration der Funktion mit dem
Präfix „friend“: „friend int name( typ klassen_name & objekt_name );“
• Beispiel2: Es seien zwei Klassen definiert, und der Konstruktor der zweiten soll auf
Elemente der ersten zugreifen. Bei der Konstruktordeklaration in der zweiten Klasse ist
dann das Präfix „friend“ erforderlich. Grundsätzlich gilt die Regel, dass die Klasse, deren
Elementfunktion die friend-Funktion ist, vor der Definition der Klasse zu stehen hat, in
der die Elementfunktion als friend deklariert wird. Daraus ergibt sich ein mögliches
Problem: Wenn nämlich mehrere Klassen wechselseitig auf Elemente der anderen
zugreifen sollen.
o Sollen alle Elementfunktionen einer Klasse zu friend-Funktionen einer anderen
werden, ist folgende Syntax zu verwenden: „friend class name_klasse;“ Hier ist

C++ KOMPENDIUM 50
Rev. 1.01
„name_klasse“ der Name derjenigen Klasse, deren Elementfunktionen friend-
Funktionen werden sollen.
o Beispiel: Es seien “class A” und “class B” definiert. Bei der Definition von class B
schreibt man: „class B { friend class A; .... }” Hiermit sind alle Funktionen von A
friend-Funktionen von B.

Spezielle Klassen

• Mittels der Schlüsselwörter „struct“ (Strukturen) und „union“ (quasi


übereinanderliegende Objekte im Speicher) können auch Klassenarten generiert werden.
• Bei Strukturen ist die default-Zugriffsspezifikation „public“.
• Unions: Datenelemente von Unions teilen sich den Speicherbereich (der so groß ist wie
das größte Element). Auch hier ist die default-Zugriffsspezifikation “public”.
o Eine Union kann keine abgeleitete Klasse sein, noch kann aus einer Union eine
Klasse abgeleitet werden. Eine Union darf über keine statischen Elemente, noch
über Objekte einer Klasse mit eigenen Konstrukturen verfügen.
o Initialisierung ohne Konstruktor kann durch ein anderes Unionobjekt gleichen
Typs mittels Zuweisung erreicht werden. Beispiel:

union a {float x};


a u1 = { 3.14 };
a u2 = u1;

o Union ohne Namenangabe: Nur zum Zeitpunkt der Definition können Objekte
einer namenslosen Union oder Zeiger zum Zugriff auf Objekte der Union
festgelegt werden.
o Eine anonyme Union hat keinen Namen, Objekt- oder Zeigerdefinitionen, keine
Elementfunktionen, und keine private- oder protected-Elemente. Sie muss als
static spezifiziert sein. Dadurch ist Zugriff auf Elemente der Unions (deren
Gültigkeitsbereich der ihrer Union ist), nur direkt möglich.
o Elemente einer anonymen Union sind zwar vom Typ „public“, die anonyme
Union selbst aber nicht unbedingt.

C++ KOMPENDIUM 51
Rev. 1.01
10. Abgeleitete Klassen

• Abgeleitete Klassen sind solche, die die Elemente einer bereits bestehenden Klasse
übernehmen und neue hinzufügen. Eine abgeleitete Klasse ist also eine Spezialisierung
einer Klasse. Besitzt die abgeleitete Klasse nur eine Basisklasse, spricht man von
einfacher Vererbung, andernfalls von mehrfacher.
• Abgeleitete Klassen folgen der Syntax:

class (oder struct) name_abgeleitete_klasse : [ zugriffsspezifizierer ]


name_basisklasse [ weitere_zugriffsspezifizierer_und_basisklassen ]
{ // Spezifikation der neu hinzugekommenen Klassenelemente
};

Die Angaben in eckigen Klammern sind hier optional.


• Der Zugriffsspezifizierer beeinflusst nicht die Vererbbarkeit, sondern den Zugriff auf die
Elemente. Der default-Wert bei fehlender Angabe ist „private“.
• Allgemein gibt es drei Möglichkeiten, wie die Ableitungen vonstatten gehen kann:
„public“, „protected“ oder „private“.

10.1 Mit “public” abgeleitete Klassen

• „public“-Ableitungen haben die Form: „class A : public B { ... };“ In der Basisklasse als
„public“ spezifizierte Elemente, die über „public“ abgeleitet worden sind, sind in der
abgeleiteten Klasse auch ohne explizite Spezifikation wieder public.
o Ebenfalls verhalten sich public-vererbte protected-Elemente so, als wären sie in
der abgeleiteten Klasse explizit als protected spezifiziert worden (d.h. nur
Elementfunktionen ihres „Blocks“ oder friend-Funktionen der Klasse oder ihre
Ableitungen können darauf zugreifen.
o Private-Elemente sind auch in der abgeleiteten Klasse private, egal auf welche
Weise die Ableitung geschieht.

10.2 Mit “protected“ abgeleite Klassen

• „protected“-Ableitungen haben die Form: „class A : protected B { ... };“ public- oder
protected-Elemente der Basisklasse erhalten in der abgeleiteten den Status „protected“.
Allerdings ist deshalb der Zugriff von außen mittels einer Elementfunktionen auf diese
Elemente nicht möglich.
• Von der Basisklasse vererbte private-Elemente sind auch in der abgeleiteten „private“,
können aber von dieser nicht direkt erreicht werden (außer mithilfe einer friend-
Deklaration in der Basisklasse).

C++ KOMPENDIUM 52
Rev. 1.01
10.3 Mit “private” abgeleitete Klassen

• „private“-Ableitungen haben die Form: „class A : private B { ... };“ public- oder
protected-Elemente erhalten durch die private-Ableitung den Status „private“.
• private-Elemente sind auch nach der Ableitung private – es kann nicht direkt auf sie
zugegriffen werden, außer mithilfe einer friend-Deklaration in der Basisklasse.

Hier eine Übersicht über die Möglichkeiten von Ableitungen:

Ableitung Element in der Basisklasse Element in der abg. Klasse

public public public


protected protected
private private (nicht zugreifbar)

protected public protected


protected protected
private private (nicht zugreifbar)

private public private


protected private
private private (nicht zugreifbar)

10.4 Redefinition von Zugriffsrechten/Konvertierung

• Eine Redefinition ist eine Rücksetzung der Zugriffsrechte auf den Status in der
Basisklasse. Beispielsweise ist Klasse B die Basisklasse, Klasse A die abgeleitete. Wenn
nun x in B als „protected“ spezifiziert ist, ist x nach einer „private“-Ableitung eigentlich
vom Typ „private“. Wenn allerdings mittels qualifiziertem Namen (d.h. mit
Basisklassenname und Bereichszugriffoperator) „B::y“ angegeben wird, wird der Zustand
auf den der Basisklasse (hier protected) geändert.
• Derselbe Vorgang ist natürlich auch für Funktionen möglich.
• Im Falle einer Namensüberdeckung wird auf das nichtvererbte Element zugegriffen
(wobei die Definitionen der gleichnamigen Elemente nicht identisch sein müssen). Wenn
auf die überdeckten Elemente (also die der Basisklasse) zugegriffen werden soll, muss der
Zugriff entsprechend qualifiziert werden.
• Für public-abgeleitete Elemente (oder auch Zeiger oder Referenzen auf solche) wird eine
implizite Konvertierung durchgeführt; jedoch wird nur das Objekt der abgeleiteten Klasse
in ein Objekt der Basisklasse konvertiert, und nicht umgekehrt. (Weil die abgeleitete
Klasse alle Elemente der Basisklasse beinhaltet, jedoch nicht umgekehrt). Deshalb kann
überall, wo ein Objekt (oder ein Zeiger darauf) der Basisklasse benötigt wird, auch ein
Objekt der abgeleiteten Klasse verwendet werden.
C++ KOMPENDIUM 53
Rev. 1.01
10.5 Nichtvererbbare Klassenelemente

• friends: Eine als friend einer Klasse deklarierte Funktion ist nicht automatisch auch
friend einer aus ihr abgeleiteten Klasse.
• Konstruktoren dürfen nicht vererbt werden – die Objekte der abgeleiteten Klasse sollen
nämlich nicht mit Basisklassenobjekten initialisiert werden.
o Allerdings müssen nicht die von der Basisklasse geerbten Objekte wieder mittels
des Konstruktors der abgeleiteten initialisiert werden – das erledigt der
Konstruktor der Basisklasse (ein expliziter Aufruf ist natürlich trotzdem möglich).
Nur die neu hinzugekommenen Elemente werden mittels des der abgeleiteten
Klasse eigenen Konstruktors initialisiert.
o Der Standardkonstruktor der Basisklasse wird innerhalb der abgeleiteten Klasse
automatisch verwendet.
o Konstruktoren werden zur Erzeugung von Objekten abgeleiteter Klassen in
folgender Reihenfolge aufgerufen: Zuerst der Konstruktor der Basisklasse(n),
dann die Konstruktoren der Elementobjekt-Klassen, und schließlich die
Konstruktoren der abgeleiteten Klasse.
• Destruktoren werden ebenfalls nicht vererbt. Für Basisklassenteil einer abgeleiteten
Klasse ist der Basisdestruktor zuständig (Der Aufruf erfolgt durch den Destruktor der
abgeleiteten Klasse).
o Die Löschung von Elementobjekten übernimmt der Elementobjekt-Klassen-
Destruktor (Aufruf wieder durch durch den Destruktor der abgeleiteten Klasse)
o Der Aufruf der Destruktoren ist umgekehrt zum Aufruf der Konstruktoren. Die
Reihenfolge ist: (Basis-, Element-, Ableitungs-)konstruktor im Gegensatz zu
(Ableitungs-, Element-, Basisklassen-)destruktor.
• Zuweisungsoperatoren werden nicht vererbt. Für einen fehlenden Zuweisungsoperator
der abgeleiteten Klasse wird der der Basisklasse aufgerufen. Andernfalls ist eine explizite
Definition eines Zuweisungsoperators für die abgeleitete Klasse nötig.

10.6 Virtuelle Funktionen

• Virtuelle Funktionen sind z.B. für folgendes Szenario nützlich: Ein Basisklassenzeiger
kann zwar auf Objekte einer abgeleiteten Klasse verweisen, aber es kann darauf nicht
ohne weiteres zugegriffen werden. Mittels einer virtuellen Funktion kann jedoch auch auf
nichtvererbte Elementfunktionen abgeleiteter Klassen über einen Basiszeiger (ohne eine
explizite Konvertierung) zugegriffen werden. Eine Klasse mit virtueller Funktion nennt
man auch eine polymorphe Klasse.
o Virtuelle Funktionen werden mit dem Schlüsselwort „virtual“ definiert. Eine
Definition ist aber nur innerhalb der Klasse möglich.
o Wenn eine virtuelle Funktion in einer abgeleiteten Klasse in Parametern und
Rückgabewert mit der virtuellen Funktion der Basisklasse übereinstimmt, kann
das Schlüsselwort „virtual“ entfallen. Wenn aber z.B. nur der Rückgabewert
unterschiedlich ist, kommt es zur Fehlermeldung.
o Konstruktoren: wird eine virtuelle Funktion innerhalb einer abgeleiteten Klasse
mit dem Konstruktor der Basisklasse aufgerufen, wird die Version der Basisklasse
ausgeführt.
C++ KOMPENDIUM 54
Rev. 1.01
o Destruktoren können virtuell sein.
o Beachte: Der Zugriff auf eine virtuelle Funktion hängt vom Zugriffstatus (z.B.
„public“ oder „private“) jener Klasse ab, auf die der Zeiger veweist.

10.7 Abstrakte Klassen/rein virtuelle Funktionen

• Virtuelle Funktion müssen in der Basisklasse zwar deklariert, aber nicht definiert werden,
gemäß folgender Syntax:

virtual typ_rückgabewert funktionsname ( [parameterliste] ) = 0 ;

• Virtuelle Funktionen werden benötigt, wenn z.B. die Basisklassenversion der Funktion
keine Verwendung hat, man aber in den abgeleiteten Klassen auf die virtuelle Funktion
zurückgreifen will.
• Mittels abstrakter Klassen können keine Objekte erzeugt werden, und sie sind nicht als
Funktionsparameter noch als Rückgabewerte zulässig.
• Zeiger und Referenzen auf abstrakte Klassen sind zulässig.

10.8 Mehrfachvererbung

• Eine Klasse kann von mehreren Basisklassen abgeleitet werden, z.B. „class Z: public X,
public Y“. Hier erbt Klasse Z alle Objekte der Klasse X und der Klasse Y.
• Achtung: Mehrdeutigkeitsprobleme sind möglich, wenn z.B. die Klassen X und Y
Objekte gleichen Namens besitzen. Man qualifiziere die Namen also eindeutig, z.B.
„Z.X::a = 1;“ (Objekt a der Klasse Z, das von der Klasse X vererbt wurde, wird auf den
Wert 1 gesetzt)
• Virtuelle Basisklassen entstehen, wenn in der Basisklassenspezifikation in der
abgeleiteten Klasse virtual hinzugefügt wird, wenn z.B. „class Basis;“ definiert ist, und
dann mittels „class abgeleitet : virtual public Basis;“ abgeleitet wird. Damit wird erreicht,
dass bei einer (virtuellen) Basisklasse, mehreren davon abgeleiteten Klassen, und einer
von den diesen abgeleiten Klasse (hier also Mehrfachvererbung) in der „abgeleiten Klasse
2. Grades“ nur ein Satz der Elemente der Basisklasse vererbt wird.
• Eine Basisklasse kann aber auch für verschiedene Ableitungszweige gleichzeitig virtuell
und nichtvirtuell sein.
• Die Aufruf-Reihenfolge von Konstruktoren ist in einem solchen Fall:
1. Konstruktoren der virtuellen Basisklassen
2. Konstruktoren der nichtvirtuellen Basisklassen
3. Konstruktoren der Elementobjekt-Klassen
4. Konstruktoren der abgeleiten Klasse

C++ KOMPENDIUM 55
Rev. 1.01
11. Operatorfunktionen

• Operatorfunktionen sind gewöhnliche Funktionen in anderer Notation. Sie sind


grundsätzlich vordefiniert in Bezug auf ihre Operanden (z.B. dürfen nach „==“ keine
Strings stehen. Bis auf wenige Ausnahmen sind Operatorfunktionen überladbar. Nicht
überladbar sind: „::“, „.“, „sizeof“ „*“ und „?“.
• Grundsätzliche Regeln für Operatorfunktionen:
o Eine Operatorfunktion ist stets klassenbezogen – entweder ist sie eine
nichtstatische Elementfunktion (außer new und delete, denn diese sind statisch)
oder eine globale Funktion, die als Parameter eine Klasse bzw. Referenz auf
Klasse besitzt. Die Operatoren „=“, „( )“, „[ ]“, „->“ und der „cast“-Operator
müssen als nichtstatische Elementfunktionen angewandt werden.
o Die Priorität und die Assoziativität werden durch das Überladen nicht verändert.
Ebenfalls wird die Operandenzahl nicht verändert (außer bei new und delete).
o Es können keine neuen Operatorsymbole eingeführt werden.
o Für Parameter können keine default-Werte vereinbart werden.
• Die Definition einer Operatorfunktion geschieht mit dem Schlüsselwort „operator“.
Überlade z.B. „==“ mittels „operator==“.

11.1 Operatorfunktionen für unäre Operatoren

• Nichtstatische Elementfunktionen oder Funktionen, die als Parameter eine Klasse oder
eine Referenz auf eine Klasse haben, können mittels folgender Syntaxen zum Überladen
eines Operators verwendet werden:
o Für Elementfunktion außerhalb der Klasse:

typ_rückgabewert klasse:: operator symbol ( ) { anweisungen }“

o Für Elementfunktion innerhalb der Klasse haben sie identische Syntax, aber
ohne „klasse::“
o Als globale Funktion:

typ_rückgabewert operator symbol ( klasse [&] par_name )


{ anweisungen }

• Nach der Definition der Operatorfunktion ist eine Deklaration innerhalb der Klasse
notwendig.

11.2 Operatorfunktionen für binäre Operatoren

• Hier ist eine Operatorfunktion entweder eine nichtstatische Elementfunktionen mit einem
(sichtbaren) Parameter oder eine globale Funktion mit zwei Parametern, von denen
mindestens einer eine Klasse oder eine Referenz auf eine Klasse sein muss.
C++ KOMPENDIUM 56
Rev. 1.01
• Als Elementfunktion außerhalb der Klasse ist folgende Syntax zu verwenden:

typ_rückgabewert klasse:: operator symbol (typ par_name { anweisungen }

• Als Elementfunktion innerhalb der Klasse oder globale Funktion ist die Syntax
identisch, nur ohne „klasse::“. Zusätzlich ist als globale Funktion „(typ par_name1, typ
par_name2)“ anzugeben.
• Die Elementfunktion hat als ersten, unsichtbaren Parameter den this-Zeiger, der auf das
Objekt verweist, für das die Operatorfunktion aufgerufen wird.
• Nur für den Fall der Elementfunktion gilt: Hierbei kann der rechte Operand kein
Klassenobjekt sein, obwohl er z.B. mittels des „+“-Operators zu einem solchen hinzu
summiert werden kann. Allerdings muss der linke Operand ein Klassenobjekt sein ( was
daran liegt, dass Konstruktor der Klasse gleichzeitig ein Konvertierungskonstruktor ist
und dafür bestimmte Regeln festliegen) Wenn dies nicht so sein soll, muss eine globale
Funktion definiert werden, für die dann keine implizite Konvertierungsregel gilt.
• Allgemein gilt: Es existieren globale Funktion für Operatoren (bei denen der linke
Operand nicht unbedingt ein Klassenobjekt ist), die die Operanden nicht verändern, wie
z.B. die Operatoren +, -, *, / oder == und !=. Dagegen werden gewöhnlich für Operatoren,
die ihren linken Operanden modifizieren (z.B. Zuweisungsoperatoren) Element-
Operatorfunktionen verwendet.

11.3 Der Zuweisungsoperator “ = ”

• Der Compiler erzeugt implizit den default-Zuweisungsoperator „=“, so dass z.B. für eine
Klasse class1 nach Definition „class1 a, b;“ die Zuweisung „b = a;“ möglich wird.
• Wenn aber beispielsweise vorher definierte Klassen Zeiger auf dynamisch allokierte
Speicherbereiche beinhalten (z.B. char *string = ... ) wird nach „class strg;“ und „strg x,
y;“ durch die Zuweisung „y = x;“ nur die Anfangsadresse von x ins Anfangselement von
y kopiert, d.h. beide verweisen danach auf denselben Speicherbereich (also eine „flache
Kopie“). Wenn eine „tiefe Kopie“ erstellt werden soll (so dass alle Werte kopiert, die
Speicherbereiche aber getrennt belassen werden), muss der Zuweisungsoperator
entsprechend überladen werden.
o Für ein Überladen des „=“-Operators zum Zwecke einer Erstellung einer tiefen
Kopie muss dies in der Form einer nichtstatischen Elementfunktion geschehen.
(d.h. der linke Operand muss ein Klassenobjekt, also ein L-Wert sein).
o Wenn der Zuweisungsoperator mit return(*this) angegeben wird, ist eine
Verkettung (z.B. „a = b = c;“ ) möglich.

11.2 Der Index-Operator “ [ ] “

• Der Index-Operator wird mit folgender Syntax verwendet: „Operand1 [ Operand2 ]“. Er
kann nur als nichtstatische Elementfunktion verwendet werden (d.h. der linke Operand ist
ein Klassenobjekt).
• Äquivalent dazu ist der explizite Aufruf mit: „Operand1.operator [ ] (Operand2)“.

C++ KOMPENDIUM 57
Rev. 1.01
11.3 Der Funktionsaufruf-Operator “ ( ) “

• Der Funktionsaufruf-Operator kann nur als nichtstatische Elementfunktion überladen


werden (d.h. der linke Operand ist ein Klassenobjekt).
• Dies geschieht gemäß der Syntax: „objekt ( [ ausdruck1, ausdruck2, ... ] )“. Eckige
Klammern signalisieren hier optionale Einträge.
• Äquivalent sind z.B. “X(a1, a2)” und die expliziten Schreibweise „X.operator ( ) (a1,
a2)“.

11.4 Der Pfeiloperator “ -> “

• Der Pfeiloperator, der für Zugriff auf Klassenelemente sorgt, wird als unärer Operator
interpretiert. Es ist allein die Verwendung als nichtstatische Elementfunktion ohne
Parameter möglich.
• Diese folgt der Syntax: „objekt -> element“. Hier ist die Angabe von „element“ nur aus
syntaktischen Gründen notwendig – „element“ ist kein Operand des Operators.

11.5 Die Operatoren new und delete

• Diese Operatoren lassen sich nicht nur für eine bestimmte Klasse, sondern global
überladen – damit ist die Anwendung auf beliebige Datentypen möglich.
• Es ist ebenfalls nicht unbedingt notwendig, dass mindestens ein Parameter ein
Klassenobjekt ist.
• Auf jeden Fall ist die Überladung mit statischer Elementfunktion – und damit auch der
Parameter „static“ – nicht notwendig.
• new folgt der Syntax:

void* operator new ( size_t objektgroesse [ , ...] );

Dies gibt stets einen Zeiger des Typs void zurück. Eckige Klammern signalisieren
optionale Formalparameter.
o Dynamische Objekt-Arrays allokieren: dies geschieht normalerweise mittels des
default-new-operator. Bei manchen Compilern kann man dennoch den new-
Operator überladen: z.B. mit

void* class1::operator new[] (size_t arraygröße);

o Weitere Parameter für new: Wenn zusätzlicher Formalparameter für new


angegeben werden (mittels des Schemas „typ par2, typ par2, ...“), müssen beim
Aufruf Aktualparameter angegeben werden.
• delete: beim Operator „delete“ ist ein Rückgabewert vom Typ void vorgeschrieben. Der
erste Formalparameter ist ein Zeiger vom Typ void* , möglicher zweiter Formalparameter
ist size_t . Dies ergibt die Syntax:
C++ KOMPENDIUM 58
Rev. 1.01
void operator delete ( void* objekt_zeiger [ size_t groesse ] );

Angaben in eckigen Klammern sind optional.


• new und delete global überladen: hierfür gelten die gleichen Verwendungsregeln wie
für das lokale Überladen, nur darf kein zweiter Formalparameter bei delete angegeben
werden.

11.6 Der cast-Operator/Konvertierungsoperatoren

• Die Konvertierung eines Klassenobjekts in einen anderen Typ erfolgt mittels Überladung
des cast-Operators.
• Er wird als nichtstatische Elementfunktion ohne einleitende Typangabe und ohne
Parameter verwendet, gemäß der Syntax:

[name_klasse ::] operator typ ( ) { [ anweisungen ] return (wert); }

„typ“ bezeichnet hier den Datentyp, in den das Klassenobjekt konvertiert werden soll und
damit den Typ des Rückgabewerts. Angaben in eckigen Klammern sind optional.
• Mit Hilfe dieser Prozedur sind auch Konvertierung eines Objekts der Klasse A in eines
der Klasse B möglich.

C++ KOMPENDIUM 59
Rev. 1.01
12. Streams

• Die Stream-Bibliothek (deren ganzer Titel „Standard Stream Input/Output Library“ ist) ist
ein Teil der C++ Standardbibliothek. An der Spitze der Stream-Bibliothek steht die
Basisklasse „ios“. Davon abgeleitet sind „istream“ (Datenstromeingabe) und „ostream“
(Datenstromausgabe). Von beiden abgeleitet ist „iostream“.
• Grundsätzliche ist die Stream-Bibliothek eingeteilt in
o Allgemeine Eingabe/Ausgabe: „istream_withassign“, „iostream“ und
„ostream_withassign“ leiten sich von „istream“ und „ostream“ ab,
„iostream_withassign“ wiederum von „iostrream“.
o Daten-Eingabe/Ausgabe: „ifstream“, „iostream“, und „ofstream“ leiten sich von
„istream“ und „ostream“ ab, „fstream“ wiederum von „iostream“.
o Speicher-Eingabe/Ausgabe: „istrstream“, „iostream“, „ostrstream“ leiten sich
von „istream“ und „ostream“ ab, „strstream“ wiederum von „iostream“.

12.1 Allgemeine Ein- und Ausgabe

• Vordefinierte Stream-Objekte zur Standardein- und –ausgabe sind:


o cin: die Standardeingabe
o cout: die Standardausgabe
o cerr: die Standardfehlerausgabe
o clog: das Standardprotokoll

• Ein/Ausgabe mit Datenströmen ist grundsätzlich gepuffert, was zu einer unerwarteten


Verzögerung der Ausgabe führen kann, wenn man nicht die sog. „tied streams“ benutzt,
bei denen der Ausgabepuffer geleert wird, wenn eine Eingabe ansteht. (tied-streams sind
beispielsweise „cin“ und „cout“, aber nicht „scanf“). Abhilfe kann man schaffen durch die
ausnahmslose Verwendung von tied-streams oder durch explizites Leeren des
Ausgabepuffers eines Ausgabestreams durch den Manipulator „flush“, der z.B. nach cout
<< „ ..““ mit „<< flush;“ angefügt wird (oder: „cout << setiosflags(0x2000);“ bewirkt
wiederholtes Leeren des Ausgabepuffers. Abschalten mit „resetiosflags(0x2000)“).
• „tie“-Verbindungen werden mit dem Schlüsselwort „tie“ gesetzt. Beispielsweise bewirkt
„cin.tie(NULL);“ die Lösung von cin und cout, „cin.tie(&cout);“ bewirkt
Wiederherstellung der tie-Verbindung.
• Ein/Ausgabe benutzerdefinierter Datenobjekte: „cin >> i;“ ist äquivalent zu
„cin.operator >>(i);“, da die Operatoren „>>“ und „<<“ für die Standardtypen
entsprechend überladen sind – die Klassen „istream“ und „ostream“ beinhalten die
Deklarationen „istream& operator>>(int& i);“ (analog für ostream). Für
benutzerdefinierte Streams müssen die Operatoren „>>“ und „<<“ entsprechend
überladen werden, nach folgendem Muster:
o Für istream:

istream & operator>> (istream& istr, UDC& u)


{

C++ KOMPENDIUM 60
Rev. 1.01
//Anweisungen
return (istr);
}

o Für ostream:

ostream& operator<<( ostream& ostr, const UDC& u)


{
//Anweisungen
return (ostr);
}

• Zuweisungen: z.B. definiert „istream_withassign ein;“ einen Eingabestrom „ein“, der


noch mittels „ein = cin;“ mit einem Standardeingabegerät verbunden werden muss (da
„ein“ sonst nicht initialisiert wäre). Ein analoges Objekt „aus“ kann auch für „cout“
konstruiert werden. Allerdings ist zu beachten, dass ein Puffer für neue Ein/Ausgabe-
Operatoren definiert werden muss (mittels „filebuf eingabeparamter (0);“ und „filebuf
ausgabeparameter (1);“). Außerdem ist eine tie-Verbindung zu schaffen, mittels
„ein.tie(&aus);“

12.2 Ein- und Ausgabe von Daten

• Um Zugriff auf eine Datei auch außerhalb des Programms zu erhalten, sind die Klassen
„ifstream“ und „ofstream“ notwendig. Beide sind mittels der Include Datei „fstream.h” in
den Programmkopf einzuschließen. Das Öffnen und Schließen von Dateien lässt sich auf
mehreren Wegen erreichen.

• Implizites Öffnen von Datein: folgt der Syntax:

klasse objektname (dateiname_string [ , öffnungsmodus ] );

Eckige Klammern signalisieren optionale Angaben. Mit dem Konstruktorparamter


„öffnungsmodus“, z.B. „ofstream outfl („ausgabe.dat“, ios::out);“, lässt sich die Datei
„ausgabe.dat“ für die Ausgabe öffnen. Der Zugriff erfolgt dann über den Befehl „outfl“.
o Folgende Öffnungsmodi sind möglich:
 ios::in: Datei für Eingabe öffnen
 ios::out: Datei für Ausgabe öffnen
 ios:ate: Nach dem Öffnen Postition an das Dateiende setzen.
 ios::app: Neue Daten werden am Dateiende angefügt
 ios::trunc löscht Dateiinhalt, wenn ios::out gesetzt wurde
 ios::nocreate bewirkt Fehler, wenn Datei nicht exisitert
 ios::noreplace bewirkt Fehler, wenn Datei exisitert.
 ios::binary öffnet Datei im Binärmodus (default: Textmodus)

o Der default-Wert bei fehlender Angabe ist ios::out.

C++ KOMPENDIUM 61
Rev. 1.01
o Bei fstream muss ein Öffnungsmodus angegeben werden: z.B. „fstream iofl
(„einaus.dat“, ios::in|ios::out);“

• Textmodus/Binärmodus: um binäre Datei zu bearbeiten, öffnet man sie im Binärmodus


mittels „ios::binary“ Beispiel: „ifstream infile(„binary.dat“ ios::in|ios::binary);“ öffnet die
binäre Datei „binary.dat“.
• Implizites Schließen von Dateien: Der Destruktor der Streamklasse sorgt für ein
automatisches Schließen der Datei, wenn der Gültigkeitsbereich des mit ihr verknüpften
Streamobjekts verlassen wird
• Explizites Öffnen/Schließen von Dateien:
o Öffnen: Um ein Objekt zu öffnen, folge man der Syntax

objekt.open(dateiname , [öffnungsmodus] );

Anstatt mit „objekt“ den Namen des Objekts anzugeben ist auch ein Zeiger auf das
Objekt möglich.
o Schließen: Das Schließen eines Objekts erfolgt mit dem Befehl „objekt.close( )“
• Beispiel: durch

ifstream infile;

wird ein Stream-Objekt definiert. Durch

infile.open („test.dat“);

wird die Datei explizit geöffnet und mit dem Stream „infile“ verknüpft.
o Beim Öffnen und Schließen empfiehlt es sich, einen Absatz zur Fehlerbehandlung
einzuschließen, z.B. mit der Syntax:

if(file == NULL) { cout << „Fehler!“; exit(1); }

• Ein- und Ausgabeoperationen mit Dateien sind wiederum auf mehrere Arten möglich:
• Formatierte Eingabe/Ausgabe folgt dem gleichen syntaktischen Muster wie die
vordefinierten Standardstreams „cin“ und „cout“, an deren Stelle jetzt Streamobjekte der
Klassen „ifstream“, „ofstream“ und „fstream“ stehen.
• Zeichenweise Ein/Ausgabe erfolgt mit „cin.get(line)“ und „ofl.put(Zeichen)“.
• Blockweise Ein/Ausgabe ist der Zeichenweise Ein- und Ausgabe ähnlich: Sie folgt dem
Schema: „streamname.write (char* puffer, int maxzahl).“ Bei ostream muss „const
char*puffer“ angegeben werden. Beispiele:

ofstream ofl(„doub.dat“, ios::out|ios::binary);


ofl.write ((char*)x, sizeof(x));
ifstream ifl(„doub.dat“, ios::in|ios::binary);
ifl.read(char*)y, sizeof(y));

• Standardeingabe/ausgabe als Dateien: „ifstream“ und „ofstream“ haben neben


Konstruktoren auch noch einen Dateideskriptor, der mit „fd“ aufgerufen wird. Der Aufruf
C++ KOMPENDIUM 62
Rev. 1.01
folgt dem Muster: „dateideskriptor ifstream::fd( ) const;“, und analog für ofstream.
Beispiel: Es werde definiert

ifstream ifl(„test.dat“);

Dann ermittelt

dateideskriptor fdc = ifl.fd( ); oder int fdc = ifl.fd( );

den Deskriptor der mit „ifl“ verknüpften Datei.


o Standard-Dateideskriptoren exisiteren aber auch in vordefinierter Form, so dass
„ifstream fin (0)“ ein ifstream-Objekt „fin“ erzeugt, das mit der Standardeingabe
verbunden ist, bzw. „ofstream fout(1)“ „fout“ mit der Ausgabe. Analog ist bei
„ofstream ferr(2);“ „ferr“ mit der Standardfehlerausgabe verbunden, sowie
„ofstream fprn(4);“ „fprn“ mit der Standarddruckausgabe. Beispiel:

char name;
fin >> name

liest „name“ ein.

• Bewegen in Dateien: definiere z.B. Streamklasse

fstream iofl(„datei.dat“ öffnungsmodus);

und ein Objekt „iofl.seekg“ mit der Streamposition n (z.B. 0). Eine Konstante (z.B.
„ios::beg“); setzt die Leseposition (Streamposition) auf das Byte Nummer n, bezogen auf
den Dateianfang. Oder man setzt die auf Position relativ zur aktuellen Position durch
„ios::cur“ oder relativ zum Dateiende mit „ios::end“.
o Weitere Funktionen zum Bewegen in Dateien sind
 istream::tellg( ) ; liefert die aktuelle Position relativ zum Dateianfang.
 ostream::seekp( streamposition n); setzt die Position zum Schreiben auf
Byte Nummer n, bezogen auf den Dateianfang.
 ostream:seekp(streamoffs, ios::seek_dir p); Die Schreiboperation beginnt
bei Byte mit offset offs, bezogen auf Anfangspunkt p.
 ostream::tellp ( ); liefert die aktuelle Position des Schreibvorgangs relativ
zum Dateianfang.

12.3 Ein- und Ausgabeoperationen mit Speicherbereichen

• Ein/Ausgabe mit Speicherbereichen ist mittels der Streamklassen „istream“ und


„ostrstream“ möglich, für die man die Include-Datei „strstream.h“ benötigt.
Konventionelle Alternativen sind „sprintf“ und „sscanf“ für die Ein/Ausgabe mit char-
Arrays.
o Eingabe setzt ein char-Array voraus, dessen Arbeitsspeicheraddresse dem
Konstruktor der Streamklasse übergeben wird. Beispiel:

C++ KOMPENDIUM 63
Rev. 1.01
char d [] = „4.23“;
istrstream iss(d);
double e;

Nun liest

iss >> e;

die in d gespeicherte Zahl als Eingabe, konvertiert sie in double-Wert und


speichert diesen in e. Es ist auch möglich, diesen Vorgang in eine Funktion
einzubinden.
o Ausgabe: Beispiel:

char x_alpha[32];
int x = 1234;

Definiere Streamobjekt mittels

ostrstream oss(x_alpha, 32);

Nun konvertiert
oss << x << ends;

„x“ in eine Zeichenkette und speichert diese in „x_alpha“. „ends“ ist hier ein
Manipulator, der ein Nullzeichen an Zeichenkette anfügt.
o Dynamische Ausgabearrays werden verwendet, ohne explizit den
Speicherbereich anzugeben. Beispiel:

ostrstream oss;
oss << 1234 << ends;

Damit das übrige Programm auf Speicherbereich zugreifen kann, muss dieser
mittels ostrstream-Funktion „str“ übergeben werden, die nach folgendem Muster
konstruiert wird: „char * ostrstream::str();“ Beispiel:

char *buf = oss.str( ) ;


cout << buf;

C++ KOMPENDIUM 64
Rev. 1.01
13. Spracherweiterungen

• Wahrheitswerte (bool): Standardmäßig sind 0 = false und 1 = true definiert, so dass


Funktionen wie

bool istrue;
if (istrue == false) istrue = true;

geschrieben werden können.


• Explizite Typumwandlung: für explizite Konvertierungen sind „static_cast“,
„const_cast“, „reinterpret_cast“ und „dynamic_cast <zieltyp> (ausdruck)“ reserviert.
o static_cast: ist einfaches Gegenteil der impliziten Typkonvertierung.
o const_cast: für Konvertierung von „const“ in nicht-„const“.
o reinterpret_cast: außer bei „const“ für beliebige Typumwandlungen
o dynamic_cast: führt eine Typumwandlung während der Laufzeit des Programms
durch; das Ziel muss ein Zeiger oder eine Referenz auf eine Klasse sein.
• Namensbereiche: Wenn z.B. in Klassen oder Include-Dateien Objekte gleichen Namens
erstellt wurden, kann man Doppeldeutigkeiten mit den „namespaces“ vermeiden, mit
Hilfe der Syntax:

namespace [name_namensbereich] {deklarationen/definitionen_der_elemente};

o Der Zugriff erfolgt mittels „name_namensbereich::name_element“. Beispiel:

namespace pool1 {int a = 0};


namespace pool2{int a = 1};

Nun greift

pool1::a=3;

auf das Element „a“ aus „pool1“ zu und weist ihm innerhalb dieses
Namensbereiches den Wert 3 zu. .
o using-Deklarationen: mittels „using name_namensbereich::elementname;“ ist die
Verwendung des Datenobjekts im lokalen Gültigkeitsbereich in gewöhnlicher
Weise und ohne Qualifizierung mit „::“ möglich, z.B. „using pool::x;“
o using-Direktiven: „using namespace name_namensbereich“ macht alle Namen
eines Namensbereich ohne Qualifizierung verfügbar.

• Laufzeit-Typinformationen: Um Zur Laufzeit eines Programmes Informationen über


den Typ eines Objekt zu erhalten, existieren folgende Objekte:
o dynamic_cast-Operator: Mit diesem Operator lassen sich Konvertierungen zur
Laufzeit eines Programmes durchführen und Prüfungen, ob Konvertierung eines
Zeigers auf eine polymorphe Basisklasse in Zeiger auf abgeleitete Klasse
fehlerhaft ist. Syntax: „dynamic_cast <zieltyp> (ausdruck)“.
C++ KOMPENDIUM 65
Rev. 1.01
o typeid-Operator und type_info-Klasse: Mittels „typeid (ausdruck)“ oder „typeid
(typname)“ wird eine Referenz auf ein Objekt der Klasse „type_info“ (definiert
innerhalb der Include-Datei „typeinfo.h“) zurückgegeben. Beispiel der
Vewendung:

double d; if (typeid(d) == typeid(double)) ... anweisungen

• Ausnahmebehandlung: Die Ausnahmebehandlung geht mit den Befehlen „try“, „throw“


und „catch“ vor sich.
o try: „try { ... };“ legt die Anweisungen fest, für die eine Ausnahmebehandlung
durchgeführt wird.
o catch folgt der Syntax: „catch (parameter) { Anweisungen };“ Die „catch“-
Anweisung befindet sich direkt hinter dem try-Block, und reagiert bzw. bearbeitet
dort entstandene Fehler.
o throw folgt der Syntax: „throw(ausdruck);“ Beispielsweise gibt „if(!infile) throw
0;“ einen Fehlerwert aus, falls ein Fehler beim Öffnen der Datei durch infile
auftritt. Außerdem ist „throw“ hilfreich, wenn beispielsweise bei der Definition
einer Funktion kenntlich gemacht werden soll, dass sie Ausnahmen produzieren
kann, z.B. bei „double funktion(int x) throw(char *);

• Templates: Es existieren Funktions- und Klassentemplates, mit denen sich beliebig viele
Varianten einer Funktion oder Klasse durch nur eine Definition erzeugen lassen.
o Funktionstemplates haben zwei mögliche Syntaxen:

template <class typ [, class typ2, class typ3, ... ] > funktionsdefnition

oder die neuere Schreibweise mit dem Schlüsselwort „typename“:

template <typename typ [, typename typ2, ... ]> funktionsdefinition

Eckige Klammern signalisieren optionale Angaben.


o Beispiel:

template <typename T> void funktion1 (T* array1, int x1)

Nun kann „void funktion1“ für verschiedene Aufgaben benutzt werden. Um das
Template zu variieren (d.h. zu überladen), benutze man die Syntax: „template <>
funktionsdefintion“.
o Klassentemplates: haben eine analoge Syntax:

template <parameterliste> klassendefintion

Parameter sind wieder mit „class“ oder „typename“ anzugeben, allerdings sind
auch gewöhnliche Parameter wie bei Funktionen möglich.
o Beispiel:

template <typname T> class array { klassen_definition}

C++ KOMPENDIUM 66
Rev. 1.01
Dann generiert „array <int> a(10);“ eine Klasse „array <int>“ und definiert ein
Objekt a dieser Klasse.
o Standard-Template-Library (STL): Sammlung von vordefinierten Templates
für verschiedenste Programmbereiche, hier z.B. Containerklassen.

Hier eine Übersicht über die STL-Container-Funktionen (Ein Iterator ist ein
Zeiger af Element eines Containers):

begin () Liefert Iterator auf erstes Element zurück

end() Liefert Iterator auf hinter dem letzten Element liegendes Element
zurück

rbegin() Liefert rückwärts zählenden Iterator auf hinter dem letzt. Elem. lieg.
Element zurück

rend() Liefert rückwärts zähl. It. auf erstes Element zurück

size() Gibt Anzahl der Elemente zurück

maxsize() Gibt maximale mögliche Größe zurück

empty() Liefert true, falls Container leer, sonst false

Swap() vertauscht Inhalt zweier Container

logisch: == liefert true, wenn zwei Cont. die lgeiche Größe und Elemente haben

logisch: != Gegenteil

< Liefert für (a<b) true zurück, wenn a lexikografisch vor b

> Analog

<= Analog

>= Analog

• C++-Präprozessor: Es existieren die folgenden Präprozessor-Anweisungen:

#include
#define
#undef
#if
#elif
#else
#endif
#ifdef
#ifndef
#error
#line
#pragma
C++ KOMPENDIUM 67
Rev. 1.01
o Präprozessor-Anweisungen können an beliebiger Stelle stehen, meist werden sie
jedoch an den Anfang gesetzt.
o Include-Dateien: Syntax: #include <Dateiname> oder #include „Dateiname“
o Symbolische Konstanten: definiere Konstanten aus Gründen der
Übersichtlichkeit vor dem Programm mit „#define name_der_konstanten [text]“,
z.B. „#define PI 3.1415“ (Die Kreiszahl π). Eckige Klammern signalisieren
optionale Elemente (d.h. eine #define-Anweisung ist auch ohne Ersatztext
möglich, z.B. #define fehler (wird wegen fehlenden Ersatztextes an allen Stellen
ihres Auftretens im Porgramm entfernt).
o Mittels „#undef name_der_konstanten“ kann die Definition rückgängig gemacht
werden.
o Makros: Beispiel: „#define TOP 8“. Manchmal wird in der C++-Literatur kein
Unterschied zwischen Makro und symbolischer Konstante gemacht. Dennoch
besteht ein Unterschied, weil ein Makro eine Operation repräsentiert und eine
symbolische Konstante eine nichtoperative Zeichenfolge darstellt.
o Makros ohne Parameter: folgen der Syntax: „#define makro_name text“. Mit
„text“ wird eine oder mehrere Operation(en)/Anweisung(en) repräsentiert.
o Beispiel:

#define NL cout << „\n“

bewirkt einen Zeilenvorschub im Programm überall da, wo „NL“ steht.


o Makros mit Parameter: haben die Syntax:

#define makro_name(par1, par2, …) text

o Beispiel:
#define summe(a,b) (a) + (b).

Hier sind a und b Aktualparameter, die nicht eigens definiert werden müssen, und
lediglich als Platzhalter für die Werte dienen, die dann im Programm mit dem
Makro verwendet werden, also z.B. summe(1,1) summiert 1+1.
o Klammern für (a) und (b) sind im Ersatztext notwendig, um Fehler zu vermeiden.
o Makros löschen: geschieht nach folgendem Schema: „#undef makro_name“
o Bedingte Kompilierung: mittels verschiedener Direktiven von „#if“ bis „#ifndef“
lässt sich ein Teil des Programms nur dann kompilieren, wenn eine bestimmte
Bedingung (true) erfüllt ist. Dies geschieht mittels der Syntax:

#if konstanter_ausdruck
programmteil
[#elif konstanter_ausdruck
programmteil
#elif konstanter_ausdruck
programmteil
….
#else
programmteil ]
C++ KOMPENDIUM 68
Rev. 1.01
#endif

Die Klammern „[ ]“ deuten an, dass der betreffende Teil nicht unbedingt nötig ist.
„konstanter_ausdruck“ ist hier die Bedingung: wenn diese ungleich Null ist,
werden die Anweisungen des zugehörigen „programmteil“s kompiliert, die
anderen werden aus dem Quellcode gelöscht.
o defined-Operator: “#ifdef” und “#ifndef”: haben die Syntax:

#if defined (symb_konst)


programmteil
[#elif defined (symb_konst)
programmteil
...]
#endif

Nur wenn „symb_konst“ definiert ist, wird „programmteil“ ausgeführt. Alternative


Schreibweise: “#if defined” entspricht “#ifdef”, “#if !defined” enspricht “#ifndef”
o #error: hat die Syntax: „#error: Message“. Der Befehl gibt „Message“ aus und
bricht den Kompiliervorgang ab.
o Pragmas: Pragmas sind Compiler-Direktiven, vergleichbar mit Compiler-
Optionen. Die Syntax ist denkbar einfach: „#pragma instruktion“. Die
Verfügbarkeit von Pragmas ist Compiler-abhängig. Beispielsweise kann mit dem
MS-C++ Compiler während Kompilierung mit „#pragma message(„text“)“ eine
Botschaft „text“ ausgegeben werden.

o Präprozessorkonstanten:

Konstante Bedeutung
__LINE__ Ganzzahlige Dezimalkonstante, die die Nummer der aktuellen Zeile im
Quelltext angibt.
__FILE__ Stringkonstante, die den Namen der aktuellen Quelldatei angibt.
__DATE__ Stringkonstante, die das Datum der letzten Kompilierung angibt.
__TIME__ Stringkonstante, die die die Uhrzeit der letzten Kompilierung angibt.
__cplusplus Ist definiert, wenn ein C++-Programm kompiliert wird.

Wenn diese Konstanten in den Quellcode (mit z.B. cout) integriert werden,
werden die jeweiligen Daten über das Programm ausgegeben.

C++ KOMPENDIUM 69
Rev. 1.01