C++ ZUSAMMENFASSUNG Christian Forster, 28. August 2007 [email protected] Patrik Rohner, 15. Juli 2008 [email protected] AUFBAU EINES PROGRAMMS #include <iostream> #include <cstdlib> #include <cmath> //für math. func #include <string> //für c++ strings #include "headerfile.h" //einbinden #include <time.h> //Zeitmessung using namespace std; HEXADEZIMALER CODE & ADRESSEN KONTROLLSTRUKTUREN ARRAYS 0,1,…,9,A,B,C,D,E,F (hex) anstelle von 0,1,…,14,15,16 (dec) Adressen werden hexadezimal angegeben. a,a+1,a+2 a,a+1,a+2,a+3… int, float (4byte = 32bit) double (8byte = 64bit) 0x22ff70 0x22ff70 0x22ff74 0x22ff78 0x22ff78 0x22ff80 0x22ff7c 0x22ff88 IF int Array mit 4 Zellen: Inhalte definieren: oder kurz: a==10?b=15:a==11?b=14:b=10; b=10; FLIESSKOMMAZAHLEN FOR Float //structs, functions, enums 1bit->sign, 8bit->exponent, >exponent, 23bit 23bit->mantisse S (E-127) 127) Wert = (-1) x 2 x (1.F) Bsp1: 0.125 = 2-3 » S -> 0, E -> 124, F -> 0 0|01111100|00000000000…0 = 0.125 0|01111111|00000000000…0 |01111111|00000000000…0 = 1 1|01111111|11000000000…0 000000000…0 = -1.75 0|00000000|00000000000…0 = 0 0|11111111|00000000000…0 = +infty 0|00000000|10010101110… = NaN int main(void) { system(“pause”); return 0; Double 1bit->sign, 11bit->exponent, >exponent, 52bit->mantisse 1023) Wert = (-1)S x 2(E-1023) x (1.F) } OPERATOREN VARIABELN + - / * ^ % x += i 1.1E-5 i++, i-- 32bit . positive und negative ganze Zahlen 31 31 range: -2 bis 2 -1 char int mit 8bit ( = 1 Byte) für Buchstaben char[] Buchstaben Array -> String float Kommazahlen 32bit,, Eingabe: 3.0f double Kommazahlen 64bit,, Eingabe: 3.0 short .. .. Verkürzung. short int ->> 16 bit int long .. .. Verlängerung long int -> 32 bit (wie int) unsigned .. .. nur positive Zahlen int ->> 0 bis 232-1 bool Wahrheitswerte (true/1, false/0) int mathematische Operatoren ganzzahliger nzzahliger Rest einer Division 15%6==3 x = x + i; ebenso *=, /=, -= = 1.1*10-5 erhöht / verkleinert i um 1 b=5; c=b++; → c=5, b=6 verwende ++b für c=6, b=6 Für weitere mathematische Funktionen: #include <cmath> fabs(), sqrt(), exp(), log(), cos(), acos() VARIABELNNAMEN LOGISCHE KONSTRUKTE Keine Leerzeichen, Satzzeichen oder Symbole Keine Zahl oder __ am Anfang case sensitivity – Gross - Kleinschreibung beachten <, <=, >, >= || && == != ! EINFACHE VARIABELN DEKLARIEREN int a,a2; int b = 10; float c = a*b – 0.5; CASTS EINGABE & AUSGABE WHILE oder do { b--; } while(b!=15); //Ausgabe //Eingabe //mindestens 1 x abbrechen mit break (immer nur die innere Schleife) Schleife); Überspringen des Rests des Rumpfes zur nächsten Auswärtung mit continue; SWITCH switch(a) { case 15:cout<<”a=15”;break; //a==15 case 14:cout<<”a=14”;break; //a==14 default:cout<<”a!=15,a!=14”; cout<<”a!=15,a!=14”; //else } Man kann verschiedene Strukturen verwenden um ein und dasselbe auszudrücken: int i=0; do { i=i+1; if (i==10) break; }while(true); //aka immer for(int i=0;i!=10;i++){} BEISPIELE FÜR ENDLOSSCHLEIFEN \n \t \” ENUM 13 6 3 1 0 Zeilenende horizontaler Tabulator Anführungszeichen UMRECHNUNG BINÄR – DEZIMAL 1 0 1 1 Dezimalzahl durch 2 teilen und Rest notieren. Bits von unten nach oben lesen. Bsp: 13 = 1101 3 2 1 1001 = 1*2 + 0*2 + 0*2 + 0*2 0 = 8+0+0+1 = 9 2D Array (Matrix): 3D Array: int a[4]; a[0] = 1; int a[4] = {1,2,3,4}; int b[3][2] = {{1,2},{ int c[x][3][ c[x][3][x-1] = {{{ Bei int a[N]; muss N als const int N = 10; definiert werden. Eine const int kann während dem Programmablauf nicht geänd dert werden. Ein Array beginnt immer mit a[0] und endet mit a[N-1] Übergibt man ein Array einer Funktion Funktion, ist das wie “Call by Reference”. Das Original-Array Array wird verändert. ARRAYS UND POINTER Arraynamen sind Pointer! Bei der Definition eines Arrays wird Speicherplatz für eine bestimmte Anzahl Objekte reserviert. Die Arrayvariable zeigt auf das erste Objekt dieses Speicherplatzes. Darum sind folgende Ausdrücke identisch: while(b<20){ b++; } …ist äquivalent zu… cout << “a = “ << endl; cin >> a; double a = 1.5; int b; b = int (a); b = (int) a; // b=1 7/2 = 3 , 7/(double)2 = 7/2.0 = 3.5 double(7/2) = 3.0 , int(19/10.0) = 1 enum farbe {ROT, BLAU, GELB}; farbe f = ROT; if(f != BLAU) { }; for(int i=0; i<10; i++) { a=a+i; } abbrechen mit break; ÄQUIVALENTE STRUKTUREN grö össer, grössergleich, kleiner “oder” “und” “gleichheit” “ungleich” “nicht” Änderung einer Variable in einen anderen Typen Typen. Enum ist ein Aufzählungstyp. Die Konstanten aus der Enum kann man im Programm verwenden. if(a==10){ b=15; } else if(a==11) b=14; else b=10; int c[10]; int* pc; pc = c; //Array definieren //Pointer definieren //Pointer zeigt auf Array pc[3] = 10; ↔ c[3]=10; ↔ *(pc+3)=10; Folgendes generiert auch ein Array mit Platz für 3 Integer: int * a = new int[3]; a[0] = 3; //ohne Stern * (!) delete [] a;//Speicher //Speicher wieder freigeben STRUCTURES Structs werden vor der main() Funktion definiert. struct point { int x, y; double gamma; }p,q; //p,q schon definiert Neuer “point” definieren: point p; Variabeln in struct definieren: p.x = 2; Schnell initialisieren: p = {1,2,0.75}; Rest wird mit 0 aufgefüllt: q={1}->q={1,0,0}; Zuweisung: p=q ist gleichbedeutend mit p.x=q.x; p.y=q.y; p.gamma=q.gamma gamma; Falsch: struct falsch {int i;falsch x;}; Strichpunkt am Ende nicht vergessen: struct point {int i;double y;} y;}; FUNKTIONEN IN STRUCTSS int i=10; do { i=i+1; if (i==10) break; }while(true); for(int i=3;i!=20;i=(i+3)%300) Die Konstruktor-Funktion Funktion wird bei der Generierung eines neuen Structs aufgerufen. struct Bar { Bar() () { //Konstruktor }; } Funktionen können auch ausgelagert w werden: int i=99; while(i>10){ i--; if(i==15) i*=6;} struct Bar { void bier bier(); void Bar::bier(){ }; Aufrufen der Funktion: } bqm; bqm.bier(); FUNKTIONEN CALL BY REFERENCE (DYNAMISCH (DYNAMISCH) Ermöglichen en Aufspaltung des Programms in Unterprogramme. Hier wird statt der Referenz ein Pointer auf das Objekt übergeben. Damit auf das Objekt zugegriffen werden kann, muss der übergeben Pointer dereferenziert werden. Aufbau: rückgabewert funktionsname (argument) {funktionskörper} Der Rückgabewert ist immer nur 1 Element und kann von beliebigem Typ sein. Falls die Funktion keine Rückgabe hat, schreibt man void. Der Funktionsname darf nicht mit einer Zahl beginnen. Nur ein Wert als Rückgabewert. Workaround: St Structs PROTOTYP EINER FUNKTION Falls eine function g die function f benötigt, muss f vorab definiert sein: int f(int,int,int); //prototyp int g(int x, double y){… a=f(x)+y …}; int f(int x, int y, int z){… b=g(z) …}; CALL BY VALUE Wenn eine Funktion aufgerufen wird, warden by call call-byvalue die Argumente auf den Stack kopiert. void swap1(int a, int b) {int c=a; a=b; b=c;} int main(){ int x=2, y=3; swap1(x,y);… //bringt nichts Auch wenn ich a und b innerhalb der Funktion tausche, bleiben x und y in main() noch gleich. ->> structures int main(){ int x=2, y=3; st out = swap1(x,y); x=out.a; y=out.b;… int main(){ int x=2, y=3; swap3(&x,&y);… //vertauschen Aufruf: swap3(pa,pb); wenn pa pa, pb Pointer sind oder swap3(&a,&b); wenn a,, b keine Pointer sind. Mann kann auch einer Funktion einen dereferenzierten Pointer übergeben: void swap3(int (int a, int b){… b){…} Aufruf: swap3(*pa, , *pb *pb); Das wirkt dann aber wie call-by-value value und macht hier keinen Sinn. REKURSION Der retu r n Wert der Funktion ruft die Funktion selber wieder auf. Dabei müssen die Abbruchbedingungen definiert werden. Es wird bis zur Abbruchbedingung in die Rekursion hineingegangen und dann von innen aufgelöst. int fakultaet( int n ) { if(n==1) return 1; return n * fakultaet(n fakultaet(n-1);} Aktive Funktion belegt Speicherplatz peicherplatz im Stack. Dieser kann dadurch überfüllt werden. STRINGS C – STRINGS char text[] = “hallo”; //auto: ‘/0’ char text[] = {’h’,’a’,’l’,’l’,’o’,’/0’} char text[6]= = “hallo”; //mit mit structure //kopieren -> x,y CALL BY REFERENCE (STATISCH) Die Variabeln werden nicht kopiert. Es wird eine Referenz auf das Objekt gemacht. Nun geht das Vertauschen einfach: void swap2(int& a, int& b) {int c=a; a=b; b=c;} int main(){ int x=2, y=3; swap2(x,y);… //vertauschen vertauschen Ein Funktionsaufruf swap2(x,y);vertauscht vertauscht x und y. Es warden von der Funktion nur die Adressen der Variabeln genommen und diese vertauscht, was Rückwirkung hat. Referenzen zeigen auf fixe Adressen, Pointer können ihre Adresse ändern. int n; int& nr = n; nr und n können nun als Synonyme verwendet werden. nr und n sind aliases. Array vergrössern/Löschen: int*aa = new int[2*n]; for (int i=0;i<n;i++) { aa[i]=a[i]; } delete[] a; a=aa; n=2*n; aa=NULL; void swap3(int* a, int* b) {int c=*a; *a=*b; *b=c;} struct st{int a,b}; st swap1(int a, int b){ int c=a; a=b; b=c; st ret={a,b}; return ret;} ASCII TABELLE nicht: char text[5]= ”hallo”; char* text = “hallo”; char* str = new char[4]; str[0] = ‘C’; Ein String wird als Pointer auf ein Array von chars definiert definiert. Das Array hat die Länge n+1. a[n] = O Bit, Abschluss, ‘/0’ liefert das i-te Zeichen liefert den ASCII Code des i-ten Zeichens int strlen(char text[]) liefert Länge ohne “\0” char(65) -> A (aus ASCII Tabelle) text[i] (int) text[i] 00-31: NUL,… 32: SPACE 48-57: 0-9 65-90: A-Z 97-122: aa-z 127: DEL POINTER Ein Pointer speichert und zeigt auf eine Adresse Adresse. Wenn an dieser Adresse ein Objekt liegt, dann zeigt der Pointer auf das Objekt. Pointer braucht (meist) 4Bytes Speicherplatz. Dereferenzierungsoperator *: Zugriff auf Inhalt der Speicherzelle auf die der Pointer verweist. Referenzierungsoperator &:: Ermittlung der Adresse einer Variable. int* pa = 0; int *pb; pb = NULL; //Pointer der auf int //zeigen soll 0 (NULL) //setzen. int a = 3; pa = &a; //pa unzeigen, o.k. *pb = 3; //b über pb ändern -> FEHLER Wenn ein Zeiger auf NULL zeigt, ist er unbrauchbar, er muss zuert wieder umgezeigt warden. Man kann * und & beliebig kombinieren. Ein Paar Beispiele: int i=1, *ip, **ipp; //ipp zeigt auf ip, ip = &i; ipp = &i; //ip auf i i->ipp auf **ipp=6; //i und **ipp=i=6 cout<<*&**&ip; //-> 6 Achtung: && ist ein logisches Konstrukt. Beispielaufgaben:: Welchen Typ haben die Variabeln? a = &b; b = c[12]||(2>=1); -> bool* a; bool b; bool c[20] c[20]; a = b.c*3.14; b.a = char(b.c%2) == c; //==,% beachten -> double a; char c; > struct foo {bool a; int c;} b; -> b.b = &b; b.dat = “jawohl, genau du”; -> struct ff{ff* f{ff* b; char* dat;} a; a.b[2] = 5.0f; a.a = (a.b[2] > a.b[1]); -> > struct sss{float b[5]; bool a;} a; liest n-11 Zeichen von der Tastatur in str[] und hängt “\0” an: void cin.getline(char str[], int n); DYNAMISCHE SPEICHERALLOZIERUNG C++ - STRINGS Mit new kann man einen Pointer inter und neues Objekt erzeugen. Bei Arrays muss die Grösse nicht mehr const sein. In C++ neue Klasse, benötigt #include <string> Überladene Operatoren in der string string-Klasse (+,…), siehe Bsp string myname, yourf fname, yourlname; myname = ”dick banger” cout << ”please enter your name ”; cin >> yourfname >> yourlname; if (yourfname +" "+ yourlname == myname) {cout << ”\n What a coincidence!”; coincidence!”;} double *dp = new double; int *ip = new int(3); //*pd=3 int *pe = new int[n]; //dyn. dyn. Array Falls kein Speicher vorhanden -> Fehler. Abhilfe: delete dp = new (nothrow) double [n]; if (dp == 0) cout<<”Error mem alloc”; else {…} //kein Programmabbruch //neues Array aa //kopieren von a //nach aa //löschen von a //umzeigen … POINTER AUF ARRAYS Wie schon erwähnt, sind Arraynamen Pointer, genauer gesagt Pointer auf eine konstante Adresse, auf das erste Element des Arrays. int* const ip //const. Zeiger (Array) const int* ip //Zeiger Zeiger auf const. int const int* const ip //beides POINTER AUF STRUCTS struct triple {int a, a,*b,c;}trp; *(trp.b) //deref von (trp.b) *trp.b //wie oben triple* * tp = new struct triple; tp->a=10; //a in tp beschreiben (*tp).a=10; //wie oben tp->getBeer(); // //siehe Klassen ARGUMENTE VON MAIN int main(int argc, , char** argv){ for(int i=0;i<argc;i++)cout<< … //output argc ist die Anzahl der Parameter argv ist das Array dieser Parameter atoi()konvertiert konvertiert ascii to integer, atof() to float EINFACHE LISTEN Man generiert Elemente (Knoten), die über einen Pointer auf das jeweils nächste Element zeigen. struct tNode{ int key; tNode* next; }; tNode *list = 0; Der Zeiger auf den Anfang der Liste ist der Anker (aka root). NEUER KNOTEN AM ENDE Die neue Liste braucht 2 neue Pointer, last zeigt auf den letzten Knoten, node auf den aktuellen Knoten: tNode *node, *last; node = new tNode; //neuer Knoten node->key = value; //Daten einfügen node->next = NULL; //letzter Knoten if (list == 0){ //Liste leer? last = node; //neuer letzer Knoten list = node;} //Anker auf Anfang else { last->next >next = node //anhängen last = last->next >next //neuer l. Knoten Beim Aufbau der List mit Anhängen der Knoten am Anfang geht man gleich ich vor, nur umgekehrt: node->next=list (nicht mehr 0) verknüpft den neuen Knoten, … LINEARE SUCHE HEAP O - NOTATION KONSTRUKTOREN / DESTRUKTOREN Suchfunktion nach Key k, mit Zähler count (Rückgabewert): Im Gegensatz zum Stack bietet der Heapspeicher viel mehr Möglichkeiten. Elemente des Heapspeichers haben einen Schlüssel und können zu jedem Zeitpunkt entnommen werden. Dafür braucht er auch viel mehr Platz. Im Heap werden oft Bäume verwendet. zur Abschätzung von Laufzeit: -> unten am Rumpf / im .cpp File definieren -> zur Erzeugung / Zerstörung der Objekte int search(int k){ node = list; //node von Aufbau int count = 1; //Start bei 1 while(node){ //nicht leer? if(node->key == k) return count; node = node->next; count++;} NEUER KNOTEN DAZWISCHEN p zeigt auf ein Element in der Liste. Neues Element mit Key k und Zeiger q hinter dem Element auf das p zeigt einfügen: void insert(int k, tNode* p){ tNode *q = new tNode; q->key = k; q->next = p->next; p->next = q;} BINARY TREES Eine Liste mit jeweils 2 Nachfolgern. Dem obersten Knoten sagt man Wurzel (root). Alle Knoten (node) die am Ende des Baums hängen werden Blatt (leaf) genannt. Höhe eines Baums = maximale Anzahl Knoten zwischen root und leaf. Folgendes Element links: kleiner als Knoten, rechts grösser. struct tNode { int key; tNode *left, *right; }; DOPPELT VERKETTETE LISTEN EINFÜGEN Jeder Knoten speichert nicht nur den Nachfolger sondern auch den Vorgänger. Füge neuen Knoten mit Key k ein. Rekursive Methode. void insert(tNode *p, int k){ if(p==0{ p = new tNode; p->key = k; p->left = NULL; p->right = NULL;} else if(p->key > k) insert(p->left,k); else insert(p->right, k);} struct tNode { int key; list *next, *prev; }; Vorteil: Man kann einfacher einfügen und löschen. Nachteil: Die Datenstruktur ist komplexer. DYNAMISCHE DATENSTRUKTUREN STACK Der Stack funktioniert nach dem LIFO-Prinzip. Last In First Out bedeutet, dass alte Daten immer weiter nach unten geschoben werden, weshalb der Stack auch als Stapel oder Kellerspeicher bezeichet wird. Man kann den Stack als einfach verkettete Liste betrachten. Jedes Element hat einen Wert val und einen Zeiger next: void push(int value){ element* el; el=new element; el->val=value; if (top){el->next=top; top=el;} else {top=el; el->next=0;}} bool isempty(){ if (top) return 0; else return 1;} int pop(){ if (top){ int ret; element* del; ret=top->val; del=top; top=top->next; delete del; return ret;} else{ cout<<"The stack is empty."<<endl; return -1;}} int size(){ int counter=0; element* tmp=top; while (top){ counter++;top=top->next;} top=tmp; tmp=0; //top = top(alt) return counter;} Aufruf: tNode *root = NULL; insert(root,2); SUCHEN Iterative Methode. Rückgabewert ist ein Pointer. tNode* search(tNode *root, int k){ tNode *p = root; while(p){ if(p->key == k) return p; if(p->key > k) p = p->left; else p = p->right;}} Aufruf: tNode *x = search(root,2); LÖSCHEN Beim Löschen eines leafs: 1.) Pointer auf leaf 2.) Ast = NULL 3.) über Pointer leaf löschen 4.) Pointer löschen Beim Löschen eines Knotens (kein leaf) gibt es mehr Schwierigkeiten: 1.) Man muss einen Folgeknoten auswählen, um den gelöschten Knoten zu ersetzen. Man könnte zählen, welcher Ast mehr Knoten hat und dann diesen nehmen, um der Degenerierung vorzubeugen. 2.) Bevor man diesen jedoch anhängt und den anderen Knoten löscht, sollte man den inneren Ast des Astes, der nach oben gezogen wird umhängen (Elementweise, Rekursion?). DEGENERIERUNG - - Fügt man die Knoten sortiert ein, so degeneriert der Baum zu einer einfach verketteten Liste, wobei jeder 2te Pointer ein Nullpointer bleiben wird. Lösung: zufälliges einfügen. Best case: height ≈ log2(#Knoten) Worst case: height = #Knoten = Länge der einfachen Liste -> bei Degenerierung for(int i=0; i<N; i++) { // for(int j=0; j<M; j++) { // cout << “hello”; //1 cout << endl; //1 } } //Laufzeit = (∗∗(1+1)) (∗) Allgemein: Konsanten << N wegstreichen. Wenn die Länge halbiert wird (Suchalgorithmen) meistens 2. MIT O RECHNEN : f wächst höchstens so schnell wie g Formal: lim ∞ (ev. Bernoulli benutzen) Oder: f wächst asymptotisch mindestens so schnell wie g, falls es ein c>0 gibt, so dass In diesem Fall schreibt man Bsp: , " 7" ist " $ lim %& lim '% ( ∞ → stimmt nicht BEISPIELE VON LAUFZEITEN Algorithmus Bubblesort Selectionsort Insertionsort Mergesort Quicksort Suchen ) Sortieren ) avg case worst case " " " " " " · log " · log " · log " " … in Listen, unsortierten Arrays … in sortierten Arrays … in Binärbäumen (Idealform) classname::classname(int a, int b){ v1 = a; v2 = b/1.5;} Destruktor bei dyn. alloziertem Speicher benötigt. (Variabeln sind Pointer) Konstruktor -> new verwenden: classname::~classname(){ delete var1; delete var2;} Standardkonstruktoren können ohne Parameter aufgerufen werden. Er setzt dann Defaultwerte ein. Entweder wird ein zweiter Konstruktor definiert, der keine Parameter braucht, oder aber im Prototyp werden die Defaultwerte bestimmt. classname::classname(){ //Version 1 v1 = 0; v2 = 0;} //mit Überladen classname(int=0,int=0); //Version 2 classname::classname(int a, int b){ v1 = a; //oben: Prototyp v2 = b/1.5;} //mit Defaultwerten ELEMENTFUNKTIONEN best case " · log " · log " log " log " KLASSEN Eine Klasse ist eine Datenstruktur. Man kann damit Daten und Funktionen (= Methoden) verwalten. Zugriffsrechte: public: von überallher zugreifbar, wo Klasse bekannt ist. private: (default) nur von innerhalb der Klasse und von friends zugreifbar protected: nur abgeleitete Klassen dieser Basisklasse haben Zugriff class classname { private: int v1; double v2; public: classname(int,int); //constructor ~classname(); //destructor friend float f1(int,int); //(1) friend classname f2(classname);//(1) void f3(int); //(2) } objectnames; Prototypen von… (1) friend Function (2) Elementfunktion Im Rumpf steht der Prototyp, unterhalb wird die Funktion wie folgt definiert: void classname::f3(int a){ cout<<a*v1*v2<<endl;} float f1(int a, int b){ int c = a*b; return a*c+b*c;} classname f2(classname input){ classname output; output.v1 = input.v1*2; output.v2 = input.v2/3.14; return output;} Die Funktion f3 ist eine Funktion der Klasse, f1 und f2 sind befreundete Funktionen, die kein clname::fx haben. AUFRUFE classname oname; //-> standardkonstrukt classname oname(4,1.3); classname oname = classname(4,1.3); oname.f3(6); //-> object function x=f1(7,1); //-> friend function this ist ein Zeiger auf das aktualle Objekt, so kann gezeigt werden, dass man nicht auf eine globale Variabel, sondern auf diejenige in dieser Klasse zugreifen möchte: classname::classname(int x, int y) { this->x = x; this->y = y; } Gibt es eine Variabel x und y innerhalb des Objekte wie auch ausserhalb oder werden die Paramerter wie im Beispiel auch so benannt, braucht man this, um Verwechlungen zu verhindern. FRIEND CLASS Friend class B einer Klasse A, d.h. ihre Funktionen können auf die privaten Elemente der Klasse A zugreifen: class classA; //forward declaration class classB{ int b1, b2; //per default private public: void function (classA); //(1) }; class classA{ float a1, a2; public: friend class classB; //(2) }; In diesem Beispiel braucht es (2), damit function (1) aus B ein Objekt von A verwenden kann. A muss vor (1) schon deklariert sein, deshalb die forward delcaration. ÜBERLADEN VON OPERATOREN Durch Operatorfunktionen kann man Operatoren wie +, -, *, %, … überladen, d.h. ihnen je nach Parameteranzahl, -typ eine neue Funktion zuweisen. Global: a + b ist eine Kurzform für a.operator+(b); oder operator+(a,b); In einer Klasse: Die Funktion operator+() erlaubt es dem Operator + neue Bedeutung zuzuweisen. Bsp: tBruch tBruch::operator+(long s){} tBruch tBruch::operator+(char s){} Je nachdem ob s ein long oder ein char ist wird in der Klasse tBruch die entsprechende Operatorfunktion aufgerufen. Aufruf: bruch = bruch.operator+(s); bruch = bruch + s; REIHENFOLGE DER OPERANDEN Die Reihenfolge der Operanden kann eine wichtige Rolle spielen. In diesem Bsp hat der Operator nur ein Argument, dieses kann float oder int sein, aber nicht ein Objekt der Klasse tRect. rectb=recta*1.5; rectc=recta*3; //o.k rectd=3*recta //->Fehler Abhilfe durch neue Operatorfunktion (mit 2 Operanden) : tRect::operator*(float s,tRect r){…} OOP STRING Objektorientiertes Programmieren = Arbeiten mit Klassen Idee: Daten und Funktionen (=Methoden) in einem problemspezifischen Objekt zusammenfassen -> Klassen Header: Beschreibung: Arbeiten mit mehreren Files: Übersichtilicher, auf verschiedene Personen aufteilbar. Headerfiles (.h) und Programfiles (.cpp) werden verbunden, zu Objektfiles (.o) kompliliert, gelinkt und ausgeführt. Elementfunktionen: length() liefert die Länge des Strings insert(n,s) fügt den String s aan Position n ein erase(p,n) entfernt n Zeichen ab Position p find(s) Liefert die Position von s begin(),end() end() zeigt hinter das letze El. rbegin(),rend() bei Rückwärts-Iteration Iteration TEMPLATES Ein Template ist wie eine Schablone für eine Klasse oder eine Funktion. Definiert man sein Klasse oder Funktion als Template, braucht man diese nicht mehrere Male zu schreiben. Funktion ohne Template: void swap(int& a, int& b) {int c=a; a=b; b=c;} //nur für int Funktion mit Template: //nicht nur für int template <class T> void swap(T& a, T& b) {T c=a; a=b; b=c;} Klasse ohne Template: //nur für int class tStack { int index; int *s; public: tStack(){s=new int[256];index=0} ~tStack(){delete[] s;} Bool push(int); }; Klasse mit Template: //nicht nur für int template <class T> class tStack { int index; T *s; public: tStack(){s=new T[256];index=0} ~tStack(){delete[] s;} Bool push(T); }; T kennzeichent den noch unbekannten Typ; überall wo dann dieser Typ vorkommen soll wird T geschrieben. Anstelle von <class T> kann man auch <typename T> schreiben. VERERBUN G STL :: STANDARD TEMPLATE LIBRARY class classname : public baseclass {…}; Die STL ist eine Bilbliothek von Templates, unter anderem für Container. Sequenzielle Container: vector, list, deque. Assozialtver Container: map. Neben Container gibt es auch Iteratoren, Algorithmen u.v.m. Die Klasse classname besitzt alle Elemente der Basisklasse baseclass plus die neuen Elemente. Als Beispiel die Basisklasse Polygon und ihr „Kind“: class tPolygon{ protected: int width, height; public: void set_values (int a, int b){…}; }; class tRectangle : public tPolygon{ public: int area(){return(width*height);} }; Iteratoren Bsp für Ramdom Access Iteratoren: vector, deque, string Bsp für bidirectional access Iteratoren: list, map DEQUE #include <string> -> siehe c++ strings. Nur so viel Speicher wie nötig. Header: #include <deque> Beschreibung: Double ended queue. Wie Vektor, aber Einfügen an beiden Enden. Für Warteschlangen geeignet (FIFO) (FIFO), besser als List. Elementfunktionen: empty(),size(),resize(n) capacity(),reserve(n) deque_name[],at,front,back _name[],at,front,back push_back(s),push_front(s)... push_front(s)... begin(),end(),rbegin(),rend() rbegin(),rend() Beispiele: string s(“154la6“); // constructor s.erase(3,2); // -> 1546 int pos = s.find(“46“); // -> > 2 string::iterator i; for r (i=s.begin();i!=s.end();i++) {cout *i;} Achtung: Positionen ab 0: “dochno“ ->> p(‘h‘) = 3 Beispiel (FIFO-Puffer): deque<int> puffer; for(int i=0;i<100;i++) //füllen {puffer.push_front(i);} do //leeren {puffer.pop_back(); }while(puffer.size()); while(puffer.size()); VECTOR Header: #include <map> Beschreibung: Assoziativer Container: gut zum suchen. Mit Key, der das Element eindeutig identifiziert. Für Telefonbücher u.a. Header: #include <vector vector> Beschreibung: Dynamisches Array.. Es kann leicht am Ende eingefügt / entfernt werden. Elemente im Speicher zusammen. Elementfunktionen: empty(),size(),resize(n) capacity(),reserve(n) vec_name[],at,front,back push_back(s),pop_back() begin(),end(),rbegin(),rend() rbegin(),rend() merge,sort,reserve Beispiele: vector<int> vec1; // empty vector vector<int> vec2(5); // [0,0,0,0,0] vector<int> vec3(3,5); // [3,3,3,3,3] vec2.at(6)=12; // prüfen ob möglich vec2[7]=10; // nicht möglich vec.resize(8); // Grösse erhöhen vec2[7]=10; // jetzt möglich vec2.reserve(20) // Kapazität neu 20 LIST MAP Elementfunktionen: empty(),size(),max_size( max_size() map_name[] find,count,find,erase,lower_bound begin(),end(),rbegin(),rend() rbegin(),rend() Beispiele (Autokennzeichen)): map<string,string> Kfz Kfz; Kfz[”ZH”] z[”ZH”] = ”Zuerich”; Kfz[”AG”] = ”Aargau”; ”; cout<<Kfz[”AG”]<<endl; <<endl; cout<<Kfz.size()<<endl; if (Kfz.find(”KL”)==Kfz.end()) )==Kfz.end()) {cout<<“not in map“<<endl; map“<<endl;} Beispiele (bidirektionale bidirektionale Iteratoren Iteratoren): map<Key,T>::iterator ::iterator it it; it->first; //key value it->second; //mapped value for(it=Kfz.begin();it!=Kfz.end();i++){} STACK Header: #include <list list> Beschreibung: Doppelt verknüpfte Liste; elemente irgendwo im Speicher; über Zeiger verbunden.. Einfügen irgendwo gut möglich. Elementfunktionen: empty(),size(),resize(n) front,back push_back(s),push_front(s),pop_back() ,pop_back() begin(),end(),rbegin(),rend() rbegin(),rend() merge,sort,reserve Beispiele: list<int> list1; // constructor list<int>::iterator it; // iterator for(int i=0;i<10;i++) {list1.push_back(rand()%9+1);} for(it=list.begin();it!=list.end();it++) {cout<<*it<<” ”;} // anzeigen Header: #include < <stack> Beschreibung: Container Adapter stellen für andere Container spezielle Schnittsteler zur Verfügung. Funktionen push_back() und pop_back() und pop() müssen vorhanden sein. ->> Stacks können auf vector, deque und list basieren. QUEUE / PRIORITY QUEUE Header: #include < <queue> Beschreibung: Queue kann auf deque (default) und list aufbauen. Bei Priority queue haben die Elemente zusätzlich eine Priorität. Priority queue kann auf vector und deque aufbauen. FIFO. #include <queue> #include <list> queue<int,list<int>> > qu;//queue auf list