• Keine Ergebnisse gefunden

C++ ZUSAMMENFASSUNG Christian Forster, 28. August 2007 cforster@student.ethz.ch Patrik Rohner, 15. Juli 2008 rohnerpa@student.ethz.ch

N/A
N/A
Protected

Academic year: 2021

Aktie "C++ ZUSAMMENFASSUNG Christian Forster, 28. August 2007 cforster@student.ethz.ch Patrik Rohner, 15. Juli 2008 rohnerpa@student.ethz.ch"

Copied!
8
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

C++ ZUSAMMENFASSUNG

Christian Forster, 28. August 2007 cforster@student.ethz.ch Patrik Rohner, 15. Juli 2008 rohnerpa@student.ethz.ch

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;

//structs, functions, enums

int main(void) { system(“pause”);

return 0;

}

VARIABELN

int 32bit . positive und negative ganze Zahlen range: -231 bis 231-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) VARIABELNNAMEN

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

Änderung einer Variable in einen anderen Typen.

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

Enum ist ein Aufzählungstyp. Die Konstanten aus der Enum kann man im Programm verwenden.

enum farbe {ROT, BLAU, GELB};

farbe f = ROT;

if(f != BLAU) { };

HEXADEZIMALER CODE & ADRESSEN

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+3…

int, float (4byte = 32bit) double (8byte = 64bit)

0x22ff70 0x22ff70

0x22ff74 0x22ff78

0x22ff78 0x22ff80

0x22ff7c 0x22ff88

FLIESSKOMMAZAHLEN

Float 1bit->sign, 8bit->exponent, 23bit->mantisse Wert = (-1)S x 2(E-127) x (1.F)

Bsp1: 0.125 = 2-3 » S -> 0, E -> 124, F -> 0 0|01111100|00000000000…0 = 0.125 0|01111111|00000000000…0 = 1 1|01111111|11000000000…0 = -1.75 0|00000000|00000000000…0 = 0 0|11111111|00000000000…0 = +infty 0|00000000|10010101110… = NaN Double 1bit->sign, 11bit->exponent, 52bit->mantisse

Wert = (-1)S x 2(E-1023) x (1.F) OPERATOREN

+ - / * ^ mathematische Operatoren

% ganzzahliger Rest einer Division 15%6==3 x += i x = x + i; ebenso *=, /=, -=

1.1E-5 = 1.1*10-5

i++, i-- 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()

LOGISCHE KONSTRUKTE

<, <=, >, >= grösser, grössergleich, kleiner

|| “oder”

&& “und”

== “gleichheit”

!= “ungleich”

! “nicht”

EINGABE & AUSGABE

cout << “a = “ << endl; //Ausgabe

cin >> a; //Eingabe

\n Zeilenende

\t horizontaler Tabulator

\” Anführungszeichen

UMRECHNUNG BINÄR – DEZIMAL

13 1 Dezimalzahl durch 2 teilen und Rest 6 0 notieren. Bits von unten nach oben

3 1 lesen.

1 1 Bsp: 13 = 1101

0

(2)

1001 = 1*23 + 0*22 + 0*21 + 0*20 = 8+0+0+1 = 9 KONTROLLSTRUKTUREN

IF

if(a==10){

b=15;

}

else if(a==11) b=14;

else b=10;

oder kurz: a==10?b=15:a==11?b=14:b=10;

FOR

for(int i=0; i<10; i++) { a=a+i;

}

abbrechen mit break;

WHILE

