Informatik - Zusammenfassung

Werbung
Informatik – Zusammenfassung
von Norman Juchler, D-MAVT, Informatik SS04. Verfasst anhand der Vorlesungsfolien von Prof. Dr. Markus Bläser
Schlüsselwörter in C++
Allgemeines
C++: Maschinennahe Sprache, ermöglicht imperative Programmierung. asm, auto, bool, break, case, catch, char, class,
Bestehend aus Folge von “elementaren” Operationen, mit const, continue, default, delete, do, double, else,
enum, extern, float, for, friend, goto, if, inline,
Variablenkonzept
Algortihmus:
Rechenvorgang, der nach einem bestimmten [sich
wiederholenden] Schema abläuft
Aufbau eines C++-Programms:
int, long, new, operator, private, protected, public,
register, return, short, signed, sizeof, static,
struct, switch, template, this, throw, try, typedef,
union, unsigned, virtual, void, volatile, while
Importliste  Programmteil
mathematische Operatoren
Syntax
+
„Grammatik“. Beschreibt korrekten Aufbau in C++. Schlüsselwörter
-
(Einheiten)  “elementaren Grundbausteine” von C++:
Semantik
Interpretation. Beschreibt Bedeutung und Inhalt eines (syntaktisch
korrekten) C++-Programms.
Beispiel
Header:
#include<iostream>
using namespace std;
Einbindung einer Headerdatei, Zugang zu (vordef.) Hilfsprogrammen,
Compiler ersetzt Zeile durch Inhalt d. Datei iostream.
Funktionsdeklaration:
void main(void){
Ausführung des Programms: Beginn immer bei main.
Semantik:
1. void: kein Wert wird zurückgegeben
2. void: main erhält keine Werte von aussen
Variablendeklaration:
double a, s, t;
Syntax:
Typname Variablennamen-Liste;
Typname: vordef. Typen: int, float, double,
Addition
*
Multiplikation
%
modulo
Subtraktion
/
Division
(nur int!)
Für weitere mathematische Funktionen: #include math.h:
fabs  Absolutbetrag, sqrt  Q.wurzel, exp, log, cos, acos
Beispiel
#include<iostream>
using namespace std;
void main(void){
double a, t, s;
cout << "a? "; cin >> a;
cout << "t? "; cin >> t;
s = 0.5 * a * t * t;
cout << "s = " << s << endl;
}
Beispiel Rechnen mit einfachen Datentypen
int:
Bemerkung:
float:
3.0f, 3.0F
char:
char c, d; c = ’A’; d = ’+’
Einige Ersatzdarstellungen für char:
Physikalisch wird Variable durch Folge von
Speicherzellen realisiert. Erste Zelle ist die Adresse,
der Inhalt der Wert der Variablen.
Namensgebung: Nur aus Buchstaben, Ziffern und
‚_’. 1. Zeichen: Buchstabe oder ‚_’.
bool:
Variabeln müssen initialisiert/dekl. werden!!!
Wertzuweisung
Syntax:
s = 0.5 * a * t * t;
Variablenname = Ausdruck ;
Semantik:
Bemerkung
Auswertung: v. links nach rechts, übliche Prioritäten
Typ der Variable = Typ des Ausdrucks! Sonst:
Konvertierung (cast), passiv oder aktiv.
(x / y) * y + (x % y) = x
double: 3.14, -1.5, .3, -1.1E-5, 1.1e6, .3 steht für 0.3
char, abgeleitete Typen (structs) oder Pointer.
Semantik:
es gilt allgemein:
log. Verknüpfungen: Und (&&), Oder (||), Negation (!).
Einfache Datentypen
int:
[-2147483648,2147483647]  N : (232 Werte)
es gilt: (x / y) * y + (x % y) = x
float, double: modelliert reelle Zahlen, Gleitkommadarstell.
Speicher: float  4 Byte, double  8 Byte
char:
speichert einzl. Buchstaben, Ziffern, Zeichen
bool:
Wahrheitswerte true, false. Verknüpfungen
Vergleichsoperatoren
Vergleiche von int, double, etc.
Typ des Rückgabewerts: bool
Rechenregeln für boolesche Operatoren
Inkrement, Dekrement
Vereinfachung:
Statt i = i + 1; kann man i++ schreiben.
Auch in Ausdrücken verwenden:
y = x * (i++);
Semantik:
Explizite Typumwandlung (cast)
Syntax:
Typname ( Ausdruck ) oder
(Typname ) Ausdruck
Nicht expl. Umwandlung möglich: 8ung! Rundung!
Beispiele:
x wird mit i multipliziert und y zugewiesen.
Danach wird i um 1 erhöht. Umgekehrt:
y = x * (++i);
Analog:
i--, --i
while-Schleife:
Syntax:
while ( Bedingung ) Block
Semantik:
Durchlauf des Blocks, wenn Bedingung true
Nach jedem Durchlauf des Rumpfs erneuter Test
der Bedingung,
Kontrollstrukturen
do-Schleife:
if-Anweisung
Syntax:
do Block1 while ( Bedingung )
Syntax:
if ( Bedingung ) B1 else B2
Semantik:
Block B1 wird ausgeführt, wenn Bedingung true Semantik:
…ist semantisch identisch mit:
ist, sonst B2.
Block1 ; while ( Bedingung ) Block1
Block1 wird vorher ausgeführt!
Block :
Anweisung ; oder { Anweisungsfolge ; }
Bemerkungen:
Bedingung muss Typ bool haben.
break, continue
else bezieht sich auf das letztgenannte if, zu dem
break verlässt die innere, momentane Schleife (for, while, donoch kein else gehört. Schachtelung möglich.
while) unmittelbar
Fallunterscheidung:
Syntax:
switch ( Ausdruck ) {
continue überspringt den Rest des Rumpfs der Schleife und geht zur
nächsten Auswertung der Bedingung
case Konst1 : Anw.folge1 ; break;
...
Arrays (Felder)
case Konstn : Anw.folgen ; break;
Syntax:
Typname Variablenname [ Feldgröße ];
Semantik:
Feldgrösse konstant w (muss). w viele Variablen
default: Anweisungsfolgen+1 ;
}
vom Typ Typname werden angelegt.
Semantik:
Ausdruck muss eine ganze Zahl ergeben.
Konst1,…, Konstn paarweise verschieden!
Zugriff:
Konst1,…, Konstn ganzzahlig
default kann fehlen, muss nicht am Ende stehen.
Vereinfachung:
break beendet switch-Anweisung.
Fehlt break nach Anw.folgei , so wird auch
Schleifen:
for-Schleife:
for ( Initialisierung ; Bedingung ; Anhang )
„Dynamik“:
Initialisierung wird einmal vor erstem Durchlauf der
Schleife ausgeführt.
Dynamische Deklaration von Arrays:
a = new int[n];
Block
Semantik:
Statt: int a[10]; besser:
const int max_groesse = 10;
int a[max_groesse];
 max_groesse wird als symb. Konstante dekl.
Vorteil: Änderungen einfacher und effizienter.
Symb. Konstanten angewendet wie Variablen,
Initialisierung zwingend, nie links bei einer
Zuweisung!
Anw.folgei+1 ,…, ausgeführt, bis zum nexten break
Syntax:
Variablenname [0],…, Variablenname [w -1]
Achtung: Arraygrenzen werden nicht kontroll.
 Arrays werden mit Pointer realisiert!
Bemerkungen:
Mehrdimensionale Arrays möglich:
double matrix[100][100];
double tensor[30][20][50];
Bedingung wird vor jedem Durchlauf ausgewertet.
Wird Schleife nicht verlassen  Block ausgeführt.
for-Schleifen gut geeignet, um eine voher bekannte
int a[5] = {1, 2, 3, 4, 5};
int a[5] = {1, 2, 3};
int a[] = {1, 2, 3, 4, 5};
Anzahl von Durchläufen durch eine Schleife zu
(Größe des Arrays wird auf 5 festgelegt.)
realisieren. Ist Bedingung leer  Endlosschleife
int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
Am Ende jedes Durchlaufs  Anhang ausgeführt.
Bemerkungen:
Initialisierung:
Dynam. Arrays können nicht initialisiert werden!
Structs (Rekord)
Daten haben heterogenen Charakter, gehören aber zusammen.
Synatx:
Semantik:
Beispiel:
Bemerkungen:
struct Name { Komponentenliste };
Geltungsbereiche: Block, Programmdatei, Funktion, Klasse.
Block
Ein Block wird durch geschweifte Klammern { ... } begrenzt.
Blöcke können geschachtelt sein. Ist ein Block A innerhalb eines
Blocks B, so ist A innerer Block und B äußerer Block.
Neuer Typ struct Name wird deklariert.
Zugriff auf Komponenten mit Punktoperator
Globale und lokale Namen
struct point {
int x, y;
double intensity; };
struct point p;
i = p.x; p.intensity = 0.65;
p.x = p.y;
Geltungsbereich eines Namens beginnt im Deklarationspunkt und endet
am Blockende.
Variablen direkt mit struct deklarieren:
struct point { ...} p, q; oder point p;
Aber: Innerhalb Rekorddekl. keine Initialisierung!
Keine rekursive Typdekl.! Aber: mit Pointer können
Rekordtypen aufeinander Bezug nehmen.
Structs sind schachtelbar.
global:
wichtig:
Name kommt in keinem Block, usw. vor.
Name nie mehrfach deklarieren in einem Block,
ausser in einem inneren Block. Lokale (innere)
Namen haben Vorrang  Verdeckung
Bezugsoperator:
unärer Bezugsoperator ::  Bezug auf gleichnamige globale Variable.
Sichtbarkeitsbereich = Geltungsbereich ohne inneren Teile, in denen der
Name überdeckt wird. Lokale Variablen existieren nur in dem Block, in
dem sie deklariert sind. Bei Blockende gehen lokale Werte verloren.
Beispiele:
Funktionen (Unterprogramme)
Initialisierung:
struct point p = {12, 27, 0.75};
struct point q = {7, 8};
Zuweisung:
p = q; ist gleichbedeutend mit
p.x = q.x; p.y = q.y;
p.intensity = q.intensity;
Übersichtlichkeit steigern durch Funktionen. Erste Strukturierung. 
Aufspaltung des Programms in Unterprogramme.
Lokalitätsprinz.:
(Achtung, bei Arrays ist das nicht so!)
Eine Funktion bildet eine funktionelle Einheit.
Interne Berechnungen ohne (unspezifizierten)
Konsequenzen nach aussen.
Funktionsaufruf: v = f(x, y)  Anw.folge unterbrochen 
Parameterübergabe  interne Berechnung 
Union
Rückgabe des Funktionswerts  Zuweisung an v
Heterogene Daten speichern. Hier aber nur jeweils ein Datum.
Unions selten verwendet. Vorteil: Speicherplatzersparnis. Nur
Param.überg.:
anwendbar, wenn man weiss, dass jeweils nur 1 Komponente benötigt
wird.
 Fortsetzung der Anweisungsfolge.
Beispiel:
union Zahl {
int ganz; double reell;
};
union Zahl z;
z.ganz = 3; speichert 3 als int ab.
z.reell = 3.14; überschreibt die 3
formale Parameter  aktuelle Parameter. S.u.
Argumente der Fkt. bei Fkt.aufruf. Jeder formale
Parameter muss einem aktuellen Parameter gleichen
Typs entsprechen! Zuordnung gemäß Reihenfolge
im Fkt.kopf.
Funktionsdeklaration
Syntax:
Typ Name ( Parameterliste ) Block
Semantik:
Typ Name ( Parameterliste ) ist Funktionskopf
Enum
Parameterliste vereinbart formalen Parameter.
Syntax:
enum Name { Identifierliste };
Beipspiel:
enum Farbe { rot, gruen, blau };
enum Farbe f;
Techn. Realis.:
rot wird durch 0, gruen durch 1, blau durch 2
Block spezifiziert Berechnung der Funktion
Typ legt Typ des Funktionswerts fest
Parameterliste:
repräsentiert. Enums  int
Sichtbarkeitsbereiche
Namen von Variablen haben Sichtbarkeitsbereich (= Geltungsbereich), Typen:
damit Namensgebung übersichtlich bleibt.
formale Param.:
Sichtbarkeitsbereich einer Namensdeklaration:
Programmteil, in dem der Name verwendet werden
kann und sich auch auf die Deklaration bezieht.
Funktionswert mittels return zurückgegeben.
(return kann mehrmals vorkommen)
Legt fest: Anzahl der Argumente, deren Typ, deren
Namen im Funktionsrumpf,
Reihenfolge der
aktuellen Parameter. Eine Parameterliste kann leer
sein.
_
einfacher Typ, Structs, Pointer, KEIN einf. Array
verhalten sich im Rumpf wie lokale Variablen.
Sichtbarkeitsbereich: Funktionsrumpf
Geltungsbereich: Kopf und Rumpf
Unterscheidung: Werteparameter (call by value)
Variablenparameter (call by reference)
Beispiele:
Zuweisungen (SS 107f)
Pointer versus Array, Strings
Signatur:
Weglassen der Parameternamen def. Prototyp.
int f(int, double); Sichtbarkeit von f ab ‚;’
Prozedur:
Fkt. kann auch keinen Wert zurückliefern:  Typ
Arrays werden in C++ durch Pointer realisiert! int a[10]; deklariert
a als Pointer auf int und reserviert dann 10 Speicherplätze. So
gesehen können Arrays als Argumente von Fkt. auftreten.
(Call by Reference mit Pointern).
void . return; (ohne Argument) nicht zwingend
Zuweisung:
Referenzen
 Variablen können mehrere Namen haben (alias)
Bedingungen:
Referenztypen müssen initialisiert werden.
Nach Initialisierung keine Änderung möglich.
Beispiel:
Ist a Pointer und b Array, so bewirkt a = b;, dass
a nun auf gleichen Platz zeigt wie b. Keine echte
Kopie  Effizienz!
Auch Strings werden in C als Pointer char* realisiert.
Dynamische Variabeln
int n; int& nr = n;
n und nr können nun new-Operator:
als Synonyme verwendet werden.
zus’en mit Pointer Variablen dyn. erschaffen:
new Typ ; erzeugt neue Variable vom Typ Typ
und liefert Adresse zurück.  Zuweisung zu einem
Variablenparameter (Call by Reference)
Bisher:
void f(int y); { ... }
Durch Fkt.aufruf f(x) wird x nicht verändert.
Jetzt:
Pointer, z.B.: int* p = new int;
Bemerkungen:
Programmstruktur ab ( Speicherplatz wird nicht
automatisch freigegeben!!). Dynamische Variable
nicht explizit benannt, nur über Adresse abrufbar!
void f(int& y) { ... }
Durch Fkt.aufruf f(x) wird x verändert, da nicht
Wert, sondern Adresse von x übergeben!
Vorteile:
Umgekehrt:
Datenrückgabe  Veränderung beabsichtigt.
Effizienter gegenüber „Call by value“ (= Wert wird
kopiert, aufwändig bei grossen Structs).
Keine Veränderung: int f (const int& y);
Geltungsbereich und Lebensdauer hängen nicht von
delete:
werden Variablen nicht mehr benötigt: Freigabe
mittels: delete p;.
Beispiel:
dynamische Arrays: int* p = new int[10];
Speicherverwaltung
Pointer
Im Wesentlichen drei Speicherbereiche:
Pointer (Zeiger) ist ein Datentyp, der eine Adresse im Hauptspeicher
speichern kann (Verweis). (Variable = Folge von Speicherzellen,
Adresse = Nummer der ersten Speicherzelle)
Statische Obj.:
Beispiel:
int* p; (alternativ int *p;)
p hat Typ Pointer auf int, int Basistyp von p
Operatoren:
Beispiel:
vorteilhaft…:
Beispiel:
Dereferenzierungsop. *:  Zugriff auf Inhalt der
Speicherzelle,
auf
die
Pointer
verweist.
Manipulation einer Variablen über ihren Namen
oder über *p (Pointer, der auf Variable zeigt).
Referenzierungsop. &  Ermittlung der Adresse
einer Variablen.
int* p; ... int i = *p; ...
int i; ... int* p = &i; ...
Lokale Blockvariablen: auf dem Stack gespeichert. Solche Variablen
sind im Programmtext deklariert. Der Stack ist
wichtig zur Abarbeitung von Fkt. Bei Verlassen des
Blocks: Speicher freigegeben.
Bei Laufzeit angelegte Variablen im Heap gespeichert. Reservierung
mit new, Lebensdauer bis zur expliziten Freigabe
durch delete.
Arbeiten mit Arrays
Gegeben:
bei Konstruktion kompl. Datenstrukturen, beim
Unterscheide:
Anlegen dyn. Variablen, für Call by Reference
Statt Call by Reference mittels
int f(int& x) { ... }auch möglich:
Arithmetik:
struct element {
int key; ... };
Statisches Array:
struct element list[max_length];
Dynamisches Array:
struct element* list
= new struct element[length];
int f(int* x) { ... }
Unterschied: Zugriff auf Wert von x im
1. Fall: x,
2. Fall: *x
z.B.
globale
Variablen,
haben
festen
Speicherbereich für den ganzen Ablauf des
Programms
Lineare Suche:
auf Pointern möglich, z.B: p = p + 2; Pointer
wird um 2 mal Anzahl Speicherzellen
Einfügen:
weitergeschoben, die zum Speichern des Basistyps
von p benötigt werden. Weitere Beispiel:
p++; q = p[6]; entspricht: q = *(p + 6);
Elemente der Reihe nach durchgehen. Überprüfen,
ob list[i].key == k.
Falls Daten sortiert: Binäre Suche
Neues Element an Stelle i. Letzter Platz voll  mit
new neues Array anlegen, umkopieren.
Löschen:
Alle Einträge an Stellen i+1,..., length-1 um
eine Position nach links schieben.
Aufwand:
Worst Case bei Arrays. S.u.
Vergleich
Arbeiten mit linearen Listen
Definition:
Lineare Listen besten aus: Listenkopf, Elementen,
Verkettung der Elemente, Listenende.
Gegeben:
struct element {
struct element* next;
int key;
... };
struct element* list;
Bilanz:
Worst Case Aufwand: (Aufwand = Kopieren)
Bemerkung:
„length“ bei V. Listen muss nicht bekannt sein!
Binäre Suchbäume
Definition:
Bemerkung:
Listenkopf mit globaler Variable element* head;
festlegen! Listenende: p->next == 0
Bemerkung:
Zugriff auf nächstes Element:
(*list).next
(Klammern notwendig!) oder:
list->next
Begriffe:
Listenende: angezeigt durch next == 0.
Lineare Suche:
Einfügen:
Elemente
heissen
Knoten
(node).
Sind
Komponenten left und right beide Nullpointer
Bemerkungen:
Geg.: Pointer p auf Element in Liste. Neues Element
mit Key k hinter dem Element, auf das p zeigt:
Suche:
( kein Nachfolger), so heisst der Knoten Blatt
(leaf ). “Startknoten” heisst Wurzel (root).
Globale Variable Zeiger auf root: element* root;
Suche Knoten mit Key k.
struct element *p = root;
while(p != 0) {
if (p->key == k) return p;
if (p->key < k) p = p->left;
else p = p->right; }
return 0;
Achtung:
Einfügen:
Wie löscht man das erste Element? Lösung:
Sonderbehandlung (eigener Code)
Erstes Element wird nie benutzt (Dummy)
Doppelt verkettete Listen:
struct element {
struct element *prev, *next;
int key;
... };
Füge Knoten mit Key k ein.
struct element** p = root;
while(*p != 0) {
if ((*p)->key<k) p = &((*p)->left);
else p = &((*p)->right); }
*p = new struct element
...
Geg.: Pointer p, der auf Element in Liste zeigt.
Lösche das Element, auf das p->next zeigt:
void DeleteAfter( element* p) {
if (p->next == 0) return;
element* q = p->next;
p->next = p->next->next;
delete q;
}
Höhe eines Baumes: maximaler Abstand (= Anzahl
Knoten) eines Blattes zur Wurzel.
Elemente zufällig: height  log(size)
void InsertAfter( int k, element* p ) {
element* q = new element;
q->key = k;
q->next = p->next;
p->next = q;
}
e.next = p->next;
Löschen:
Jedes Element hat zwei mögliche Nachfolger:
struct element {
struct element *left, *right;
int key;
... };
Suche nach Element mit Key k:
p = list;
while (p != 0) {
if (p->key == k) return p;
p = p->next;
}
return 0;
p->next = &e;
(  Grad γ+ = 2)
p als Pointer auf Pointer, um am Schluss new zu
ermöglichen.
Lösche Knoten, auf den p->r. bzw. p->l. zeigt.
Mindestens ein Teilbaum ist leer:
Löschen:
1. Fall:
struct element* &q = p->right; // bzw. p->left
if (q->right == 0 || q->left == 0) {
struct element* h = q;
if (q->right == 0) q = q->left;
else q = q->right;
delete h; }
Bemerkungen:
q
ermöglicht gleiches Behandeln der Fälle p-
>right bzw. p->left (hier p->right).
2. Fall:
Beide Teilbäume sind nicht leer.
Im rechten Teilbaum Element mit kleinstem
Schlüssel suchen. (links: grössten Schlüssel.)
Kopiere dessen Inhalt nach *q. Lösche dieses
Element wie in 1. Fall. (Bemerkung: kann kein
linkes Kind haben)
Bilanz:
Worst Case Aufwand: Suche, Einfügen, Löschen in
Bisher:
height Schritten. Typisch: height  log(size)
Rekursion
Jetzt:
Funktionen dürfen andere Funktionen aufrufen, auch sich selbst!
wichtig:
explizite Abbruchbedingung
struct complex {
double real, imag; };
struct complex add(struct complex
struct complex b);
...
a,
“Integration” der Fkt.en wie add in die Klasse
 nur auf Klassenelemente anwendbar
 Kein direkter Zugriff auf real, imag
Rekursion versus Iteration

Rekursion und Iteration funktional äquivalent: Rekursion  Schleife.
Iterativer Ansatz oft schneller, rekursiver Ansatz oft klarer.
Beispiel:
iterativ:
Beispiel:
Deklaration: complex c; Zugriff auf c.real
und c.imag ist verboten (private). Stattdessen:
Zugriff per c.re() und c.im() (public).
Extern:
Definition Methoden auch ausserhalb Klassendefinition möglich (mittels scope-Operator ::):
effiziente Ausgabe eines binären Suchbaums.
Ausgabe zwischen den Aufrufen: inorder
Durchlauf. (preorder = vor, postorder = danach)
void inorder(struct element* p) {
if (p->left != 0) inorder(p->left);
cout < < p->key;
if (p->right!=0) inorder(p->right);
}
void complex::addto(complex d) {
real += d.real;
imag += d.imag;
}
Aufruf: c.addto(d);
Zugriffskontrolle
3 Zugriffsarten:
Speicherorganisation
Aktive Fkt. belegen Speicherplatz im Stack. Bei rekursivem Aufruf:
aufrufende Funktion noch im Stack; aufgerufende Funktion kommt Syntax:
dazu.
Inkarnationen:
Gleichzeitig
im
Stack
vorhandene
Funktionsblöcke derselben Funktion. Rekursionstiefe = Anzahl
Inkarnationen.
Lösung:
globales
Gedächnis
anlegen:
Array,
der
Zwischenergebnisse speichert (wie iterativer Fall).
Klassen
Eine Klasse ist eine (abstrakte) Datenstruktur: verwaltet Daten, bietet
Funktionen (Methoden) an, um die Daten zu manipulieren.
 Verwaltung der Daten nur über angebotene Fkt.  Fkt. sind nur auf Beispiel:
Elemente (Objekte) der Klassen anwendbar.
Erweiterung des structs um Funktionen.
Klasse = Menge aller Objekte eines spez. Typs
Instanz
= Variable des Typs. Besteht aus:
Datenelementen, Elementfkt.en (Methoden)
Syntax:
Klassenart Klassenname { Elementliste };
von
Variablen-
Funktionsdeklarationen.
Beispiel:
Speichern kompl. Zahlen mit Klasse complex.
die Zugriffsart der Methoden und Variablen bis zum
nächsten Zugriffsart : fest.
Default bei class: private
gleicher Name wie Klasse, kein Rückgabewert, wird
in der Klassendekl. dekl., kann fehlen. Der
Konstruktur
ohne
Parameter
heißt
Standardkonstruktor.
class complex {
...
complex() {real = 0; imag = 0};
complex(double a, double b) {real =
a; imag = b};
...}
c;
deklariert
c
und
ruft
Standardkonstruktor auf. (In c steht dann die Null.)
complex c(1,0); deklariert c und ruft zweiten
Konstruktor auf. (In c steht dann die Eins.)
Klassenart: class, struct, union.
Folge
Zugriffsart : Legt innerhalb der Klassendeklaration
complex
(Meistens class. struct, union sind legacy.)
Elementliste:
private (kein Zugriff von aussen möglich)
public (Zugriff möglich)
protected
Konstruktoren
Konstruktor:
Begriff:
können
Bemerkungen:
rekursiv: Rand: 0! = 1, Allgemein: n! = n(n-1)!
int fak_rek(int n) {
if (n == 0) return 1;
return n * fak_rek(n-1); }
Daten
class complex {
double real, imag;
public:
double re() { return real };
double im() { return imag };
void addto(complex);
};
Fakultätsfunktion n!
int fak_it(int n) {
int erg = 1;
for (int i = 1; i <= n; i++)
erg = erg * i;
return erg;
}
Konsistenzbedingungen an
eingehalten werden.
und
Destruktoren
Ist Ende des Gültigkeitsbereichs einer Instanz erreicht oder wird sie mit
dem delete-Operator gelöscht, so wird der Destruktor der Klasse
aufgerufen.
Deklaration:
classname();
Bemerkungen:
kann
fehlen.
Wichtig
für
dynamische
Datenstrukturen: In einer Suchbaum-Klasse können
alle Elemente der Klasse gelöscht werden.
Beispielprogramm: Matrixmultiplikation
Bsp.: Mischen zweier sortierter Listen
Gegeben sind zwei sortierte (geordnete) Listen a und b ( Listenköpfe). Die Elemente sind vom Typ struct element. Die neue List soll
so vereinigt werden, dass sie wieder sortiert ist.
#include <iostream>;
int main()
{
const int N = 10;
int a[N][N], b[N][N], c[N][N];
... // a, b einlesen
for( int i = 0; i < N; i++ )
{
for( int j = 0; j < N; j++ )
{
c[i][j]= 0.0;
for ( int k = 0; k <10; k++)
c[i][j] = c[i][j] + a[i][k] * b[k][j];
}
}
}
element* Mischen ( element *a, element *b) {
element *p, *q;
// festlegen des Listenkopfs
if ( a->key <= b->key )
{ p = a; a = a->next; }
else
{ p = b; b = b->next; }
q = p;
//Kopf der Ergebnisliste in q
// berechne der neuen Liste durch
// Umstrukturierung der beiden alten
while ((a != 0) && (b != 0)) {
if ( a->key <= b->key )
{ p->next = a; a = a->next; }
else
{ p->next = b; b = b->next; }
p = p->next;
}
Bsp.: Rekursion – grösster gemeinsamer Teiler
int ggt( int n, int m )
{
if (m == 0) return n;
return ggt (m, n%m);
}
// falls eine Liste zu Ende ist
// entweder a == 0 oder b == 0
if ( a != 0)
p->next = a;
else
p->next = b;
Bsp.: Rekursion – Fibonacci-Zahlen
int fib( int n )
{
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + fib(n-2);
}
Bsp.: Rekursion – Quicksort
void Quicksort( double S[], int l, int r )
{
if ( l >= r) return;
int m = Umordne ( S, l, r);
Quicksort ( S, l, m-1);
Quicksort ( S, m+1, r);
}
//Rückgabe des Zeigers auf Listenkopf
return q;
}
Herunterladen