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 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.
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
1.3 Konstanten
const int x = 1;
1.4 Variablen
C++ KOMPENDIUM 6
Rev. 1.01
int a = 1;
oder
double x = 2;
• 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:
Beispielsweise definiert
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
oder
quellcode /* Kommentar
mehrzeilig */
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).
char a = ’A’;
cout << a;
cout << x;
cout << y;
ist ebenso
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 << setiosflags (128L) << setiosflags(512L) << 123;
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:
Konventionelle Ausgabe
Für die konventionelle Ausgabe ist es nötig, am Programmanfang die Include-Datei „stdio.h“
mittels „#include(stdio.h)“ einzuschließen.
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:
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.
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);
Erläuterungen:
printf(„%+f“, 12.34);
printf(„%08g“, 1.234);
1.235
C++ KOMPENDIUM 13
Rev. 1.01
(hier wird wegen maximal 3 Nachkommastellen gerundet) und die Ausgabe
1.234500
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);
Beispielsweise liest
int = i;
C++ KOMPENDIUM 14
Rev. 1.01
und
cin >> i;
mit
0x10
und
020
möglich was beides zur Speicherung von „16“ führt, oder mit Manipulatoren dec, oct
und hex, z.B.
Mit
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;
scanf(„%d“, &x);
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
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)
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
zwar drei Eingabewerte gelesen, aber nur der erste und dritte berücksichtigt.
o Feldbreite: beispielsweise nach
int i, j;
und
145
machen, was
1 45
• 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.
Vergleichsoperatoren
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
• 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:
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
• 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
• 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.
• 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:
Typumwandlungen
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.Ä.
C++ KOMPENDIUM 24
Rev. 1.01
4. Kontrollstrukturen
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
if
else
if
else
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.
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 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’)
Beispiel:
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;
do
C++ KOMPENDIUM 27
Rev. 1.01
anweisung(en)
while (ausdruck);
Sprunganweisungen
• 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.
datentyp_des_verweisobjekts * name_des_zeigers;
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.
short a, b;
short *zs = &a;
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:
(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;
vz = &a;
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:
• 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;
const int a = 2;
const int &ref_a = a;
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
beispielsweise „long tabelle1 [10]“. Das Array heißt hier „tabelle1“, und besteht aus 10
Elementen des Typs „long“.
• Initialisierung geschieht z.B. mittels
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:
oder aus einem anderen, bereits bestehenden Array (hier „tabelle0“) kopiert werden
sollen:
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)
• 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
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
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:
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:
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“.
• Um einzelnen Objekten Speicher zuzuweisen, kann man die Funktion „new“ benutzen,
deren Syntax sich folgendermaßen gestaltet:
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
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:
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:
• 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;“
• 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;“
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:
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:
• calloc: bei dieser Funktion werden alle Elemente mit dem Wert 0 initialisiert. „calloc“
besitzt folgende Syntax:
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:
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
• 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:
Diese Funktion hat den Namen „funktion“, keinen Rückgabewert (void), zwei Parameter
(int a, b), und eine Anweisungen (a+b).
• 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
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;“)
• 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“.
• 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
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 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 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.
• 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.
• 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
• 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:
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.:
• 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:
C++ KOMPENDIUM 46
Rev. 1.01
void(Klasse1::*z)(double) = Klasse1::funktion1;
(objekt.*elementzeiger) (parameterliste)
oder
• 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.
C++ KOMPENDIUM 47
Rev. 1.01
class abc ... int a; int b; public: abc(int c, int d);
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:
name_klasse::~name_klasse ( ) { [Anweisungen] }
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.
• 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.
• 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
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:
• „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.
• „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.
• 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.
• 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.
• Virtuelle Funktion müssen in der Basisklasse zwar deklariert, aber nicht definiert werden,
gemäß folgender Syntax:
• 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
• 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:
o Für Elementfunktion innerhalb der Klasse haben sie identische Syntax, aber
ohne „klasse::“
o Als globale Funktion:
• Nach der Definition der Operatorfunktion ist eine Deklaration innerhalb der Klasse
notwendig.
• 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:
• 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.
• 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.
• 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 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.
• 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:
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
• 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:
„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“.
C++ KOMPENDIUM 60
Rev. 1.01
//Anweisungen
return (istr);
}
o Für ostream:
• 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.
C++ KOMPENDIUM 61
Rev. 1.01
o Bei fstream muss ein Öffnungsmodus angegeben werden: z.B. „fstream iofl
(„einaus.dat“, ios::in|ios::out);“
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;
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:
• 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:
ifstream ifl(„test.dat“);
Dann ermittelt
char name;
fin >> name
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.
C++ KOMPENDIUM 63
Rev. 1.01
char d [] = „4.23“;
istrstream iss(d);
double e;
Nun liest
iss >> e;
char x_alpha[32];
int x = 1234;
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:
C++ KOMPENDIUM 64
Rev. 1.01
13. Spracherweiterungen
bool istrue;
if (istrue == false) istrue = true;
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.
• 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
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:
Parameter sind wieder mit „class“ oder „typename“ anzugeben, allerdings sind
auch gewöhnliche Parameter wie bei Funktionen möglich.
o Beispiel:
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):
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
logisch: == liefert true, wenn zwei Cont. die lgeiche Größe und Elemente haben
logisch: != Gegenteil
> Analog
<= Analog
>= Analog
#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:
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:
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