while(b<20){

b++;

} oder do {

b--; //mindestens 1 x }

while(b!=15);

abbrechen mit break (immer nur die innere 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”; //else }

ÄQUIVALENTE STRUKTUREN

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

…ist äquivalent zu…

for(int i=0;i!=10;i++){}

BEISPIELE FÜR ENDLOSSCHLEIFEN int i=10;

do { i=i+1;

if (i==10) break;

}while(true);

for(int i=3;i!=20;i=(i+3)%300) int i=99;

while(i>10){

i--;

if(i==15) i*=6;}

ARRAYS

int Array mit 4 Zellen: int a[4];

Inhalte definieren: a[0] = 1;

int a[4] = {1,2,3,4};

2D Array (Matrix): int b[3][2] = {{1,2},{

3D Array: int 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ändert werden.

Ein Array beginnt immer mit a[0] und endet mit a[N-1]

Übergibt man ein Array einer Funktion, ist das wie “Call by Reference”. Das Original-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:

int c[10]; //Array definieren int* pc; //Pointer definieren pc = c; //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 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;

Falsch: struct falsch {int i;falsch x;};

Strichpunkt am Ende nicht vergessen:

struct point {int i;double y;};

FUNKTIONEN IN STRUCTS

Die Konstruktor-Funktion wird bei der Generierung eines neuen Structs aufgerufen.

struct Bar {

Bar() { //Konstruktor } };

Funktionen können auch ausgelagert werden:

struct Bar { void bier(); } bqm;

void Bar::bier(){ };

(3)

Aufrufen der Funktion: bqm.bier();

FUNKTIONEN

Ermöglichen Aufspaltung des Programms in Unterprogramme.

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: 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-by- value 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 struct st{int a,b};

st swap1(int a, int b){

int c=a; a=b; b=c;

st ret={a,b};

return ret;}

int main(){

int x=2, y=3;

st out = swap1(x,y); //mit structure x=out.a; y=out.b;… //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 Ein Funktionsaufruf swap2(x,y);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.

CALL BY REFERENCE (DYNAMISCH)

Hier wird statt der Referenz ein Pointer auf das Objekt übergeben. Damit auf das Objekt zugegriffen werden kann, muss der übergeben Pointer dereferenziert werden.

void swap3(int* a, int* b) {int c=*a; *a=*b; *b=c;}

int main(){

int x=2, y=3;

swap3(&x,&y);… //vertauschen Aufruf: swap3(pa,pb); wenn 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 a, int b){…}

Aufruf: swap3(*pa, *pb); Das wirkt dann aber wie call-by-value und macht hier keinen Sinn.

REKURSION

Der retu rn 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-1);}

Aktive Funktion belegt Speicherplatz 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”;

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.

Das Array hat die Länge n+1. a[n] = O Bit, Abschluss, ‘/0’

text[i] liefert das i-te Zeichen (int) text[i] liefert den ASCII Code

des i-ten Zeichens int strlen(char text[]) liefert Länge ohne “\0”

char(65) -> A (aus ASCII Tabelle) liest n-1 Zeichen von der Tastatur in str[] und hängt “\0”

an: void cin.getline(char str[], int n);

C++ - STRINGS

In C++ neue Klasse, benötigt #include <string>

Überladene Operatoren in der string-Klasse (+,…), siehe Bsp string myname, yourfname, yourlname;

myname = ”dick banger”

cout << ”please enter your name ”;

(4)

cin >> yourfname >> yourlname;

if (yourfname +" "+ yourlname == myname) {cout << ”\n What a coincidence!”;}

ASCII TABELLE

00-31: NUL,… 48-57: 0-9 97-122: a-z 32: SPACE 65-90: A-Z 127: DEL POINTER

Ein Pointer speichert und zeigt auf eine 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; //Pointer der auf int int *pb; //zeigen soll 0 (NULL) pb = 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->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];

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* 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;

DYNAMISCHE SPEICHERALLOZIERUNG Mit new kann man einen Pointer und neues Objekt erzeugen. Bei Arrays muss die Grösse nicht mehr const sein.

double *dp = new double;

int *ip = new int(3); //*pd=3 int *pe = new int[n]; //dyn. Array Falls kein Speicher vorhanden -> Fehler. Abhilfe:

delete dp = new (nothrow) double [n];

if (dp == 0) cout<<”Error mem alloc”;

else {…} //kein Programmabbruch Array vergrössern/Löschen:

int*aa = new int[2*n]; //neues Array aa for (int i=0;i<n;i++) //kopieren von a

{ //nach aa

aa[i]=a[i];

}

delete[] a; //löschen von a a=aa; n=2*n; aa=NULL; //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 auf const. int const int* const ip //beides

POINTER AUF STRUCTS

struct triple {int 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 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 = node //anhängen

last = last->next //neuer l. Knoten

(5)

Beim Aufbau der List mit Anhängen der Knoten am Anfang geht man gleich vor, nur umgekehrt: node->next=list (nicht mehr 0) verknüpft den neuen Knoten, …

LINEARE SUCHE

Suchfunktion nach Key k, mit Zähler count (Rückgabewert):

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;}

DOPPELT VERKETTETE LISTEN

Jeder Knoten speichert nicht nur den Nachfolger sondern auch den Vorgänger.

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;}

HEAP

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.

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;

};

EINFÜGEN

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);}

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.

(6)

- Lösung: zufälliges einfügen.

- Best case: height ≈ log2(#Knoten)

- Worst case: height = #Knoten = Länge der einfachen Liste -> bei Degenerierung

O - NOTATION

zur Abschätzung von Laufzeit:

for(int i=0; i<N; i++) { // O(N)

for(int j=0; j<M; j++) { // O(M)

cout << “hello”; //� (1)

cout << endl; // O(1)

} }

//Laufzeit = �(�∗�∗(1+1)) ¿ �(�∗�) Allgemein: Konsanten << N wegstreichen. Wenn die Länge halbiert wird (Suchalgorithmen) meistens

O(log 2(n)) . MIT O RECHNEN

f(n)∈O

(

g(n)

)

: f wächst höchstens so schnell wie g

Formal: lim

n →∞

|

gf(n)(n)

|

< (ev. Bernoulli benutzen) Oder: f wächst asymptotisch mindestens so schnell wie g, falls es ein c>0 gibt, so dass g(n)≤O(f(n))

In diesem Fall schreibt man g ≤O(f)

Bsp: f1=n3, f2=7n2 ist f1<O(f2)

|

7nn35

|

=¿n→∞lim

(

n7

)

=∞

n →∞lim¿

→ stimmt nicht

BEISPIELE VON LAUFZEITEN Sortieren O(⋯)

Algorithmus avg case worst case best case

Bubblesort N2 N2 N

Selectionsort N2 N2 N2

Insertionsort N2 N2 N

Mergesort N ∙log2N N ∙log2N N ∙log2N

Quicksort N ∙log2N N2 N ∙log2N

Suchen

O(⋯)

… in Listen, unsortierten Arrays N

… in sortierten Arrays log2N

… in Binärbäumen (Idealform) log2N

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 KONSTRUKTOREN / DESTRUKTOREN

-> unten am Rumpf / im .cpp File definieren -> zur Erzeugung / Zerstörung der Objekte

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

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

(7)

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){…}

VERERBUNG

class classname : public baseclass {…};

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);} };

