Repetitorium Programmieren I + II Stephan Gimbel Johanna Mensik Michael Roth 6. März 2012
Agenda 1 Operatorüberladung 2 Pointer Zeigervariable 3 CopyConstructor 4 Listen 5 Array String 6 STL Container und Iteratoren
Operatorüberladung Einleitung Operatorüberladung bedeutet das implementieren eines bestimmten Operators für einen bestimmten Datentyp Für Standarddatentypen wie int sind alle Operationen wie +, ++, <, etc. definiert Für eigene Klassen logischerweise nicht! Es gibt unäre und binäre Operatoren
Operatorüberladung Unäre Operatoren ++, cast Operatoren, z.b. nach int Zugriffsoperator: [ ] Binäre Operatoren Rechenoperatoren: +,,, / Vergleichsoperatoren: <, > Streamoperatoren: <<, >>
Operatorüberladung Codebeispiel - 1 c l a s s Number { p r i v a t e : i n t number ; p u b l i c : Number ( ) ; Number ( i n t number ) ; } Number o p e r a t o r +( c o n s t Number othernumber ) c o n s t ;
Operatorüberladung Codebeispiel - 2 Number Number : : o p e r a t o r +( c o n s t Number othernumber ) c o n s t { r e t u r n ( Number ( number + othernumber. number ) ) ; }
Operatorüberladung Sinn der Überladung Operatoren erfüllen an sich den Zweck normaler Funktionen, allerdings ist ihre Funktionsweise sowie die Schreibweise festgelegt Ein < Zeichen ist intuitiver als eine Funktion islessthan, sieht im Code besser aus und das Zeichen ist standartisiert Viele STL Funktionen verwenden überladene Operatoren wie <, = und = =, um mit Objekten zu arbeiten
Operatorüberladung - Beispiele < Operator b o o l e a n Number : : o p e r a t o r <( c o n s t Number& othernumber ) c o n s t { r e t u r n ( number < othernumber. number ) ; } = = Operator b o o l e a n Number : : o p e r a t o r==(c o n s t Number& othernumber ) c o n s t { r e t u r n ( number == othernumber. number ) ; }
Operatorüberladung Achtung! Ihr könnt keine neuen Operatoren erfinden Operatoren gehen davon aus, dass sich das Objekt linksseitig befindet Der Streamoperator wird daher meist global implementiert Streamoperator - 1 ostream& Number : : o p e r a t o r <<(ostream& os ) { os << number ; r e t u r n ( os ) ; } c o n s t // Syntax : somenumber << cout ;
Operatorüberladung Lösung: Globale Funktion Das Problem lässt sich beheben, in dem man den Streamoperator global implementiert. Und zwar mit cout als linksseitigen Operator! Streamoperator - 2 ostream& o p erator <<(ostream& os, c o n s t Number& n ) { os << n. number ; r e t u r n ( os ) ; } // Syntax : cout << somenumber ;
Pointer Pointer (Zeiger) werden zur indirekten Adressierung benutzt Anwendung i n t i = 5 ; i n t p i ; p i = &i ; i n t p i 1 = &i ;
Pointer können den Wert des Objekts, auf das sie zeigen, lesen Anwendung i = p i + 2 ; // i = 7 cout << p i ; // Ausgabe : p i = 7 können den Wert des Objekts, auf das sie zeigen, ändern Anwendung p i = i + 2 ; // Ausgabe : p i = 9 p i += p i ; // Ausgabe : p i = 18 können zur Laufzeit neue Adressen zugewiesen bekommen, um auf andere Objekte zu zeigen Anwendung p i 1 = p i ;
Pointer Anwendung // V e r g l e i c h d e r Objekt Werte : cout << ( p i == p i 1 ) ; // t r u e // V e r g l e i c h d e r Z e i g e r Werte : cout << ( p i == p i 1 ) ; // t r u e // V e r g l e i c h d e r Z e i g e r A d r e s s e n : cout << (& p i == &p i 1 ) ; // f a l s e
Pointer können zur Laufzeit Adressen von Objekten zugewiesen bekommen, die erst zur Laufzeit erzeugt werden Anwendung p i 1 = new i n t ; p i 1 = 2 0 ; cout << p i 1 ; // Ausgabe : 20 d e l e t e p i 1 ; // g i b t S p e i c h e r f r e i, l ö s c h t a b e r n i c h t p i 1 p i 1 = p i ;
Pointer bekommen zur Compile-Zeit nur Speicher für eine Objektadresse, aber nicht den Speicher für das Objekt selbst zugewiesen. Sie werden vom C++ Compiler nicht initialisiert. Falsch double pd ; pd = 2. 0 ; Richtig double pd = 0 ; Absturz zur Laufzeit vorprogrammiert und evtl. nicht reproduzierbar Fehler schwer zu finden
Pointer unterliegen der Typkontrolle durch den Compiler Anwendung i n t p i = 0 ; i n t i = 7 ; double pd = 0 ; p i = &i ; // Geht, b e i d e vom Typ i n t Absturz pd = &i ; // Absturz, da pd d o u b l e und i i n t i s t pd = 0xB001 ; // Absturz, da pd d o u b l e und Wert vom Typ l o n g pd = p i ; // Absturz, da pd d o u b l e und p i i n t i s t Typkontrolle aushebeln mit explizitem cast Anwendung pd = r e i n t e r p r e t c a s t < double > ( pv ) ;
Pointer Pointer Adressen i n t i = 4 2 ; // i l i e g t b e i 0xB000 i n t p i = &i ; // p i z e i g t a u f 0xB000 ( p i )++; // i = 4 3 ; p i ++; // z ä h l t A d r e s s e hoch, p i z e i g t nun a u f 0xB004 const Pointer (Zeiger auf Konstante) c o n s t i n t i = 4 2 ; c o n s t i n t j = 2 3 ; i n t p i = &i ; // C o m p i l e r f e h l e r c o n s t i n t p i = &i ; // geht p i = &j ; // geht Der Pointer zeigt auf eine Konstante. Wohin der Pointer zeigt kann verändert werden, nicht aber der Inhalt.
Pointer const Pointer (konstante Zeiger auf Variablen) i n t a = 42, b=23; i n t c o n s t pa = &a ; pa = &b ; // C o m p i l e r f e h l e r pa = 1 0 ; // geht, I n h a l t von a=10 Der Pointer zeigt auf eine Variable deren Inhalt sich ändern lässt, allerdings kann der Pointer nicht auf andere Speicherstellen zeigen.
Pointer const Pointer (konstante Zeiger auf Variablen / Konstanten) i n t a = 4 2 ; c o n s t i n t b=23; c o n s t i n t c o n s t p=&a ; // geht cout << p << e n d l ; // Ausgabe : 42 p = 1 7 ; // geht n i c h t a = 1 7 ; // geht b = 1 7 ; // geht n i c h t Der Pointer zeigt auf eine Variable oder Konstante deren Inhalt sich nicht über den Pointer ändern lässt. Der Pointer kann nicht auf andere Speicherstellen zeigen.
Pointer Ersatz für Referenzparameter in Parameterlisten von Funktionen/Methoden Anwendung i n t gibwert ( ) c o n s t { r e t u r n t h i s >wert ; } Zahl& add ( c o n s t i n t wert ) { t h i s >wert += wert ; r e t u r n t h i s ; } Anwendung v o i d main ( ) { Zahl z = 1 0 ; cout << z. add ( 4 ). add ( 6 ). gibwert ( ) << e n d l ; }
Pointer Anwendung i n t main ( ) { Konto pkonto1 = 0, pkonto2 = 0, pkonto3 = 0 ; // Dynamisch O b j e k t e e r z e u g e n pkonto1 = new Konto ; pkonto1 >setname ( Max ) ; // K o n s t r u k t o r mit 3 Parametern a u f r u f e n pkonto2 = new Konto ( M o r i t z, 38923, 1 0. 0 0 ) ; // Kopie von Konto2 e r z e u g e n pkonto3 = new Konto ( pkonto2 ) ; // S p e i c h e r w i e d e r f r e i g e b e n d e l e t e pkonto1 ; d e l e t e pkonto2 ; d e l e t e pkonto3 ; }
Copy Constructor Bisher Objekt erzeugt von anderem Objekt des gleichen Typs Objekt übergaben (Call by Value) Objekt Rückgabe aus Funktion c l a s s A { p r i v a t e : i n t x ; p u b l i c : A( ) { x = 1 0 ; } A( ) {} } ;
Copy Constructor mit Copy Konstruktor c l a s s B { p r i v a t e : char name ; p u b l i c : B( ) {name = new char [ 2 0 ] ; } B( ) { d e l e t e [ ] name ; } } ; // copy c o n s t r u c t o r B( c o n s t B &b ) { name = new char [ 2 0 ] ; s t r p y ( name, b. name ) ; }
Deep- und Shallow-Copy
einfach verkette Liste Anwendung c l a s s Number { p r i v a t e : i n t number ; Number n e x t ; }
einfach verkette Liste Anwendung Number Anfang = 0 ; Number A k t u e l l = 0 ; Number neueselement = 0 ;
einfach verkette Liste Anwendung neueselement = new Number ( 4 0 4 ) ;
einfach verkette Liste Anwendung Anfang = neueselement ;
einfach verkette Liste Anwendung A k t u e l l = neueselement ;
einfach verkette Liste Anwendung neueselement = new Number ( 2 ) ;
einfach verkette Liste Anwendung A k t u e l l >s e t N e x t ( neueselement ) ;
einfach verkette Liste Anwendung A k t u e l l = neueselement ;
einfach verkette Liste Anwendung neueselement = new Number ( 9 9 ) ;
einfach verkette Liste Anwendung Number Anfang = 0 ; Number A k t u e l l = 0 ; Number neueselement = 0 ; neueselement = new Number ( 4 0 4 ) ; Anfang = neueselement ; A k t u e l l = neueselement ; neueselement = new Number ( 2 ) ; A k t u e l l >s e t N e x t ( neueselement ) ; A k t u e l l = neueselement ; neueselement = new Number ( 9 9 ) ;...
einfach verkette Liste Elemente ausgeben Anwendung A k t u e l l = Anfang ; do { cout << A k t u e l l >getnumber ( ) << e n d l ; A k t u e l l = A k t u e l l >getnext ( ) ; } w h i l e ( A k t u e l l!= 0) ;
Doppeltverkettete Liste Anwendung c l a s s Number { p r i v a t e : i n t number ; Number p r e ; Number n e x t ; }
Baum Anwendung c l a s s Number { p r i v a t e : i n t number ; Number l e f t ; Number r i g h t ; }
Array Seite 1 i n t a r r a y 1 [ 1 0 ] ; // D e f i n i t i o n f o r ( i n t i =0 ; i <10 ; i ++) { a r r a y 1 [ i ]= i i ; } f o r ( i n t i =0; i <10; i=i +1) { cout<<a r r a y 1 [ i ]<< \n ; }
Array Seite 2 i n t a r r a y 1 [ ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ; // a r r a y hat 10 e l e m e n t e i n t a r r a y 2 [ 3 ] = { 0, 1 } ; // a r r a y 2 [0]==0, a r r a y 2 [1]==1, a r r a y 2 [2]==??? i n t a r r a y 3 [ 2 ] ; // a r r a y 3 [ 0 ] und a r r a y 3 [ 1 ] s i n d u n d e f i n i e r t i n t a r r a y 4 [ 1 0 0 ] = { 0 } ; // a l l e e l e m e n t e haben den wert 0 i n t a r r a y 5 [ 1 0 0 ] = { 1 } ; // das e r s t e e l e m e n t hat den wert 1, a l l e a n d e r en den wert 0 char a r r a y 6 [ 2 ] [ 5 ] = // d i e 2 i s t o p t i o n a l { { H, a, l, l, o }, { W, e, l, t } } ; // a r r a y 6 [ 1 ] [ 4 ] == 0
Array Seite 3 char c ; // Ein P o i n t e r a u f c h a r c=new char [ j ] ; // R e s e r v i e r e S p e i c h e r f ü r j c h a r a c t e r s // P o i n t e r c z e i g t nun d a r a u f d e l e t e [ ] c ; //wenn mit new [ ] S p e i c h e r r e s e r v i e r t wird, // muss d e r S p e i c h e r mit d e l e t e [ ] w i e d e r // f r e i g e b e n werden
Array Seite 4 char c [ 4 ] ; // A r r a y von P o i n t e r n c [ 0 ] = new char ; c [ 1 ] = new char ; c [ 2 ] = new char ; c [ 3 ] = new char ; c [ 0 ] = A ; c [ 1 ] = B ; c [ 2 ] = C ; c [ 3 ] = D ; d e l e t e c [ 0 ] ; d e l e t e c [ 1 ] ; d e l e t e c [ 2 ] ; d e l e t e c [ 3 ] ;
Array Array als Parameter v o i d somefunction ( i n t a r r [ ] ) { cout << s i z e o f ( a r r ) << e n d l ; // Ausgabe : 4 } i n t main ( ) { i n t meinarray [ 1 5 ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1 0, 1 1, 1 2, 1 3, 1 4, 1 5 } ; somefunction ( meinarray ) ; cout << s i z e o f ( meinarray ) << e n d l // Ausgabe : 60 << s i z e o f ( i n t ) << e n d l // Ausgabe : 4 << s i z e o f ( meinarray ) / s i z e o f ( i n t ) << e n d l ; // Ausgabe : 15 r e t u r n 0 ; } Achtung! In einer Parameterliste ist int arr[] das gleiche wie int*, daher kann sizeof() nicht zur ermittling der Größe verwendet werden. Dies funktioniert nur im Sichtbarkeitsbereich der Definition!
Array Array als Parameter - korrekt v o i d somefunction ( i n t a r r [ ], s i z e t n ) { f o r ( s i z e t i =0; i <n ; ++i ) cout << a r r [ i ] << e n d l ; }
Array Mehrdimensionale Arrays als Parameter (2D) v o i d t a b e l l e n a u s g a b e ( i n t ( T) [ 3 ], s i z e t n ) { // A l t e r n a t i v : v o i d t a b e l l e n a u s g a b e ( i n t T [ ] [ 3 ], s i z e t n ) { f o r ( s i z e t i =0; i <n ; ++i ) { f o r ( s i z e t j =0; j <3; ++j ) cout << T[ i ] [ j ] ; cout << e n d l ; } cout << e n d l ; } i n t main ( ) { i n t f e l d 1 [ 2 ] [ 3 ] = { { 1, 2, 3 }, { 4, 5, 6 } } ; i n t ( p F e l d ) [ 3 ] = f e l d 1 ; // z e i g t a u f Z e i l e f e l d 1 [ 0 ] ; t a b e l l e n a u s g a b e ( f e l d 1, 2) ; r e t u r n 0 ; }
Array Mehrdimensionale Arrays als Parameter - Fortsetzung 3D... Information [3][4] für den Datentyp int Matrix3D [2][3][4], usw... v o i d t a b e l l e n a u s g a b e ( i n t ( T) [ 3 ] [ 4 ], s i z e t n ) ; // bzw : v o i d t a b e l l e n a u s g a b e ( i n t T [ ] [ 3 ] [ 4 ], s i z e t n ) ;
Array Zweidimensionale Arrays als Parameter - Beliebige Spaltengröße template<typename Datentyp> v o i d t a b e l l e n a u s g a b e T m p l ( Datentyp t a b e l l e, s i z e t n ) { c o n s t s i z e t SPALTEN = s i z e o f t a b e l l e [ 0 ] / s i z e o f t a b e l l e [ 0 ] [ 0 ] ; f o r ( s i z e t i =0; i <n ; ++i ) { f o r ( s i z e t j =0; j <SPALTEN ; ++j ) cout << t a b e l l e [ i ] [ j ] << ; cout << e n d l ; } cout << e n d l ; } i n t main ( ) { i n t f e l d 1 [ 2 ] [ 7 ] = { { 1, 2, 3, 4, 5, 6, 7 }, { 4, 5, 6, 7, 8, 9, 1 } } ; t a b e l l e n a u s g a b e T m p l ( f e l d 1, 2) ; r e t u r n 0 ; } Funktioniert für beliebige Spaltengröße, aber Zeilen müssen angegeben werden!
Array Interpretation von [], [][],... Compiler wandelt Indexoperatoren in Zeigerdarstellung um X bezeichnet alles vor dem letzten Klammerpaar, Y ist der Inhalt des letzten Klammerpaares X[Y] = *((X)+(Y)) Fortgesetzte Anwendung löst alle Indexoperatoren auf statt matrix[i][j] geht also auch: *(matrix[i]+j) oder *(*(matrix+i)+j) Interpretation: Matrix[i] ist ein Zeiger auf i-te Zeile Position (matrix+i) liegt (i mal sizeof(matrix[0])) Bytes von der Stelle matrix entfernt siehe Template Beispiel auf vorheriger Seite!
String Stellt typische Operationen für die Verarbeitung von Zeichenketten zur Verfügung Anwendung #i n c l u d e <i o s t r e a m > #i n c l u d e <s t r i n g > u s i n g namespace s t d ; Anwendung s t r i n g month = J u l i ;
String Verwendung Stellt typische Operationen für die Verarbeitung von Zeichenketten zur Verfügung Anwendung c i n >> month ; // Eingabe i n t l e n g t h = month. l e n g t h ( ) ; // Länge des S t r i n g s i f ( month == J u l i ) // V e r g l e i c h e n z w e i e r S t r i n g s cout << month ; cout << month. s u b s t r ( 1, 2) ; // S u b s t r i n g month2 = August ; month. swap ( month2 ) ; // I n h a l t e t a u s c h e n
String Verwendung Anwendung month = month + + month2 ; // S t r i n g s zusammensetzten s i z e t pos1 = month. f i n d ( J u l i ) ; //Wort f i n d e n i f ( pos1!= s t r i n g : : npos ) // F a l l s S t r i n g vorkommt month. e r a s e ( pos1, 4) ; f o r ( i n t i = 0 ; i < month. l e n g t h ( ) ; i ++) cout << month [ i ] ; // Z u g r i f f a u f e i n z e l n e Z e i c h e n
String Verwendung Anwendung s t r i n g s t r i n g 1 = A ; // ASCII : 65 s t r i n g s t r i n g 2 ; s t r i n g 2 [ 0 ] = ( s t r i n g 1 [ 0 ] + 1 ) ; cout << s t r i n g 2 [0]<< e n d l ; // ASCII : 66 = B
STL Container und Iteratoren Häufig verwendete Container vector list set multiset map multimap Für jeden dieser Container gibt es einen Iterator
STL Container und Iteratoren Anlegen von Containern und Iteratoren Container und Iteratoren sind Templates, sie werden also entsprechend angelegt: // Einen v e c t o r a n l e g e n v e c t o r <i n t > myvector ; // Ein Set a n l e g e n s e t <s t r i n g > myset ; // I t e r a t o r a u f s e t a n l e g e n s e t <s t r i n g >:: i t e r a t o r i t = myset. b e g i n ( ) ;
STL Container und Iteratoren vector Einfaches dynamisches Array Einfügen typischerweise mit push back() Zugriff über [ ] Operator oder Iteratoren list Einfügen mit push back oder insert() Zugriff über Iteratoren, kein wahlfreier Zugriff auf Elemente in der Mitte Sortierbar über sort(), verwendet entweder den < Operator oder einen Funktionszeiger zu einer eigenen lessthan Funktion
STL Container und Iteratoren set Ein Satz von einmaligen Elementen, intern ein binärer Baum Einfügen über insert, beim Einfügen wird sortiert Zugriff über Iteratoren multiset Elemente dürfen mehrfach vorkommen count() Funktion gibt Auskunft über die Häufigkeit
STL Container und Iteratoren Vector #i n c l u d e <v e c t o r >... v e c t o r <i n t > myvector ; // d e c l a r e V e c t o r myvector. p u s h b a c k ( 4 2 ) ; // add Element a t end myvector. p u s h b a c k ( 2 3 ) ; myvector. p u s h b a c k ( 1 1 7 ) ; // V e c t o r c o n t e n t : 42, 23, 117 cout << myvector. s i z e ( ) ; //... 3 cout << myvector. f r o n t ( ) ; //... 42 cout << myvector. back ( ) ; //... 117 myvector. empty ( ) ; //... f a l s e cout << myvector. a t ( 1 ) ; //... 23 cout << myvector [ 1 ] ; //... 23 myvector. e r a s e ( myvector. b e g i n ( ) +1) ; // Vec c o n t e n t : 42, 117 myvector. pop back ( ) ; // Vec c o n t e n t : 42
STL Container und Iteratoren Vector Iteration v e c t o r <i n t > o t h e r V e c t o r ;... // V e c t o r Content : 17, 64, 74, 23, 42, 73, 87 v e c t o r <i n t >:: i t e r a t o r i t ; // I t e r a t o r ( wie P o i n t e r ) z u r N a v i g a t i o n durch STL C o n t a i n e r f o r ( i t=o t h e r V e c t o r. b e g i n ( ) ; i t < o t h e r V e c t o r. end ( ) ; i t ++) cout << << i t ; // Ausgabe : 17 64 74 23 42 73 87 f o r ( i n t i =0; i <o t h e r V e c t o r. s i z e ( ) ; i ++) cout << << o t h e r V e c t o r. a t ( i ) ; f o r ( i n t i =0; i <o t h e r V e c t o r. s i z e ( ) ; i ++) cout << << o t h e r V e c t o r [ i ] ; o t h e r V e c t o r. c l e a r ( ) ; //... l e e r
STL Container und Iteratoren List #i n c l u d e <l i s t >... v e c t o r <i n t > mylist ; mylist. p u s h b a c k ( 1 7 ) ; mylist. p u s h b a c k ( 6 4 ) ; mylist. p u s h b a c k ( 7 4 ) ; mylist. p u s h b a c k ( 2 3 ) ; mylist. p u s h b a c k ( 4 2 ) ; mylist. p u s h b a c k ( 7 3 ) ; mylist. p u s h b a c k ( 8 7 ) ; // L i s t c o n t e n t : 17, 64, 74, 23, 42, 73, 87 mylist. p u s h f r o n t ( 1 0 ) ; // L i s t c o n t e n t : 10, 17, 64, 74, 23, 42, 73, 87 mylist. pop back ( ) ; //... 10, 17, 64, 74, 23, 42, 73 mylist. p o p f r o n t ( ) ; //... 17, 64, 74, 23, 42, 73 mylist. remove ( 7 4 ) ; //... 17, 64, 23, 42, 73
STL Container und Iteratoren List Iteration v e c t o r <i n t > o t h e r L i s t ; // I n h a l t : 23, 43, 45, 67, 34, 54, 43, 17, 43, 56 o t h e r L i s t. u n i q u e ( ) ; //... 23, 43, 45, 67, 34, 54, 17, 56 o t h e r L i s t. s o r t ( ) ; //... 17, 23, 34, 43, 45, 54, 56, 67 l i s t <i n t >:: i t e r a t o r i t ; f o r ( i t=o t h e r L i s t. b e g i n ( ) ; i t!= o t h e r L i s t. end ( ) ; ++i t ) cout << << i t ; kein Index-Operator [] kein.at() sequentiell!