Der C++ Crashkurs v1.0 Daniel Stöckel, M. Sc. October 14, 2013 1 Grundlegendes 1.1 Das erste Programm Wir beginnen mit einem einfachen Hello world Programm. Listing 1: hello world.cpp // Einbinden der i o s t r e a m s B i b l i o t h e k #include <iostream > // Die main Funktion g i b t immer einen g a n z z a h l i g e n Wert // zurueck. Die Parameter s i n d d i e Anzahl an Kommando // zeilenarumenten, sowie d i e Kommandozeilenargumente // a l s String Array argv. int main ( int argc, char argv [ ] ) // Ausgabe e r f o l g t ueber cout. // Diese b e f i n d e t s i c h im namespace s t d. Daher s t d : : cout. // s t d : : e n d l beendet d i e Z e i l e und f l u s h t den stream. std : : cout << H e l l o world! << std : : endl ; return 0 ; Kompilieren und Ausführen des Programmes über eine Shell: g++ -o hello_world hello_world.cpp./hello_world 1.2 Die erste Klasse C++ ist eine objektorientierte Sprache. Hier lernen wir wie man eine Klasse erstellt und verwendet. Eine Klasse sollte immer in einer eigenen Headerdatei (.h) deklariert und eigenen Sourcedatei (.cpp) definiert werden. 1
Listing 2: simple class.h // I n c l u d e g u a r d s verhindern d i e M e h r f a c h d e k l a r a t i o n // von Typen und Variablen #ifndef SIMPLE CLASS H #define SIMPLE CLASS H // Einbinden der s t d : : s t r i n g Klasse #include <s t r i n g > class SimpleClass public : // Konstruktoren SimpleClass ( ) ; SimpleClass ( const std : : s t r i n g& name ) ; // D e s t r u k t o r SimpleClass ( ) ; // Getter und S e t t e r void setnumber ( int number ) ; int getnumber ( ) const ; void setname ( const std : : s t r i n g& name ) ; const std : : s t r i n g& getname ( ) const ; // Ausgabe void printnumber ( ) ; void printname ( ) ; private : // Membervariablen int number ; std : : s t r i n g name ; //WICHTIG: das Semikolon am Ende der K l a s s e n d e k l a r a t i o n ; #endif //SIMPLE CLASS H Listing 3: simple class.cpp // Einbinden der D e k l a r a t i o n der Klasse // I n c l u d e s mit Anfuehrungszeichen suchen z u e r s t nach // entsprechenden Dateien im g l e i c h e n V e r z e i c h n i s #include s i m p l e c l a s s. h // I n c l u d e s s o l l t e n nach M o e g l i c h k e i t in der S o urcedateien // eingebunden werden ( s c h n e l l e r e Compilierung ) #include <iostream > 2
// D e f a u l t k o n s t r u k t o r SimpleClass : : SimpleClass ( ) // I n i t i a l i s i e r u n g m i t t e l s I n i t i a l i s i e r u n g s l i s t e. // Dies i s t w e i t a u s s c h n e l l e r a l s das I n i t i a l i s i e r e n in der // Methode und d i e e i n z i g e M o e g l i c h k e i t Memberklassen e f f i z i e n t // zu i n i t i a l i s i e r e n : number ( 0 ), name ( Default ) //Der Ausgabestream cout b e f i n d e t s i c h im namespace s t d // d i e s e r s o l l t e zumindest in Headerdateien immer angegeben // werden std : : cout << Default c o n s t r u c t o r o f SimpleClass << std : : endl ; // D e t a i l l i e r t e r Konstruktor SimpleClass : : SimpleClass ( const std : : s t r i n g& name) // I n i t i a l i s i e r u n g s l i s t e : number ( 0 ), name (name) //Auf Member kann b e r e i t s h i e r z u g e g r i f f e n werden std : : cout << D e t a i l e d c o n s t r u c t o r o f SimpleClass << std : : endl << Set name to : << name << std : : endl ; //Der D e s t r u k t o r der Klasse. Zur Z e i t i s t d i e s e r noch unnoetig //und koennte auch w e g g e l a s s e n werden. SimpleClass : : SimpleClass ( ) std : : cout << SimpleClass d e s t r u c t o r. Does nothing f o r now << std : : endl ; // Getter und S e t t e r, n i c h t s b esonderes void SimpleClass : : setnumber ( int number ) number = number ; int SimpleClass : : getnumber ( ) const return number ; void SimpleClass : : setname ( const std : : s t r i n g& name) name = name ; 3
const std : : s t r i n g& SimpleClass : : getname ( ) const return name ; void SimpleClass : : printnumber ( ) std : : cout << Number : << number << std : : endl ; void SimpleClass : : printname ( ) std : : cout << Name : << name << std : : endl ; #include s i m p l e c l a s s. h Listing 4: main.cpp int main ( int argc, char argv [ ] ) // Erzeuge ein Objekt der Klasse SimpleClass SimpleClass a ; a. printnumber ( ) ; a. printname ( ) ; a. setnumber ( 1 ) ; a. setname ( Blaaaaaaaaaaa ) ; a. printnumber ( ) ; a. printname ( ) ; SimpleClass b ( Blubb ) ; b. printnumber ( ) ; b. printname ( ) ; b. setnumber ( a. getnumber ( ) ) ; b. printnumber ( ) ; // Aufruf von SimpleClass ( const s t d : : s t r i n g& name) SimpleClass c = std : : s t r i n g ( Huhu ) ; c. printname ( ) ; return 0 ; //WICHTIG: Objekte werden h i e r z e r s t o e r t Kompilieren und Ausführen des Programmes: 4
g++ -c simple_class.cpp g++ -c main.cpp g++ -o simple_class main.o simple_class.o./simple_class Wurde kein Konstruktor explizit angelegt, erzeugt C++ einen Defaultkonstruktor. Existiert ein detaillierter Konstruktor, und ein Defaultkonstruktor ist erwünscht, muss dieser selbst angelegt oder kann mittels default angelegt werden. Ist keine Defaultimplementierung erwünscht, kann diese mit delete unterdrückt werden. Hinweis: default und delete sind nur im aktuellen C++ standard C++ 11 verfügbar, welcher explizit aktiviert werden muss. Dies geschieht durch das hinzufügen des Kommandzeilenparameters -std=c++0x. Dies funktioniert z.b. unter MacOS noch nicht. Listing 5: example.cpp class SimpleClass public : // E r s t e l l e einen automatischen D e f a u l t k o n s t r u k t o r SimpleClass ( ) = default ; // E x p l i z i t e r d e t a i l l i e r t e r Konstruktor SimpleClass ( int x ) ; ; // G e l o e s c h t e r Copy Konstruktor SimpleClass ( const SimpleClass& a ) = delete ; 1.3 Makefiles Umfangreiche programme per Hand zu bauen ist mühsehlig. Makefiles bieten eine Skriptsprache um das Kompilieren vollständig zu automatisieren. Das vorherig Beispiel kann beispielsweise mittels dieser einfachen Makefile erzeugt werden: Listing 6: Makefile a l l : g++ c s i m p l e c l a s s. cpp g++ c main. cpp g++ o s i m p l e c l a s s main. o s i m p l e c l a s s. o Make ermöglicht auch das Verwenden von Variablen und Pattern Matching um flexiblerer Makefiles zu ermöglichen: Listing 7: Makefile #Anlegen e i n e r Variable, d i e a l l e O b j e c t d a t e i e n e n t h a e l t OBJECTS=main. o s i m p l e c l a s s. o 5
#Der verwendete Compiler und Compileroptionen : # s t d=c++0x a k t i v i e r t d i e U n t e r s t u e t z u n g f u e r den a k t u e l l e n # C++ 11 Standard # # Wall a k t i v i e r t a l l e Warnungen des Compilers. Code s o l l t e # immer ohne Warnungen k o m p i l i e r e n. # # p e d a n t i c z w i n g t den Compiler Code s t r e n g e r nach Standard # k o n f o r m i t a e t zu pruefen. CXX=g++ CXXFLAGS= std=c++0x Wall pedantic #Die Z i e l e a l l und c l e a n s i n d k e i n e Dateien. #T e i l e d i e s make mit..phony: a l l c l e a n #Das ( Standard ) z i e l a l l haengt von s i m p l e c l a s s ab a l l : s i m p l e c l a s s #Regel zum bauen von O b j e c t d a t e i e n. Wichtig : #Die z w e i t e Z e i l e muss mit einem t a b e i n g e r u e c k t s e i n! #Die g e n e r e l l e Syntax i s t : #Target : Dependency1 Dependency2... # Command1 # Command2 #... #Erzeuge e i n e Datei mit Endung. o #Diese i s t abhaengig von den Dateien mit gleichem Namen und #den Dateiendungen. cpp und. h #Diese Regel g i l t a l s o f u e r a l l e Z i e l e d i e auf %.o enden und f u e r #d i e d i e entsprechenden Source und Headerdateien e x i s t i e r e n #Die automatische V a r a i b l e $< wird auf den e r s t e n Wert der #A b h a e n g i g k e i t s l i s t e g e s e t z t ( h i e r : %.cpp ) %.o : %.cpp %.h $CXX $CXXFLAGS c $< #Fuer main. o e x i s t i e r t k e i n e main. h. #Wir b e n o e t i g e n a l s o e i n e g e s o n d e r t e Regel %.o : %.cpp $CXX $CXXFLAGS c $< #Um s i m p l e c l a s s zu bauen muessen d i e O b j e c t d a t e i e n gebaut s e i n #Die automatische V a r i a b l e $@ wird auf den Wert des Z i e l s #( h i e r : s i m p l e c l a s s ) g e s e t z t s i m p l e c l a s s : $OBJECTS $CXX $CXXFLAGS $OBJECTS o $@ #Loesche a l l e von make g e n e r i e r t e n Dateien 6
c l e a n : rm f $OBJECTS s i m p l e c l a s s Wir können das Klassenbeispiel nun mit einem einfachen Aufruf von make erstellen. 1.4 Pointer Zeiger sind ein wichtiger Bestandteil von C++. Sie werden benötigt um dynamischen Speicher anzufordern und verhalten sich ähnlich wie Java Referenzen. #include <iostream > Listing 8: main.cpp class Dummy public : Dummy( ) std : : cout << Default Konstruktor << std : : endl ; Dummy( ) std : : cout << Destruktor << std : : endl ; ; void p r i n t B l a ( ) std : : cout << Bla << std : : endl ; int main ( int argc, char argv [ ] ) // D e k l a r a t i o n e i n e s Z e i g e r s auf einen I n t e g e r int p o i n t e r ; // Dies e r z e u g t a b s i c h t l i c h e i n e Compilerwarnung std : : cout <<! Anfangswert! von p o i n t e r : << std : : hex << p o i n t e r << std : : endl ; int i = 5 ; // S e t z e p o i n t e r auf d i e Addresse von i : p o i n t e r = &i ; std : : cout << Wert von p o i n t e r : << std : : hex << p o i n t e r << std : : endl ; // Gib den Wert der S p e i c h e r z e l l e auf d i e p o i n t e r z e i g t aus : std : : cout << p o i n t e r z e i g t auf : << std : : dec << p o i n t e r << std : : endl ; 7
// Gib i aus : std : : cout << Wert von i : << i << std : : endl ; // S e t z e d i e S p e i c h e r z e l l e von p o i n t e r auf 10; p o i n t e r = 1 0 ; // Gib i aus : std : : cout << Wert von i : << i << std : : endl ; // Fordere neuen S p e i c h e r f u e r einen I n t e g e r an : p o i n t e r = new int ; std : : cout << Wert von p o i n t e r : << std : : hex << p o i n t e r << std : : endl ; std : : cout << p o i n t e r z e i g t auf : << std : : dec << p o i n t e r << std : : endl ; p o i n t e r = 1234; std : : cout << p o i n t e r z e i g t auf : << std : : dec << p o i n t e r << std : : endl ; // Gib den S p e i c h e r wieder f r e i : delete p o i n t e r ; // Fordere einen S p e i c h e r b e r e i c h an : p o i n t e r = new int [ 1 0 ] ; // I t e r i e r e ueber den S p e i c h e r for ( int i = 0 ; i < 1 0 ; ++i ) std : : cout << Wert an S p e i c h e r z e l l e p o i n t e r + << i << : << p o i n t e r [ i ] << std : : endl ; p o i n t e r [ i ] = i ; std : : cout << Wert an S p e i c h e r z e l l e p o i n t e r + << i << : << p o i n t e r [ i ] << std : : endl ; // I t e r i e r e n Runde zwei int p = p o i n t e r ; for ( int i = 0 ; i < 1 0 ; ++i, ++p ) std : : cout << Wert an S p e i c h e r z e l l e p o i n t e r + << i << : << p o i n t e r [ i ] << std : : endl ; p = 2 i ; std : : cout << Wert an S p e i c h e r z e l l e p o i n t e r + << i << : << p o i n t e r [ i ] << std : : endl ; // I t e r i e r e n Runde d r e i p = p o i n t e r ; int p end = p o i n t e r + 1 0 ; 8
while ( p!= p end ) std : : cout << Wert an S p e i c h e r z e l l e p o i n t e r + << p << : << p << std : : endl ; p++ = 1 0 0 ; std : : cout << Wert an S p e i c h e r z e l l e p o i n t e r + << (p 1) << : << (p 1) << std : : endl ; // S p e i c h e r b e r e i c h f r e i g e b e n. Die e c k i g e n Klammern s i n d WICHTIG! delete [ ] p o i n t e r ; // Klassen koennen auch per new a n g e l e g t werden. Wichtig : // Bei D e f a u l t Konstruktoren d a r f k e i n e Klammer verwendet werden. // r i c h t i g : new Dummy; // f a l s c h : new Dummy( ) Dummy a = new Dummy; // Aufruf von Methoden oder Z u g r i f f auf Felder ( a ). p r i n t B l a ( ) ; a >p r i n t B l a ( ) ; // Freigeben des S p e i c h e r s ; Der D e s t r u k t o r der Klasse // wird a u f g e r u f e n. delete a ; std : : cout << A l l e Destruktoren wurden a u f g e r u f e n. << std : : endl ; return 0 ; ; 1.5 Referenzen Ein weiteres wichtiges Sprachmittel von C++ sind Referenzen als Alternative zu Pointern. Diese verhalten sich gänzlich anders wie Java Referenzen und sollten nicht mit ihnen verwechselt werden. Wenn möglich ist ihre Verwendung der von Pointern vorzuziehen. Am einfachsten kann man Referenzen als alternative Namen für eine Variable erklären. #include <iostream > Listing 9: main.cpp class Dummy public : Dummy( int v a l ) : a ( v a l ) 9
int a ; //Das Verwenden von c o nst Referenzen e r l a u b t das // Uebergeben von Objekten d i e nur g e l e s e n werden // koennen void s e t V a l u e s ( const Dummy& bla ) a = bla. a ; ; // Dieses l e i c h t schwachsinnige B e i s p i e l wird in main // naeher e r l a e u t e r t. void s e t V a l u e s ( const int& blubb ) a = blubb ; int main ( int argc, char argv [ ] ) Dummy a ( 1 2 1 3 2 1 3 ) ; // Referenzen muesen b e i Erzeugung i n i t i a l i s i e r t werden. Dummy& a r e f = a ; // Referenzen werden e x a k t so verwendet wie r e g u l a e r e Variablen. Nur d i e // I n i t i a l i s i e r u n g i s t u n t e r s c h i e d l i c h. std : : cout << a r e f. a << std : : endl ; const Dummy b ( 9 9 9 9 9 ) ; // Const Referenzen ( s i e h e s e t V a l u e s ) koennen auch an Objekte // gebunden werden, denen k e i n Wert zugewiesen werden kann a. s e t V a l u e s ( b ) ; // I n s b e s o n d e r s g i l t d i e s auch f u e r L i t e r a l e : a. s e t V a l u e s ( 1 2 3 4 5 6 ) ; return 0 ; 1.6 Const Correctness Das Schlüsselwort const hat verschiedene Bedeutungsnuancen in C++. Im Allgemeinen jedoch bringt es zum Ausdruck, dass ein Wert, eine Funktion oder Objekt nur zum Auslesen verwendet werden darf. Mit const markierte Methoden dürfen auch auf const Objekten aufgerufen werden. Dies erlaubt dem Compiler stärkere Annahmen während des Optimierungsschrittes zu machen. 10
#include <iostream > #include <vector > Listing 10: main.cpp class Dummy public : Dummy( ) : data ( 1 0 ) for ( int i = 0 ; i < 1 0 ; ++i ) data [ i ] = i ; // Diese Methode wird auf veraenderbaren Objekten a u f g e r u f e n int& operator [ ] ( s i z e t s ) std : : cout << In o p e r a t o r [ ] << std : : endl ; return data [ s ] ; // Diese auf Konstanten Objekten const int& operator [ ] ( s i z e t s ) const std : : cout << In const o p e r a t o r [ ] << std : : endl ; return data [ s ] ; protected : std : : vector <int> data ; ; int main ( int argc, char argv [ ] ) Dummy a ; const Dummy b ; a [ 3 ] = 4 ; std : : cout << b [ 3 ] << std : : endl ; return 0 ; 11
1.7 Strings Aufgrund des C Erbes von C++ gibt es zwei Möglichkeiten Strings in C++ darzustellen. Die einfachste Methode ist das Verwenden der std::string Klasse. Alternativ werden auch nullterminierte Character Arrays als Strings verwendet. Das Konvertieren von Strings in andere Datentypen jedoch ist ein leidiges Thema. Hier bietet C++ zwar Lösungen an, diese sind jedoch langsam und umständlich zu verwenden, sodass oft auf funktionen aus der C Bibliothek zurückgegriffen wird. #include <c s t d i o > // Fuer p r i n t f #include <c s t r i n g > // Fuer strcmp #include <s t r i n g > #include <iostream > #include <sstream> #include <cerrno > Listing 11: main.cpp int main ( int argc, char argv [ ] ) // Dies l e g t einen C S t r i n g mit Laenge 5 an const char example = Hallo ; // A e q u i v a l e n t i s t f o l g e n d e r Code : char example2 [ ] = H, a, l, l, o, \0 ; // Die C Funktion strcmp g i b t 0 zurueck wenn b e i d e S t r i n g s // i d e n t i s c h s i n d i f ( strcmp ( example, example2 ) == 0) std : : cout << Die beiden S t r i n g s s i n d g l e i c h! << std : : endl ; //Der argv Parameter i s t e b e n f a l l s ein Vektor aus S t r i n g s : for ( int i = 0 ; i < argc ; ++i ) std : : cout << argv [ i ] << std : : endl ; // A l t e r n a t i v g i b t es d i e Klasse s t d : : s t r i n g // Diese s o l l t e v o r z u g s w e i s e verwendet werden. std : : s t r i n g s t r = Hallo ; //Auf d i e Zeichen kann m i t t e l s [ ] z u g e g r i f f e n werden : std : : cout << s t r [ 0 ] << std : : endl ; //Manchmal i s t es n o e t i g einen s t r i n g in einen C S t r i n g //umwandeln zu koennen. Hier ein B e i s p i e l, dass // d i e standard C Ausgabefunktion verwendet. p r i n t f ( %s \n, s t r. c s t r ( ) ) ; 12
//Beim E i n l e s e n von Dateien oder Benutzereingaben i s t es o f t n o e t i g // einen s t r i n g in e i n e Zahl k o n v e r t i e r e n std : : s t r i n g bla = 4.543 ; f l o a t number ; std : : s t r i n g s t r e a m strm ; strm << bla ; strm >> number ; std : : cout << number << std : : endl ; // Eine andere, o f t angenehmere Methode i s t es d i e entsprechenden //C f u n k t i o n e n zu verwenden. Diese i s t So zu v e r s t e h e n : // s t r t o f > k o n v e r t i e r e STRing to F l o a t // b l a. c s t r ( ) i s t d i e Eingabe //Der z w e i t e Parameter g i b t an b i s wohin im s t r i n g g e l e s e n wurde. // mit NULL wird d i e s e Angabe i g n o r i e r t. number = 0. 0 f ; number = std : : s t r t o f ( bla. c s t r ( ), NULL) ; i f ( errno!= 0) std : : c e r r << Error c o n v e r t i n g s t r i n g to f l o a t << std : : endl ; return 0 ; 1.8 Container Die C++ Standardbibliothek bietet Datentyp um Sammlungen von Objekten zu speichern. Zum Schreiben von effizienten Algorithmen ist es wichtig den passenden Container zu verwenden, da sie sich stark im Laufzeitverhalten unterscheiden. #include <iostream > #include <s t r i n g > Listing 12: main.cpp // Container b e f i n d e n s i c h in Headern mit // entsprechendem Namen #include <vector > #include <l i s t > #include <map> int main ( int argc, char argv [ ] ) // Ein mit dem Wert Hallo i n i t i a l i s i e r t e r v e c t o r der Groesse zehn. 13
// Vektoren s o l l t e n immer dann verwendet werden, wenn d i e Groesse der // Daten bekannt i s t. std : : vector <std : : s t r i n g > vec (10, Hallo ) ; vec [ 4 ] = Bla! ; // Print t h e c o n t e n t s o f t h e v e c t o r for ( s i z e t i = 0 ; i < vec. s i z e ( ) ; ++i ) std : : cout << i << : << vec [ i ] << std : : endl ; // Eine d o p p e l t v e r k e t t e t e L i s t e. // Sehr n u e t z l i c h, wenn Daten dynamisch // am Ende e i n g e f u e g t werden muessen. std : : l i s t <int> l s ; l s. push back ( 2); l s. push back ( 1 ) ; l s. push back ( 5 ) ; l s. push back ( 4 ) ; // Gebe den I n h a l t der L i s t e aus. int i = 0 ; for ( std : : l i s t <int >:: i t e r a t o r i t = l s. begin ( ) ; i t!= l s. end ( ) ; ++i t, ++i ) std : : cout << i << : << i t << std : : endl ; // Eine Abbildung von s t d : : s t r i n g nach d o u b l e // s t d : : map wird i n t e r n durch einen Baum r e p r a e s e n t i e r t und // e n t h a e l t d e s h a l b a l l e Elemente s o r t i e r t std : : map<std : : s t r i n g, double> s t r t o d o u b l e ; s t r t o d o u b l e. i n s e r t ( std : : make pair ( Hallo, 1. 0 ) ) ; s t r t o d o u b l e. i n s e r t ( std : : make pair ( Bla, 2. 0 ) ) ; s t r t o d o u b l e. i n s e r t ( std : : make pair ( Blubber, 5. 0 ) ) ; // Doppeltes h i n z u f u e g e n f u e h r t zum Ueberschreiben. s t r t o d o u b l e. i n s e r t ( std : : make pair ( Hallo, 3e6 ) ) ; // Ein Element der Abbildung suchen std : : map<std : : s t r i n g, double >:: i t e r a t o r i t = s t r t o d o u b l e. f i n d ( Bla ) ; i f ( i t!= s t r t o d o u b l e. end ( ) ) // Das Element wurde gefunden, g i b e s aus std : : cout << i t >f i r s t << > << i t >second << std : : endl ; // C++ 11 e r l a u b t range b a s i e r t e s i t e r i e r e n : // V i e l b e s s e r! for ( int i : l s ) std : : cout << i << std : : endl ; // Wichtig : Fuer komplexe Datentypen Referenzen verwenden! for ( const std : : s t r i n g& s : vec ) 14
std : : cout << s << std : : endl ; // Auch ueber maps kann i t e r i e r t werden : for ( const std : : pair <std : : s t r i n g, double>& p : s t r t o d o u b l e ) std : : cout << p. f i r s t << > << p. second << std : : endl ; return 0 ; 1.9 Ein- und Ausgabe Für die Ein- und Ausgabe von Daten stellt C++ die IOstream Bibliothek bereit. #include <iostream > #include <fstream > Listing 13: main.cpp int main ( int argc, char argv [ ] ) i f ( argc < 3) // Beschwerde an den Benutzer, d a s s etwas n i c h t stimmt std : : c e r r << Not enough arguments s u p p l i e d << std : : endl ; return 1; std : : i f s t r e a m i n f i l e ( argv [ 1 ] ) ; i f (! i n f i l e ) std : : c e r r << Could not open f i l e << argv [ 1 ] << std : : endl ; return 1; std : : ofstream o u t f i l e ( argv [ 2 ] ) ; i f (! o u t f i l e ) std : : c e r r << Could not open f i l e << argv [ 1 ] << std : : endl ; return 1; char c ; // Lese einen c h a r a c t e r. Der o p e r a t o r >> i g n o r i e r t w h i t e s p a c e s! // Sind d i e s e erwuenscht i s t g e t ( ) zu verwenden. i n f i l e >> c ; 15
// Solange a l l e s in Orndung i s t while ( i n f i l e. good ( ) ) // S c h r e i b e den c h a r a c t e r in d i e Ausgabe o u t f i l e << c ; //Und l e s e den naechsten. i n f i l e >> c ; // Dateien s o l l t e n g e s c h l o s s e n werden wenn s i e n i c h t mehr // b e n o e t i g t werden i n f i l e. c l o s e ( ) ; o u t f i l e. c l o s e ( ) ; return 0 ; 2 Objektorientiertes Programmieren Man kann zwar vollständig imparative Programme in C++ schreiben, seine Stärken jedoch liegen in der objektorientierten Programmierung. Hierzu werden die Konzepte Kapselung, Vererbung und Polymorphie benötigt. #include <iostream > Listing 14: main.cpp class Base public : Base ( int bla ) : b l a ( bla ) // V i r t u e l l e Destruktoren s o l l t e n immer d e k l a r i e r t werden, da // ansonsten memory l e a k s a u f t r e t e n koennen virtual Base ( ) // V i r t u e l l e Funktionen sorgen dafuer, dass e i n e Funktion d i e auf // einem Pointer der O b e r k l a s s e a u f g e r u f e n wird an d i e Implementierung // der U n t e r k l a s s e w e i t e r g e l e i t e t wird. // In Java s i n d a l l e Funktionen automatisch v i r t u e l l virtual void do sth ( ) // A b s t r a k t e Methoden geben ein I n t e r f a c e vor, das a l l e U n t e r k l a s s e n // implementieren muessen virtual void d o a b s t r a c t ( ) = 0 ; protected : 16
; int b l a ; //Wir b e t r a c h t e n nur normale p u b l i c Vererbung class DerivedA : public Base public : DerivedA ( ) : Base ( 1 ) void d o a b s t r a c t ( ) std : : cout << In DerivedA << std : : endl ; ; class DerivedB : public Base public : DerivedB ( ) : Base ( 2 ) void b S p e c i f i c ( ) std : : cout << Only DerivedB implements t h i s << std : : endl ; void d o a b s t r a c t ( ) std : : cout << In DerivedB << std : : endl ; ; int main ( int argc, char argv [ ] ) // Polymorphie b e n o e t i g t Pointer oder Referenzen Base i n s t a n c e = new DerivedA ( ) ; i n s t a n c e >d o a b s t r a c t ( ) ; delete i n s t a n c e ; i n s t a n c e = new DerivedB ( ) ; i n s t a n c e >d o a b s t r a c t ( ) ; //Wenn e i n e Typumwandlung b e n o e t i g t wird, // muessen c a s t s verwendet werden : // Hier f i n d e t Typumwandlung zur L a u f z e i t s t a t t DerivedB b = dynamic cast<derivedb >( i n s t a n c e ) ; //WICHTIG: Auf E r f o l g pruefen : i f ( b ) b >b S p e c i f i c ( ) ; 17
// S o l l d i e Typumwandlung zur C o m p i l e z e i t s t a t t f i n d e n, //muss ein anderer Operator verwendet werden. Dieser s o l l t e // i n der Regel vermieden werden! DerivedB b s t a t i c = static cast <DerivedB >( i n s t a n c e ) ; b s t a t i c >b S p e c i f i c ( ) ; return 0 ; 18