OOP

Objektorientiertes Programmieren = Arbeiten mit Klassen Idee: Daten und Funktionen (=Methoden) in einem problemspezifischen Objekt zusammenfassen -> Klassen Arbeiten mit mehreren Files: Übersichtilicher, auf verschiedene Personen aufteilbar. Headerfiles (.h) und Programfiles (.cpp) werden verbunden, zu Objektfiles (.o) kompliliert, gelinkt und ausgeführt.

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: //nur für int void swap(int& a, int& b)

{int c=a; a=b; b=c;}

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.

(8)

STL :: STANDARD TEMPLATE LIBRARY

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.

Iteratoren

Bsp für Ramdom Access Iteratoren: vector, deque, string Bsp für bidirectional access Iteratoren: list, map

STRING

Header: #include <string>

Beschreibung: -> siehe c++ strings. Nur so viel Speicher wie nötig.

Elementfunktionen:

length() liefert die Länge des Strings

insert(n,s) fügt den String s an 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 Beispiele:

string s(“154la6“); // constructor s.erase(3,2); // -> 1546 int pos = s.find(“46“); // -> 2 string::iterator i;

for (i=s.begin();i!=s.end();i++) {cout *i;}

Achtung: Positionen ab 0: “dochno“ -> p(‘h‘) = 3 VECTOR

Header: #include <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()

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

Header: #include <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()

begin(),end(),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 DEQUE

Header: #include <deque>

Beschreibung:

Double ended queue. Wie Vektor, aber Einfügen an beiden Enden. Für Warteschlangen geeignet (FIFO), besser als List.

Elementfunktionen:

empty(),size(),resize(n)

capacity(),reserve(n)

deque_name[],at,front,back

push_back(s),push_front(s)...

begin(),end(),rbegin(),rend() 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());

MAP

Header: #include <map>

Beschreibung:

Assoziativer Container: gut zum suchen. Mit Key, der das Element eindeutig identifiziert. Für Telefonbücher u.a.

Elementfunktionen:

empty(),size(),max_size()

map_name[]

find,count,find,erase,lower_bound

begin(),end(),rbegin(),rend() Beispiele (Autokennzeichen):

map<string,string> Kfz;

Kfz[”ZH”] = ”Zuerich”;

Kfz[”AG”] = ”Aargau”;

cout<<Kfz[”AG”]<<endl;

cout<<Kfz.size()<<endl;

if (Kfz.find(”KL”)==Kfz.end()) {cout<<“not in map“<<endl;}

Beispiele (bidirektionale Iteratoren):

map<Key,T>::iterator it;

it->first; //key value it->second; //mapped value

for(it=Kfz.begin();it!=Kfz.end();i++){}

STACK

(9)

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

Referenzen

ÄHNLICHE DOKUMENTE

Verfahren zum Färben oder Ausrüsten von textilen Fasermaterialien, insbesondere Teppichmaterial, mit Hilfe von Schaum, wobei man eine verschäumte

Partitioning algorithms fix the number k of desired clusters first, choose k starting points (e.g. randomly or by sampling) and assign the patterns to the closest cluster....

ZBINDEN Zwischenbericht über ein von der GDPTT unterstütztes Forschungsprojekt:. MITLAUFENDES

From Galois theory we know that the intermediate fields of a finite separable and normal field extension F of a field E are in bijection with the subgroups of the group of

Weitere Infos: blueballs.ch 18.30 Stattkino, Löwenplatz 11, Luzern Heart of a Dog &amp; Was soll man noch werden, wenn man gross

Musikalisch-literarisches Treffen von Wilhelm Tell und Richard Wagner 21.00 Stattkino, Löwenplatz 11, Luzern freier fall. von Stephan Lacant, Deutschland 2013, 100

In einem 50 ml Einhalskolben mit Rückflusskühler und Trockenrohr werden unter Rühren 60 mmol (5.21 g) wasserfreies Lithiumbromid in 15 ml trockenem Ethanol gelöst, mit 30.0 mmol

Durch portionsweise Zugabe von 0.20 mol (4.60 g) Natrium durch eine Schlifföffnung (nach der Zugabe sofort wieder verschließen!) wird eine Alkoholatlösung bereitet.. Das Natrium