Einführung in die Programmierung mit C++

Werbung
Einführung in die Programmierung mit C++
Peter Fischer
Folien basierend auf früheren Kursen von
Florian Painke, Volker Lindenstruth, Udo Kebschull
Inititut für Technische Informatik
ZITI
B6, 26, Mannheim
Universität Heidelberg
6. April – 9. April 2010
Kursinhalt
Einleitung
Was heißt eigentlich „Programmieren“?
Hallo Welt!
Werkzeuge
Strukturierte und Prozedurale Programmierung
Elementare Datentypen & Operatoren
Kontrollstrukturen
Zusammengesetzte Datentypen
Funktionen & Gültigkeitsbereiche
Zeiger & Speicherverwaltung
Kursinhalt
Objektorientierte Programmierung
Grundzüge der Objektorientierung
Klassen & Methoden
Vererbung & Polymorphie
Weiterführende Themen
Ausnahmebehandlung
Schablonen
Die C++ Klassenbibliothek
Literatur
I
Stroustrup, Bjarne:
Die C++ Programmiersprache (Addison-Wesley)
I
Viele andere Bücher über C++
I
http://www.cplusplus.com/
I
Josuttis, Nicolai:
Objektorientiertes Programmieren in C++ (Addison-Wesley)
I
McConnell, Steve:
Code Complete (Microsoft Press)
I
Maguire, Steve:
Writing Solid Code (Microsoft Press)
Lektion 1
Was heißt eigentlich
„Programmieren“?
Vom Problem zum Programm
Definition
Programmieren ist der gesamte Prozess von der Erfassung eines
Problems bis zur fertigen Umsetzung der Lösung auf einem
Computer und deren Wartung.
I
Problemstellung
I
Forderungsanalyse
I
Architektur
I
Implementierung
I
Systemtest
I
Wartung
Vom Problem zur Architektur
Was wollen die eigentlich von mir?
I
Konkrete Problemstellung
z.B. Ballistischer Wurf
I
Analyse der Anforderungen
z.B. Plot verschiedener Trajektorien
I
Festlegen der Architektur
z.B. Struktogramm des groben Programmablaufes
Fehlerquellen
I
I
I
I
Unklare Problemstellung
Falsch verstandene Anforderungen
Denkfehler in der Architektur
Von der Architektur zum Programm
Wie bringe ich mein Problem dem Computer bei?
I
Implementieren der Architektur
z.B. C++Quellcode schreiben
I
Test des Programmes
z.B. Vergleich der Plotdaten mit von Hand gerechneten
Werten
Fehlerquellen
I
I
I
Tippfehler, syntatktische Fehler
Werden vom Übersetzer gefunden
Semantische Fehler, Denkfehler im Algorithmus
Müssen vom Programmierer gefunden werden
Ziele dieses Kurses
I
Erfassen einfacher Problemstellungen
I
Erlernen der syntaktischen Struktur von C++
I
Erlernen der Sprachsemantik von C++
I
Erlernen des Umgangs mit den nötigen Werkzeugen
Am Ende dieses Kurses sollte jeder Teilnehmer befähigt
sein, einfache Programme zur Lösung einfacher Probleme
zu schreiben und sich die für komplexere Aufgaben
nötigen Kenntnisse eigenständig zu erarbeiten.
Lektion 2
Hallo Welt!
Das erste Programm in C++
1 #i n c l u d e <i o s t r e a m >
2
3 // Ü b e r s e t z e n m i t
g++ −Wa ll −c h e l l o . c c
4 // B i n d e n m i t
g++ −o h e l l o h e l l o . o
5
6 i n t main ( v o i d )
7 {
8
// Begrüßung a u s g e b e n
9
s t d : : c o u t << " H a l l o Welt ! " << s t d : : e n d l ;
10
11
// Programm b ee n d e n
12
return 0;
13 }
Programm 1: hello.cc
Übersetzen und Binden
I
Quelltext (*.cc) ist von Menschen lesbare
Form
I
Computer benötigt ausführbaren Code
(’executable’)
hello .cc
I
1.Schritt: Übersetzen jeder ’unit’ (*.cc) in
eine Objektdatei
g++ −c xyz.cc
erzeugt xyz.o
g++ −c
I
2. Schritt: Binden (’linken’) der *.o
Dateien zu einer ausführbaren Datei
hello .o
g++
g++ −o xyz yxz.o qrt.o ...
I
Ausführung in der Shell:
./xyz
I
Nach Änderungen im Quelltext:
Erneutes Übersetzen und Binden!
hello
Zurück zum Quelltext. . .
I
#include <iostream> lädt Ein-/Ausgabe
I
Eigentliche Funktionalität in main()
I
Kommentare beginnen mit //
I
Blöcke von Kommentaren mit
Start
Ausgabe
/∗ ... Text ... ∗/
I
std :: cout kann mit << alles „zugeworfen“
werden, was ausgegeben werden soll
I
Beenden
Ausgaben über std :: cout
Programm endet mit return 0;
Ende
Eine Frage des guten Stiles
I
Achten Sie auf die Struktur Ihrer Programme
I
I
I
Einrückungen deuten die logische Struktur an
Leerzeichen verbessern die Lesbarkeit
Mit Kommentaren erklären, was passiert!
(jedoch triviale Kommentare vermeiden!)
I
Bereits nach kurzer Zeit ist selbstgeschriebener Quellcode
ohne Kommentare nur noch schwer zu verstehen!
I
Bedenken Sie, dass möglicherweise andere Ihren Quellcode
lesen und warten müssen
I
Schauen Sie sich andere Programme an.
I
Gewöhnen Sie sich einen einheitlichen Stil an.
Lektion 3
Werkzeuge
Unix Programme / Kommandos
I
I
Verzeichnisse ( . : aktuelles, .. : übergeordnetes Verzeichnis)
pwd
aktuelles Verzeichnis anzeigen
ls [<dir>]
Dateien in einem Verzeichnis anzeigen
ls [<dir>] −al
Details anzeigen
cd <dir>
Verzeichnis wechseln
mkdir <dir>
Verzeichnis anlegen
rmdir <dir>
Verzeichnis löschen
rmdir −r <dir> Mit Unterverzeichnissen löschen (Vorsicht!)
Dateien
cp <src> <dest>
Datei kopieren
mv <src> <dest> Datei verschieben oder umbenennen
rm <file>
Datei löschen
cat < file >
Datei ausgeben
less < file >
Datei seitenweise ausgeben
Weitere Programme
I
Suchen
find <dir> −name <file>
grep <pattern> <file>
I
Hilfe
Beschreibung zu Kommando anzeigen
Hilfesystem anzeigen
Programmierung
g++ −c <file>
Datei übersetzen
g++ −Wall −c <file>
dabei alle Warnungen anzeigen
g++ −o <file> <obj> [...] Objektdateien binden
gdb <file>
Debugger aufrufen
man <cmd>
info
I
Datei suchen
Ausdruck in Datei suchen
Lektion 4
Elementare Datentypen &
Operatoren
Programme arbeiten mit Daten
I
Zum Rechnen werden Variablen und Operatoren benötigt
I
Variablen enthalten Daten des Programmes
z.B. Position des Geschosses auf Trajektorie
I
Variablen haben jeweils Typ und Namen
z.B. ganzzahlig, reell, . . .
I
Es gibt jeweils unterschiedliche Genauigkeiten (byte, int, . . . )
I
Operatoren (+, -, . . . ) manipulieren die in Variablen
gespeicherten Werte
Elementare Datentypen
I
Ganze Zahlen
short, int , long
unsigned short, unsigned int
I
’a’ , ’Z’, ’3’ , ’?’ , ...
Wahrheitswerte
bool
I
1., .5, 3.1415, 1.6022e−19
höhere Genauigkeit
Zeichen
char
I
positive Zahlen
Reele Zahlen
float , double
long double
I
−7, 42, 123456789
true, false
Leerer Datentyp
void
Definition von Variablen
I
Einfache Definition
int index ;
I
Mehrfache Definition
float a, b, c;
I
Definition mit Initialisierung
char c = ’a’ ;
unsigned long grosse_zahl = 1234567890ul;
float g = 9.81, v = 3.4;
I
Definition einer Konstanten, die später im Programm nicht
mehr verändert werden kann (const ’modifier’)
const float pi = 3.14159;
Namen von Variablen, Funktionen,...
I
Buchstaben: a .. z und A .. Z
Groß-/Kleinschreibung wird unterschieden
I
Unterstrich: _
I
Ziffern: 0 .. 9
I
Umlaute und Sonderzeichen sind nicht erlaubt
I
Sinnvolle Namen verwenden!
Nicht als erstes Zeichen!
Namen sollten für die jeweilige Funktion sprechen:
kontostand, start_value, . . .
Für Zähler und Indizes (int) reicht meist ein Buchstabe.
Oft: i, j, k, l, m, n. . .
Für reele Zahlen: a, x, y, . . .
I
Einheitliche Konventionen verwenden, auch für
Groß-/Kleinschreibung
Literale
I
‘Zeichenfolgen, die zur Darstellung der Werte von Basistypen
definiert bzw. zulässig sind’
z.B. true, false , ’A’, ’4’ , 15, 3.14159, . . .
I
Literale sind typisiert
true, false
−7, 23, 42
23u
1., 1e0, 23f
I
bool
int
unsigned
float
’a’ , ’Z’, ’3’
1234l
42ul
42 lf
Ganzzahlige Zahlenbasis
Dezimal:
−7, 23, 42
Hexadezimal (0x..):
0 xaffe , 0xD00F
Oktal (führende Null) 077, 0123
char
long
unsigned long
double
Vorsicht!
1
2
3
4
5
int
int
float
float
i
i
x
x
=
=
=
=
3.5;
011;
6 / 5;
6. / 5;
//
//
//
//
//
i
i
x
x
i s t 3!
i s t 9!
i s t 1!
i s t 1.20000004768372
( maschinenabhängig )
Fluchtsequenzen
I
Problem: ’ und " haben spezielle Bedeutung
Begrenzung von Zeichenliteralen bzw. -ketten
wie soll ’ selbst als Zeichen in einem Literal dargestellt
werden?
I
Linksseitiger Schrägstrich \ leitet Fluchtsequenz ein
’ \’ ’ ’ als Literal
’\"’ " als Literal
’\\’ \ als Literal
Fluchtsequenz auch für sogenannte nichtdruckende Zeichen
’\0’ ASCII Code 0 ’\n’ New Line
’\a’ Alarm
’\r ’ Carriage Return
’\b’ Backspace
’\t ’ Horizontal Tab
’\f ’ Form Feed
’\v’ Vertical Tab
I
Wertebereiche
I
Tatsächliche Größe nicht standardisiert
Lediglich Mindestgröße und aufsteigende Reihenfolge
I
Wertebereiche über #include <limits> zugänglich
1 #i n c l u d e <i o s t r e a m >
2 #i n c l u d e < l i m i t s >
3
4 i n t main ( )
5 {
6
s t d : : c o u t << " i n t : "
7
<< s t d : : n u m e r i c _ l i m i t s <i n t > : : min ( ) << " . . "
8
<< s t d : : n u m e r i c _ l i m i t s <i n t > : : max ( ) << s t d : : e n d l ;
9
10
return 0;
11 }
Programm 2: limits.cc
Arithmetische Operatoren
I
I
I
Einfache Operatoren
()
Klammerung von Ausdrücken
+ − Addition und Subtraktion, bzw. unäres Minus
∗ /
Multiplikation und Division
%
Modulo (Rest einer ganzzahligen Division)
Inkrement und Dekrement
++ −− präfix bzw. postfix Inkrement und Dekrement
b = a++; ist dasselbe wie b = a; a = a + 1;
b = ++a; ist dasselbe wie a = a + 1; b = a;
Zuweisung
=
einfache Zuweisung
+= ∗= ...
zusammengesetzte Zuweisung
b += a; ⇔ b = b + a; etc.
x ? A1 : A2; Ausdruck ergibt A1 oder A2, je nach Wahrheitswert
von x, z.B. y = (x>3) ? 5.0 : 7.0
Logische und Bitweise Operatoren
I
Vergleiche
Gleichheit, Ungleichheit
kleiner, größer
kleiner oder gleich, größer oder gleich
Logische Verknüpfungen
!
unäres logisches nicht
&& ||
und, oder
Bitweise Operatoren
~
unäres bitweises nicht
& | ^ bitweises und, oder, exklusiv-oder
<< >>
Bits nach links- bzw. rechts schieben
== !=
< >
<= >=
I
I
Typumwandlung
I
Implizite Typumwandlung
int a, b = 10;
float pi = 3.14159;
a = b ∗ pi ; // b nach float gewandelt, a ist 31
I
Zwischen elementaren Typen wird automatisch umgewandelt
I
Bei Rechnungen mit Elementen unterschiedlichen Typs
Wandlung aller Elemente in Typ mit größtem Wertebereich
Warnung bei impliziter Typumwandlung mit Verlust
I
Explizite Typumwandlung
int a, b = 10;
float pi = 3.14159;
a = b ∗ (int) pi ; // a ist 30 oder
a = b ∗ static_cast<int>( pi );
Rangfolge der Operatoren
::
. −> (..)
[..] postfix ++ −− typecast
präfix ++ −− unäre ~ ! − & ∗ sizeof new delete
∗ / %
+ −
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += −= ∗= /= %= <<= >>= &= ^= |=
throw ,
Lektion 5
Kontrollstrukturen
Programmverlauf und Anweisungen
I
Bisher streng monotoner Verlauf
A
I
Ermöglicht nur feste Anzahl von
Berechnungen
B
Weitere Anweisungen sind nötig, um
Programme flexibler zu gestalten
C
I
Anweisungen
I
Leere Anweisung
;
I
Einfache Anweisungen (was wir bisher kennen)
I
I
I
I
Definition von Variablen
Einfache Anweisungen mit Operatoren
Beenden von main() mit return (...);
Zusammengesetzte Anweisungen (Anweisungsblock)
{ Anweisung_A; Anweisung_B; ... }
I
Kontrollstrukturen (brechen Monotonie auf)
I
I
Bedingungen ermöglichen Alternativen
Schleifen ermöglichen Wiederholungen
Bedingte Anweisung
1
i f ( Ausdruck_x ) Anweisung_A ;
I
Ausführung von Anweisung_A, wenn
Ausdruck_x wahr
I
Ausführen von mehreren Anweisungen mit
{...}
I
WICHTIG: C++ interpretiert jeden Wert
ungleich 0 als „wahr“
x
A
Bedingte Anweisung mit Alternative
1
2
3
4
5
6
7
i f ( Ausdruck_x ) Anweisung_A ;
e l s e Anweisung_B ;
// o d e r :
i f ( Ausdruck_x )
Anweisung_A ;
else
Anweisung_B ;
I
Ausführung von Anweisung_A, wenn
Ausdruck_x wahr
I
Ausführung von Anweisung_B, wenn
Ausdruck_x falsch
B
x
A
Schachtelung bedingter Anweisungen
1
2
3
4
5
6
i f ( Ausdruck_x )
i f ( Ausdruck_y )
Anweisung_A ;
else
Anweisung_B ;
Anweisung_C ;
1
2
3
4
5
6
i f ( Ausdruck_x )
i f ( Ausdruck_y )
Anweisung_A ;
else
Anweisung_B ;
Anweisung_C ;
I
Gefahr von Mißverständnissen der Zugehörigkeit von else
I
Verwendung von geschweiften Klammern schafft Klarheit
Schachtelung mit Klammern
1
2
3
4
5
6
7
8
9
10
I
i f ( Ausdruck_x ) {
i f ( Ausdruck_y ) {
Anweisung_A1 ;
Anweisung_A2 ;
} else
Anweisung_B ;
} else {
Anweisung_C1 ;
Anweisung_C2 ;
}
Andere Arten der Einrückung möglich
Mehrfachverzweigung
1
2
3
4
5
6
7
8
9
10
11
12
s w i t c h ( Ausdruck_x ) {
case Konstante_1 :
Anweisung_A ; . . .
break ;
case Konstante_2 :
Anweisung_B ; . . .
break ;
default :
Anweisung_X ;
}
...
x
1
A
2
B
*
X
Mehrfachverzweigung
I
Auswertung von Ausdruck_x
I
Ergibt sich Ausdruck_x zu einer der Konstanten eines
case-Zweiges, dann Abarbeitung der darauf folgenden
Anweisungen bis zur nächsten break-Anweisung
I
Steht zwischen zwei case-Zweigen kein break, dann wird
Abarbeitung einfach fortgesetzt!
case-Zweige immer mit break beenden!
I
Wird kein passender case-Zweig gefunden, dann wird
default-Zweig gewählt falls vorhanden
I
default-Zweig ist optional
Einfache kopfgesteuerte Schleife
1
2
w h i l e ( Ausdruck_x )
Anweisung_A ;
I
So lange Ausdruck_x wahr ist, wird
Anweisung_A ausgeführt
I
Ausdruck_x wird evtl. nicht ausgeführt
I
Wenn Anweisung_A nicht dazu führt, dass
Ausdruck_x irgendwann falsch wird, führt
dies zu einer Endlosschleife
x
A
Einfache fußgesteuerte Schleife
1
2
do Anweisung_A
w h i l e ( Ausdruck_x ) ;
A
I
Anweisung_A wird mindestens einmal
ausgeführt
I
So lange Ausdruck_x wahr ist, wird
Anweisung_A wiederholt ausgeführt
x
Flexible Schleife
1
2
3
4
f o r ( Anweisung_I ;
Ausdruck_x ;
Anweisung_E )
Anweisung A ;
I
Zunächst wird Anweisung_I
(Initialisierung) ausgeführt
I
So lange Ausdruck_x wahr ist, werden
Anweisung_A und Anweisung_E
(Inkrement) ausgeführt
I
x
A
E
Beispiele für Flexible Schleifen
1
2
3
4
5
6
7
float x = 0.123;
f o r ( i n t i =0; i <100; i ++) x = x ∗ x − 1 ;
i n t h e x z a h l = 0 xa5 ;
f o r ( i n t b i t = 0 x80 ; b i t >0; b i t >>=1)
s t d : : c o u t << ( ( h e x z a h l & b i t ) ? ’ 1 ’ : ’ 0 ’ ) ;
s t d : : c o u t << " \n " ;
Frühzeitiges Fortsetzen
1
2
3
4
5
while ( Ausdruck x ) {
Anweisung A ;
i f ( Ausdruck y ) continue ;
Anweisung B ;
}
x
I
Anweisung A wird in jedem
A
y
Schleifendurchlauf ausgeführt
I
Falls Ausdruck y wahr ist, wird Schleife
frühzeitig fortgesetzt
I
Anweisung B wird nur ausgeführt, wenn
Ausdruck y falsch ist
I
Bei for -Schleife wird Inkrement in jedem
Fall ausgeführt
B
Frühzeitiges Beenden
1
2
3
4
5
while ( Ausdruck x ) {
Anweisung A ;
i f ( A u s d r u c k y ) break ;
Anweisung B ;
}
x
I
Anweisung A wird in jedem
A
y
Schleifendurchlauf ausgeführt
I
Falls Ausdruck y wahr ist wird, Schleife
frühzeitig beendet
I
Anweisung B wird nur ausgeführt, wenn
Ausdruck y falsch ist
B
Lektion 6
Zusammengesetzte Datentypen
Strukturen
1
2
3
4
struct vector
{
float x , y , z ;
};
I
Zusammenfassung benannter Elemente beliebigen Typs
I
Typdefinition vor main()
I
Definition und Initialisierung einer Variablen:
z.B. vector pos = { .0, 1.8, .0 };
I
Zugriff auf Strukturelemente über Elementzugriffsoperator .
z.B. pos.x = 1.;
Aufzählungen
1
2
3
4
5
6
7
enum l a n g u a g e
{
unknown ,
german ,
french ,
klingon
};
I
Eine Variable vom Typ ‘language ’kann (nur) die Werte
unknown, german,... annehmen.
I
Typdefinition vor main()
I
Definition & Initialisierung einer Variable dieses Typs:
z.B. language ConfLang = french;
I
Abfrage z.B. if (ConfLang == german) {...}
I
Explizite Zuordnung zu int möglich, z.B.
enum newtype {ok = 3, bad = 7};
Felder (Arrays)
I
Indizierte Zusammenfassung von Elementen gleichen Typs
I
Einfache Definition
z.B. int koeffizienten [5];
I
Definition mit Initialisierung
z.B. int koeffizienten [] = { 1, 2, 4, 8, 16 };
I
Mehrdimensionale Felder
z.B. float matrix [2][3] =
{ { 1., .0, .0 }, { .0, 1., 1.} };
I
Zugriff auf Feldelemente über Indexoperator [..]
z.B. koeffizienten [4] = 23;
I
Erstes Element hat Index 0
I
Ausblick; Nach int A [5]; ist A einfach ein Zeiger auf das erste
int im Feld. Die Größe des Feldes kann daraus nicht ersehen
werden
Primitive Zeichenketten (‘c-strings’)
I
Primitive Zeichenketten (‘strings ’) sind Zeichenfelder
I
Definition z.B.: char name[] = "Bond, James Bond";
I
Um das Ende der Zeichenkette erkennen zu können, wir
(automatisch) eine Null (0) angehängt.
Daher hat char str [] = "Tom"; die Länge 4!
I
Nur sehr eingeschränkte Manipulationsmöglichkeiten (kein
automatisches Kopieren,...)
I
C++ string -Klasse bietet mehr Flexibilität
Zugänglich über #include <string>
z.B. std :: string name = "Tom";
Mischungen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct point3 { float x , y , z ; };
const i n t N = 1 0 0 ;
p o i n t 3 v [N ] ;
const i n t NDAY = 3 6 5 ;
enum g e n d e r { male , f e m a l e } ;
struct Mitarbeiter
{
char
Name ;
// E r s t mal n u r e i n B u c h s t a b e
gender g ;
bool
I s O n H o l i d a y [NDAY ] ;
};
M i t a r b e i t e r XYZ ;
XYZ . g = f e m a l e ;
XYZ . I s O n H o l i d a y [ 1 3 ] = f a l s e ;
XYZ . Name = ’A ’ ;
i f (XYZ . g == male ) . . .
Noch ein Beispiel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
s t r u c t T_Land {
s t r i n g name ;
int
Vorwahl
};
// z . B . " F r a n k r e i c h "
// 33
s t r u c t T_Adresse {
T_Land l a n d ,
int
PLZ
};
// s . o .
// e i n e Z a h l
struct T_Mitarbeiter {
string
name ,
T_Adresse a d r
};
M i t a r b e i t e r Team1 [ 1 0 0 ] ; // P l a t z f ü r 100 M i t a r b e i t e r
Team1 [ 1 ] . name = "Tom" ;
Team1 [ 1 ] . a d r . l a n d . name = " E n g l a n d " ;
...
Typdefinition
I
Definition eines neuen Typs:
I
I
1
2
Implizit über struct oder enum
Explizit mit typedef
t y p e d e f unsigned i n t UINT ;
UINT k = 3 ;
Lektion 7
Funktionen &
Gültigkeitsbereiche
Strukturierte Programmierung
I
Bisher Programm komplett in main()
I
Beobachtungen:
I
I
I
Programmteile können mehrfach vorkommen
Programmteile können für andere Programme interessant sein
Bereits mittlere Programme werden schnell unübersichtlich
I
Abhilfe: Funktionen kapseln Programmteile in Block
I
Übergang zur prozeduralen Programmierung
Funktionsdefinition
I
1
2
3
4
5
I
Funtionen bestehen aus Kopf und Rumpf:
R ü c k g a b e t y p Name ( t y p name , . . . )
// Kopf
{
Etwas s i n n v o l l e s machen . . . ;
// Rumpf
return Ergebnis ;
// E r g e b n i s vom Typ R ü c k g a b e t
};
Kopf:
I
I
I
I
Rumpf:
I
I
I
I
I
Name
Rückgabetyp oder void für Funktionen ohne Rückgabe
Parameter: Jeweils Typ und Name, durch Komma getrennt
definiert Verhalten
Funktionen mit Ergebnistyp müssen return xxx; enthalten
Ohne Rückgabewert (void): optionales leeres return;
Parameter und im Rumpf definierte Variablen sind lokal.
Sie werden bei jedem Aufruf reserviert und initialisiert
Funktionsdefinitionen können nicht geschachtelt werden
Beispiel für Funktionsdefinition und Aufruf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
f l o a t A r i t h m e t i s c h e s M i t t e l ( f l o a t x1 , f l o a t x2 ) {
float mittel ;
m i t t e l = ( x1 + x2 ) / 2 ;
return m i t t e l ;
}
f l o a t G e o m e t r i s c h e s M i t t e l ( f l o a t x1 , f l o a t x2 ) {
r e t u r n s q r t ( x1 ∗ x2 ) ;
}
i n t main ( v o i d ) {
c o u t << A r i t h m e t i s c h e s M i t t e l ( 3 , 4 ) << e n d l ;
c o u t << G e o m e t r i s c h e s M i t t e l ( 3 , 4 ) << e n d l ;
}
Fakultät iterativ
1 #i n c l u d e <i o s t r e a m >
2
3 // F u n k t i o n z u r Be rechn ung d e r F a k u l t a e t
4 // n ! = n ∗ ( n−1) ∗ . . ∗ 2 ∗ 1
5
6 unsigned i n t f a c t o r i a l ( unsigned i n t n )
7 {
8
i f ( n == 0 ) r e t u r n 1 ; // 0 ! i s t 1
9
unsigned i n t f a c t = n ;
10
w h i l e ( n > 1 ) f a c t ∗= −−n ;
11
return f a c t ;
12 }
13
14 i n t main ( )
15 {
16
s t d : : c o u t << " 1 0 ! i s t " << f a c t o r i a l ( 10 )
17
<< s t d : : e n d l ;
18
return 0;
19 }
Funktionsaufruf
I
Aufruf durch Name und Funktionsoperator (..)
Übergabe von Argumenten in der Klammer
z.B. unsigned x = factorial ( 10 );
I
Funktionen können Funktionen aufrufen
Funktionen können auch sich selbst wieder aufrufen
Rekursion; Abbruchbedingung wichtig, sonst Endlosrekursion
I
Auch main() ist eine Funktion
Wird von Laufzeitumgebung aufgerufen
return in main() kehrt zur Laufzeitumgebung zurück
I
Funktion muß vor Aufruf bekannt sein
Entweder Deklaration oder Definition
I
Deklaration über Funktionskopf abgeschlossen durch ;
z.B. unsigned factorial ( unsigned n );
I
Definition muss dann später erfolgen!
Übergabe von Parametern nach main()
I
Die Parameter aus der linux - shell werden als array von char∗
übergeben
1 #i n c l u d e <i o s t r e a m >
2 u s i n g namespace s t d ;
3
4 i n t main ( i n t a r g c , char ∗ a r g v [ ] )
5 // i n t main ( i n t a r g c , c h a r ∗∗ a r g v )
// Ä q u i v a l e n t
6 {
7
c o u t << " A n z a h l p a r a m e t e r = " << a r g c << e n d l ;
8
f o r ( i n t i =0; i <a r g c ; i ++)
9
c o u t << " P a r a m e t e r " << i
10
<< " : " << a r g v [ i ] << e n d l ;
11 }
Mathematik
I
Die math-Bibliothek (#include <cmath>) stellt viele nützliche
Funktionen zur Verfügung
I
sqrt(), pow(base, exponent)
I
exp(), log()
I
sin(), cos(), . . . , sinh(), cosh(), . . .
I
fabs() (Betrag)
I
Konstanten wie M_PI (π),...
Gültigkeitsbereiche
Definition
Der Gültigkeitsbereich beschreibt, in welchem Kontext ein Name in
einem Programm verwendet werden kann.
I
Zwei Gültigkeitsbereiche: global und lokal
I
Funktionen sind global gültig
I
Variablen lokal zu Funktion
I
Jeder Block stellt abgeschlossenen Gültigkeitsbereich dar
z.B. for ( int i = 0; i < 5; i ++ ) {..}
i besitzt Gültigkeit nur innerhalb des for -Blocks
I
Zugriff auf globale Variable (unschön) mit :: name
Globale Variablen
I
Variablen können außerhalb von Funktion definiert werden
I
Zugriff aus allen Funktionen möglich
I
Gefahr von Inkonsistenzen hoch
I
Fehlersuche erschwert
I
Vermeiden!
Aufruf mit Argumentwert
I
Funktionen können beliebige Argumente erhalten
I
Funktionen können nur einen Wert zurückgeben
I
Funktionsparameter sind lokal
I
Argumente werden in Parameter kopiert
I
Änderungen an Parametern innerhalb einer aufgerufenen
Funktion wirken sich nicht auf Argumente innerhalb der
aufrufenden Funktion aus!
Aufruf mit Argumentreferenz
1
2
3
4
5
6
v o i d swap ( i n t & x , i n t & y )
{
int t = x ;
x = y;
y = t;
}
I
Referenzparameter werden mit & vor dem Namen definiert
I
Argumente werden an Funktion „durchgereicht“
I
Änderungen wirken auf Argumente zurück!
I
Konstanten oder Literale können nicht übergeben werden
Ausnahme: const-Referenzen, zur Vermeidung von Kopien
Namensräume
I
Strukturierung großer Programme über Namensräume
I
Namensraum wird über Schlüsselwort namespace und Name
definiert und leitet Block ein, der Elemente kapselt:
namespace Space1 { ... definitionen ...}
I
Elemente eines Namensraumes können über Namen und
Auflösungsoperator :: erreicht werden
I
Globaler Namensraum hat leeren Namen
I
C++ verwendet Namensraum std
Überladen von Funktionen und Operatoren
I
Funktionen mit unterschiedlicher Parameterliste können
gleiche Namen haben
I
Auswahl der aufzurufenden Funktion erfolgt anhand des Typs
der Argumente
Bei Mehrdeutigkeiten explizite Typwandlung nötig
I
Funktionen mit gleichem Namen sollten ähnliche
Funktionalität haben!
I
Operatoren sind in C++ Funktionen zugeordnet
I
Name einer Operatorfunktion beginnt mit Schlüsselwort
operator, gefolgt von Operatorsequenz
z.B. a = b + c; wird operator=( a, operator+( b, c ) );
I
Auch Operatoren können überladen werden
Operator überladen für selbstdefinierte Typen
1
2
3
4
5
6
7
8
9
10
11
12
vector
o p e r a t o r +( const v e c t o r & a ,
const v e c t o r & b )
{
vector c ;
c.x = a.x + b.x;
c.y = a.y + b.y;
c.z = a.z + b.z;
return c ;
}
Überladen des Ausgabeoperators
I
cout und cin sind im Namensraum std definierte Variablen
I
Ausgabe mit std :: cout über spezielle Überladungen von
operator<<() für Typ von std :: cout
I
Typ von cout ist std :: ostream
I
Eingabe mit std :: cin entsprechend, Typ ist std :: istream
Ausgabe für selbstdefinierte Typen
1
2
3
4
5
6
7
8
s t d : : o s t r e a m & operator <<(
s t d : : o s t r e a m & os , const v e c t o r & v )
{
o s << " ( " << v . x << " , "
<< v . y << " , " << v . z << " ) " ;
return os ;
}
Lektion 8
Zeiger & Speicherverwaltung
Was sind Zeiger?
I
Verweis auf andere Variable
I
"Die Adresse im Speicher"
I
Eine Referenz ist letzlich ein Zeiger, aber anders ’verpackt’
I
Die dynamische Erzeugung der Referenzierten ist möglich
I
Syntax ist näher an der Hardware
I
Flexibles Konzept der dynamischen Speicherverwaltung
Flexibilität bringt neue Fehlerquellen
Definition und Speicherverwaltung
I
Einfache Definition über Typ und Typmodifizierer ∗
z.B. int ∗ ptr ;
I
I
I
Leseweise: ptr ist ein Zeiger auf ein int (also ein int ∗)
Alternativ: ∗ ptr ist ein int
ptr zeigt zunächst irgendwo hin.
I
Verweis auf benannte Variable über Adressoperator &
z.B. int i ; ptr = & i;
I
Alternativ: dynamische Erzeugung von neuem Speicherplatz
(für ein int) mit new, z.B. ptr = new int;
I
Dynamisch erzeugte Variablen müssen nach Benutzung mit
delete freigegeben werden, z.B. delete ptr ;
I
Zeiger sind i.A. typgebunden, Ausnahme sind void-Zeiger
Verwendung von Zeigern
I
Zeiger müssen vor Zugriff initialisiert sein
I
Nach Freigabe darf kein Zugriff auf Zeiger mehr erfolgen
I
Zugriff auf Referenzierte über Dereferenzierungsoperator ∗
z.B. ∗ ptr = 42;
I
void-Zeiger können nicht dereferenziert werden
I
Zeiger können auch auf Zusammengesetzte Typen zeigen
z.B. vector v, ∗ pv = & v;
I
Zugriff auf Strukturelemente
z.B. (∗ pv).x = .5; pv−>y = 1.;
Merke
I
’&’ heißt: "die Adresse von (nachfolgendes Objekt)"
z.B.: ’& x’ ist die Adresse von x.
x ist hier ein beliebiges Objekt.
I
’*’ heißt: "Der Wert auf den (nachfolgendes Objekt) zeigt"
z.B.: ’* x’ ist der Wert, auf den x zeigt.
x muss ein Pointer sein!
Identität von Zeigern und Feldern
I
Dynamische Erzeugung von Feldern mit new[]
z.B. ptr = new int[16];
I
Zugriff auf Feldelemente
z.B. ∗ (ptr + 5) = 17; ptr [8] = 42;
I
Freigabe dynamisch erzeugter Felder mit delete []
z.B. delete [] ptr ;
I
Zeigerarithmetik von C++
I
I
I
Element bei Index n: ∗ (ptr + n) oder ptr [n]
Zeiger auf n. Element: ptr + n oder & ptr[n]
Zeiger sind syntaktisch und semantisch identisch zu Feldern
Lektion 9
Grundzüge der
Objektorientierung
Paradigmenwechsel
I
Bisher: Funktionen und Datensätze (weitgehend getrennt
behandelt)
I
Jetzt: Objekte, die ‘Dinge können’und wechselwirken
Schlagworte:
I
I
I
I
I
Abstrakte Datentypen
Kapselung
Vererbung
Polymorphie
Abstrakte Datentypen
I
Datentypen beschreiben zunächst nur, was Inhalt/Funktion
sein kann
Beispiel: Ein Objekt ’Form’ hat eine Farbe, es kann aber so
erst mal nicht gedruckt werden
Eine spezielle Form, z.B. ein Dreieck, läßt sich drucken.
I
Ein Vektor als abstrakter Typ ist Zusammenfassung von:
I
I
I
I
I
Vektorkomponenten
Addition und Subtraktion, evtl. äußeres Produkt
Multiplikation und Division mit Skalar
Skalarprodukt und Betrag
Die Größe des Vektors spielt hier erst mal keine Rolle!
Kapselung
I
Durch Kapselung werden Daten vor der Außenwelt verborgen
I
Die Details der Implementierung einer Funktionalität sind
unwichtig
I
Durch kontrollierte Zugegriffe auf die Daten passieren weniger
Fehler
Vererbung
I
Abstrakte Datentypen stehen oft in Beziehung zueinander
I
Beispiel:
I
I
I
I
Abstrakte Form hat Eigenschaften wie Linienfarbe, Füllung, . . .
Spezielle Formen haben auch einen Rand, . . . , z.B. Dreieck
oder Kreis
Das Zeichnen ist bei jeder Form anders, das Ändern der
Linienfarbe bei allen gleich
→ alle speziellen Formen erben Eigenschaften von Form
Ableiten der Gemeinsamkeiten von Basis durch Vererbung
I
I
Gemeinsame Programmteile nur einmal implementiert
Erweiterungen der Basis kommen allen Erben zugute
Polymorphie
I
Abgeleitete Objekte lassen sich auf Basis reduzieren
I
Kenntnis verallgemeinerter Basis oft ausreichend
I
Unterscheidung zwischen Spezialisierungen oft unnötig
I
Voraussetzung:
spezialisierte Objekte verhalten sich trotz Reduktion korrekt
I
Beispiel: ostream, fstream, sstream etc. verhalten sich sehr
ähnlich
Lektion 10
Klassen & Methoden
Klassen: Beispiel
1
2
3
4
5
6
7
8
9
10
11
12
13
class vector {
//
public :
vector ();
vector ( float , float , float ) ;
~vector ();
f l o a t Abs ( v o i d ) ;
v e c t o r & C r o s s ( const v e c t o r &, const v e c t o r &)
private :
float x , y , z ;
};
vector v (1.1 , 2.1 , 3.0);
f l o a t l a e n g e = v . Abs ( ) ;
Klassen
I
I
Klassen sind ein sprachliches Mittel zur objektorientierten
Programmierung
Sie erlauben die Definition abstrakter Datentypen
I
I
Klassenelemente beschreiben Inhalt
Methoden beschreiben Schnittstelle
I
Kapselung über Schutzattribut private
I
Veröffentlichung über Schutzattribut public
I
Vererbung über Basisklassen
I
Polymorphie über Zeiger
Definition
I
Definition beginnt mit Schlüsselwort class und Name
I
Klassenelemente und Methoden in Block danach
I
Klassenelemente analog zu Strukturelementen
I
Methoden sind gewöhnliche Funktionen
In Klasse deklarierte Methoden müssen außerhalb um
Klassenname erweitert definiert werden
I
Öffentliche Teile mit public: eingeleitet
I
Private Teile mit private : eingeleitet
Verwendung
I
Typdefinition heißt Klasse
I
Variable abstrakten Typs heißt Instanz
I
Zugriff analog zu Strukturen
I
Zusätzlich Aufruf von Methoden über . bzw. −>
I
Methoden erhalten implizites Argument this
Zeiger auf konkrete Instanz
Operatoren jeweils um ein Argument reduziert
Spezielle Methoden
I
Konstruktor wird bei Erzeugung einer Instanz aufgerufen
I
I
I
I
I
Definition mit Name der Klasse ohne Rückgabetyp
Konstruktor kann überladen werden
Implizit statische Methode
Aufruf durch Angabe von Argumenten bei Instanzierung
Destruktor wird bei Freigabe einer Instanz aufgerufen
I
I
Definition wie Konstruktor mit vorangestelltem ~
Erhält außer implizitem keine weiteren Argumente
I
Methoden ohne Wirkung auf Inhalt als const definieren
I
Statische Elemente (Schlüsselwort static ) sind nur einmal
vorhanden, nicht für jede Instanz!
I
Funktionen außerhalb des Klassenkontext erhalten als friend
Zugriff auf private Teile
Automatische Methoden
I
Standardkonstruktor ohne weitere Argumente
Implizit definiert, falls kein Konstruktor
I
Kopierkonstruktor mit Referenzargument auf Klassentyp
I
Typumwandlung durch Konstruktoren mit einem Argument
Verhinderung durch explicit
I
Zuweisungsoperator mit Referenzargument auf Klassentyp
Implizit definiert, Wertekopie
Lektion 11
Vererbung & Polymorphie
Vererbung
I
Klassen können von anderen abgeleitet werden
I
Elemente und Methoden werden dabei vererbt
I
Private Teile der Basisklasse nicht zugänglich
Schutzattribut protected erlaubt Zugriff bei Vererbung
I
Schutzattribute der Basisklasse können modifiziert werden
I
Teile der Basisklasse können überdeckt werden
Verwendung
I
Name der Basisklasse wird bei Definition nach dem
Klassennamen durch : getrennt angegeben
I
Vererbungsattribut steht vor Name der Basisklasse
I
Konstruktor muß Konstruktor der Basisklasse aufrufen
Aufruf folgt bei Definition Konstruktorkopf durch : getrennt
Ggf. impliziter Aufruf des Standardkonstruktors
I
Methoden können auf überdeckte Teile zugreifen
Zugriff erfolgt über Name der Basisklasse und ::
Polymorphie
I
Zeiger auf Basisklasse kann auf abgeleitete Klasse zeigen
I
Nur Aufruf von Methoden der Basisklasse möglich
I
Virtuelle Methoden ermöglichen weiterreichen von Aufrufen
Defintion über Schlüsselwort virtual vor Rückgabetyp
I
Frage: Wie soll sich reduzierte Instanz verhalten?
I
I
Wie Instanz der Basisklasse → Nicht-virtuelle Überdeckung
Wie Instanz der abgeleiteten Klasse → Virtuelle Überdeckung
I
Destruktoren sollten Virtuell definiert werden
I
Rückumwandlung mit dynamic_cast<..>(..)
Abstrakte Basisklassen
I
Basisklasse oft nicht zur Instanzierung konzipiert
I
Einzelne Methoden der Basisklasse oft leer
I
Leere Methoden können rein-virtuell definiert werden
Methodenkopf gefolgt von Nullzuweisung
I
Basisklassen mit rein-virtuellen Methoden heißen abstrakt
I
Abstrakte Basisklassen können nicht instanziert werden
Lektion 12
Ausnahmebehandlung
Klassische Fehlerbehandlung
I
Zunächst: Betrachtung von Laufzeitfehlern
z.B. Datei nicht gefunden, nicht genug Speicher, . . .
I
Laufzeitfehler müssen behandelt werden
z.B. Fehlermeldung ausgeben, Aktion wiederholen, . . .
I
Klassisches Konzept: Behandlung vor Ort
I
I
I
I
Aktion ausführen
Auf Fehler prüfen
Fehler ggf. behandeln
Nachteil: Funktionalität und Fehlerbehandlung verwoben
Unübersichtlich, Umständlich
Ausnahmen (‘Exceptions ’)
I
Ausnahmen ermöglichen nachgelagerte Fehlerbehandlung
I
I
I
Aktionen kann Ausnahme auslösen
Ausnahme unterbricht Programmfluss
Kann an anderer Stelle aufgefangen werden
I
Fehlerbehandlung z.B. am Ende eines Abschnitts
I
Vorteil: Funktionaler Programmfluss nicht unterbrochen
I
Nachteil: erhöhter Aufwand durch die Laufzeitumgebung
Ausnahmen in C++
I
Ausnahme kann mit throw ausgelöst werden
I
Auslösung immer mit Objekt zur Repräsentation verbunden
Repräsentant kann bel. Instanz sein, auch elementaren Typs
I
Bei Auslösung wird aktueller Block sofort verlassen
I
Auffangen in catch-Block nur nach try -Block möglich
I
try -Block können mehrere catch-Blöcke folgen
I
catch-Block fängt Repräsentanten bestimmten Typs auf
Nur erster passender catch-Block wird verwendet
I
catch ( ... ) fängt alle Repräsentanten auf
Standardausnahmeklassen
I
C++ stellt Standardausnahmeklassen bereit
Einbinden mit #include <exceptions>
I
Basisklasse aller Repräsentanten ist std :: exception
I
Laufzeitfehler sind von std :: runtime_error abgeleitet
I
Logikfehler sind von std :: logic_error abgeleitet
I
Diverse spezifische Klassen
z.B. std :: bad_alloc, wird ausgelöst, wenn new fehlschlägt
Struktur
1
2
3
4
5
6
7
8
9
10
11
12
try {
i n t ∗ f e l d = new i n t [ a n z a h l ] ;
...
}
catch ( const s t d : : b a d _ a l l o c & e ) {
...
}
catch ( const s t d : : e x c e p t i o n & e ) {
...
}
Lektion 13
Schablonen
Generische Programmierung
I
Funktionen oft für verschiedene Typkombinationen benötigt.
Beispiel:
<max(int a, int b) ...> funktioniert für float nicht richtig
(warum?) und muss daher für float neu definiert werden
I
C++ erlaubt Überladen von Funktionen
I
Nachteil: oft identischer Quelltext
Gefahr von Tippfehlern, Fehler müssen an mehreren Stellen
behoben werden
Lösungsansatz: Generische Programmierung
I
I
I
Funktion wird für generischen Typ nur einmal implementiert
Übersetzer generiert Implementierung für konkreten Typ
Schablonen (Templates)
I
Schablonen ermöglichen in C++ generische Programmierung
I
Schablone wird mit template eingeleitet
I
Generischer Typ in spitzen Klammern mit <typename ...>
I
In Definition wird generischer Typ verwendet
Schablonen erlaubt für Funktionen und Klassen
I
Bei Verwendung konkreter Typ in spitzen Klammern
1
2
3
4
template <typename T> T a b s ( T x )
{ i f ( x < 0 ) r e t u r n −x ; r e t u r n x ; }
...
i n t a = abs<i n t >( b ) ;
Lektion 14
Die C++ Klassenbibliothek
Überblick
I
Die ‘standard template library’stellt viele nützliche Objekte zur
Verfügung!
I
Namensraum std
I
Ein-/Ausgabe
I
Zeichenketten
I
Kontainerklassen
I
Algorithmen
I
Verwendung von stl Objekten kann viel Arbeit sparen und
macht das Programm kurz und sicher!
Ein-/Ausgabe
I
Ist ein spezieller stream
I
#include <iostream>
I
Objekte
cin , cout
cerr
clog
endl
I
Standardeingabe, -ausgabe
Fehlerausgaben
Verlaufskontrolle
Zeilenende
Operatoren
<<, >>
Ausgabe an bzw. Eingabe von Stream
Dateizugriffe
I
Ein weiterer stream
I
#include <fstream>
I
Klassen
ifstream , ofstream
fstream
I
Methoden
open(), close ()
read (), write ()
tellg (), seekg (),
tellp (), seekp()
I
Lese- bzw. Schreibzugriff
Wahlfreien Zugriff
Öffnen und Schließen
Zugriff auf Binärdateien
Abfragen/Setzen des
Lese-/Schreibzeigers
Beispiel: ofstream f ; f .open(" file . txt" ); f << ’A’; f. close ();
Zeichenketten
I
Header #include <string>
I
Konstruktoren
string ( const char ∗ )
string ( const string & )
I
Operatoren
=, +, +=
[..]
I
Kopie von normalem C String
Kopierkonstruktor
Kopieren, Zusammenführen und Anhängen
Zugriff auf einzelne Zeichen (besser at() )
Methoden
find (), rfind (), substr ()
insert (), erase (), replace ()
length (), size ()
c_str()
Suchen und extrahieren von
Substrings
Operationen auf Substrings
Länge bzw. Größe abfragen
zu C String umwandeln
Container
I
I
Container-Objekte enthalten andere Objekte
Oft benötigt, mit verschiedenen Inhaltstypen
→ Alle Container nutzen Schablonen (templates)
Klassen für verschiedene Zugriffsmuster
I
I
I
I
I
I
vector
Wahlfreier Zugriff ([]), schnelles Anfügen am Ende
(push_back), langsames Einfügen am Anfang (push_front),
Integer als Indizes
list
Schnelles Einfügen überall, nur serieller Zugriff (kein [])
deque (double-ended queue)
Wahlfreier Zugriff, schnelles Einfügen am Anfang und Ende,
Integer als Indizes
stack: Einfügen/entnehmen nur am Ende
map
Wahlfreier Zugriff, beliebige Typen als Indizes (Schlüssel)
Oft auch Dictionary genannt
set: Menge. Jeder Wert kann nur einmal vorkommen.
Container
I
Methoden
insert ()
erase ()
clear ()
size ()
capacity ()
empty()
push_back()
pop_back()
push_front()
pop_front()
I
Einfügen eines Elements
Löschen eines Elements
Löschen aller Elemente
Anzahl der gespeicherten Elemente abfragen
Anzahl der Elemente, die gespeichert werden
können bevor Speicher allokiert werden muss
Prüfen, ob Kontainer leer ist
Anhängen ans Ende
Entfernen vom Ende
Einfügen am Anfang (außer vector )
Entfernen vom Anfang (außer vector )
Operatoren
[..]
Wahlfreier Zugriff ( vector , deque)
Iteratoren
I
Bei allgemeinen Container (z.B. set) ist Zugriff auf die
Elemente mit Indices nicht sinnvoll.
I
Iteratoren sind ein allgemeines Konzept für Zugriff auf
Elemente in containern
I
Idee: ’Erstes Element’, ’Nächstes Element’ etc.
I
Je nach Kontainer Vorwärts- und Rückwärtsiteratoren
( iterator , reverse_iterator )
begin (), end()
Iterator auf Anfang, hinter Ende
rbegin (), rend() Iterator auf Ende, vor Anfang
Operatoren
++ Auf nächstes Element setzen
∗
Dereferenzierung
I
Herunterladen