C++ ZUSAMMENFASSUNG MENFASSUNG

Werbung
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
Herunterladen