Anmerkung vom Verfasser Änderungsprotokoll
Diese Zusammenfassung wurde von Max Schaldach 27.08.2023 Veröffentlichung der originalen Version
für die Vorlesung Informatik I von Ralf Sasse (DE)
(Herbstsemester 2022) erstellt. Als Orientierung
30.12.2023 Verbesserung Code-Beispiel zu
wurde die Zusammenfassung von Luzian Bieri Primzahlen
verwendet.
Ihr wollt stets auf dem neusten Stand meiner
Zusammenfassungen sein? Ein Klick genügt:
https://forms.gle/LHYF3qmqf3HSFjSn6
Auf meiner Webseite findet ihr eine Sammlung meiner
Zusammenfassungen und aller Unterlagen, die mir das
Leben einfacher gemacht haben:
https://n.ethz.ch/~mschaldach/
Es ist jedem freigestellt, diese Zusammenfassung
weiterzuentwickeln und zu veröffentlichen. Um
Verwirrung zu vermeiden, sollte jedoch deutlich
darauf hingewiesen werden, dass es sich nicht um die
Originalfassung handelt.
Viel Erfolg, ihr packt das!
Hinweis: Richtigkeit und Vollständigkeit kann nicht
garantiert werden. Korrekturen,
Verbesserungsvorschläge, und anderweitige
Anregungen zum Inhalt und der Gestaltung bitte an
[email protected] weiterleiten.
Informatik I Max Schaldach Logische Operatoren erlauben es mehrere Aussagen zu verbinden: 2. Zahlensysteme (Umrechnungen im Anhang)
AND: false && … // ist unabhängig von … false
1. Operatoren und Variablen true || … // ist unabhängig von … true
2.1. Fliesskommazahlensystem
OR: Ein Fliesskommazahlensystem wird durch vier natürliche Zahlen
1.0. Typkonvertierung NOT: !a // invertiert Aussage
!(a && b) == (!a || !b) // De-morgansche Regel definiert:
Bei Ausdrücken mit mehreren Variablen verschiedener Datentypen
!(a || b) == (!a && !b) // De-morgansche Regel 𝐹(𝛽, 𝜌, 𝑒𝑚𝑖𝑛 , 𝑒𝑚𝑎𝑥 )
wird in den präzisesten Datentyp umgewandelt:
a b XOR(a,b) a && b a || b 𝛽 ist die Basis, 𝜌 die Präzision/Stellenzahl, 𝑒𝑚𝑖𝑛 der kleinste
bool < char < int < unsigned int < float < double
0 0 0 0 0 Exponent und 𝑒𝑚𝑎𝑥 der grösste Exponent ist
1.1. L-Werte & R-Werte 1 0 1 0 1
0 1 1 0 1 Die normalisierte Darstellung 𝐹 ∗ erlaubt keine 0 vor dem Komma
• L-Werte besitzen eine Speicheradresse (z.B. int a). Ihnen können
1 1 0 1 1 Formeln für normalisierte Darstellung:
Werte zugewiesen werden (z.B. int b = 1, int a, c += b, ++b, <<)
• R-Werte besitzen keine Speicheradresse (z.B. 1, !b, a == b, a++, &a) 1.5. Character (ASCII-Tabelle im Anhang) • Grösste und kleinste Zahl: ±βemax +1 ∗ (1 − β−𝜌 )
char (8𝑏𝑖𝑡) ist ein Datentyp für die Speicherung von Buchstaben. • Kleinste positive Zahl: βemin
1.2. Präzedenzen Gerechnet wird wie mit int: • Anzahl Werte: 2 ∗ (β − 1) ∗ β𝜌−1 ∗ (emax − 𝑒min + 1)
Präz. Operatoren Eingabe Ausgabe char a = ‘a’; // “a” wäre ungültig • Anzahl positive Werte: (β − 1) ∗ β𝜌−1 ∗ (emax − 𝑒min + 1)
2 a++, a-- L-Wert R-Wert char A = ‘A’;
3 ++a, --a L-Wert L-Wert std::cout << a - A; // 32, per ASCII-Konvention 2.2. Binärsystem
3 *a, &a L- /R-Wert L-Wert char b = a + 1; // ’b’ Im Binärsystem werden Zahlen mittels {0,1} zur Basis 2 dargestellt
5 *, /, % R-Wert R-Wert • Präfix: 0b
1.6. Float & Double
6 +, - R-Wert R-Wert • bn 𝑏(𝑛−1) … b1 b0 ↔ bn ⋅ 2n +··· +b1 · 2 + b0
float (32𝑏𝑖𝑡) und double (64𝑏𝑖𝑡) stellen reelle Zahlen dar. Ein
9 >, >=, <, <= R-Werte R-Wert • Beispiel 1: Binäre Addition
float wird mit einem f / .f hinter dem Zahlenwert gekennzeichnet
10 ==, != R-Werte R-Wert 1.11011 ∗ 22
>> L-Werte L-Wert • Gleichheit: Nicht mit Operatoren auf Gleichheit überprüfen,
+ 0.11010 ∗ 22
<< L- /R-Werte L-Wert sondern eine Fehlermarge [𝑥2 − 𝑥1 ] < 𝜀 definieren
10.10101 ∗ 22 auf 1.010101 ∗ 22 normalisieren
14 && R-Werte R-Wert • Addition: Zahlen unterschiedlicher Grösse nicht addieren, da
15 || R-Werte R-Wert kleine Beträge durch Rundung verschwinden • Beispiel 2: 3.141610 in 𝐹(2,4, −3,3) umwandeln
16 =, +=, -=, *=, /=, %= L- /R-Wert L-Wert • Subtraktion: Zahlen sehr ähnlicher Grösse nicht subtrahieren, da (1) Die Vorkommastelle 3 als 11 ∗ 20 schreiben
ein Rundungsfehler übrigbleibt (2) Die Nachkommastellen berechnen:
1.3. Integer & Unsigned Integer 𝑥𝑖 𝑏𝑖 𝑥𝑖 − 𝑏𝑖 2 ∗ (𝑥𝑖 − 𝑏𝑖 )
int (32𝑏𝑖𝑡) stellen die ganzen Zahlen und unsigned int (32𝑏𝑖𝑡) die 1.7. Auto 0.1416 0 0.1416 0.2832
natürlichen Zahlen dar Wird eine Variable mit auto initialisiert, erkennt der Compiler … … … …
automatisch den Datentyp des R-Wertes: 1.1328 1 0.1328 0.2656
auto name = 2.0f; // als Typ wird float zugewiesen … … … …
0.1416 … … …
1.3. bis 1.7. Konstanten
• Mit 𝑛 𝑏𝑖𝑡𝑠 können 2𝑛 Zahlen dargestellt werden: (3) Bei erster Wiederholung aufhören und als 0.001 … ∗ 20
Konstanten werden mit dem Präfix const gekennzeichnet. Der
o unsigned int: Maximum: 232 − 1, Minimum: 0 schreiben (Achtung: Das Komma wird hinter die erste Null
Compiler prüft, dass Konstanten nicht verändert werden:
o int: Maximum: 231 − 1, Minimum: −231 gesetzt)
const T name = 3; // Wert 3 ist unveränderlich
o Die Umrechnung erfolgt mit 𝑥𝑢𝑛𝑠𝑖𝑔𝑛𝑒𝑑 𝑖𝑛𝑡 = 𝑥𝑖𝑛𝑡 − 2𝑛 (4) Mit Vorkommastelle zusammensetzen: 11.001 ∗ 20
• Overflow: Einer Variable wird ein Wert zugewiesen, der nicht mit 1.8. Typ-Aliase (5) Auf 𝜌 = 4 Stellen runden: 11.01 ∗ 20
𝑛 𝑏𝑖𝑡𝑠 gespeichert werden kann. Wird das Maximum/Minimum Mit using name = T und typedef T name kann der Gebrauch (6) Normalisieren: 1.101 ∗ 21
erreicht, wird am anderen Ende des Zahlenstrahls fortgefahren umständlicher Typennamen simplifiziert werden: (7) Überprüfen ob der Exponent ∈ [𝑒𝑚𝑖𝑛 , … , 𝑒𝑚𝑎𝑥 ]
• Die Division / mit int und unsigned int ist verlustbehaftet: using int_vec = std::vector<int>;
o (a / b) * b ≠ a typedef std::set<T>::iterator set_it; 2.3. Hexadezimal- & Oktalsystem
o (a / b) * b + a % b = a Im Hexadezimalsystem werden Zahlen mittels {0, … ,9, 𝐴, … , 𝐹} zur
• Der Modulo % gibt den Rest einer Division mit int oder unsigned 1.9. In- und Decrement Operatoren Basis 16 dargestellt. Dazu werden sie in Hex-Nibbles bestehend aus
int zurück. Er übernimmt das Vorzeichen des linken Operanden c += 1 ↔ c = c + 1 gilt für die Operatoren +, -, *, /, %, & 4𝑏𝑖𝑡𝑠 dargestellt. Der Präfix lautet 0𝑥 und das Zahlenformat
Postfix: c++ ↔ c = c + 1 gilt für die Operatoren ++, --. Zuerst entspricht ℎ𝑛 ℎ(𝑛−1) … ℎ1 ℎ0 ↔ ℎ𝑛 ⋅ 16𝑛 +··· +ℎ1 · 16 + ℎ0
1.4. Logische Werte
bool Variablen können die Werte true / 1 oder false / 0 wird die Zeile ausgeführt, dann wird der Wert erhöht Im Oktalsystem werden Zahlen mittels {0, … ,7} zur Basis 8
annehmen. Alle int Werte ≠ 0 werden als true interpretiert Präfix: ++c ↔ c = c + 1 gilt für die Operatoren ++, --. Zuerst wird dargestellt. Der Präfix lautet 0𝑜 und das Zahlenformat entspricht
der Wert erhöht, dann wird die Zeile ausgeführt 𝑜𝑛 𝑜(𝑛−1) … 𝑜1 𝑜0 ↔ 𝑜𝑛 ⋅ 8𝑛 +··· +𝑜1 · 8 + 𝑜0
3. Funktionen 3.4. Schleifen std::ostream ist der Datentyp für Output-Streams:
Eine for-Schleife wiederholt eine Anweisung unter einer #include <iostream>
3.0. Hilfsfunktionen bestimmten Bedingung - solange condition == true: void print (std::ostream& out, std::string name) {
assert(condition) bricht ein Programm ab, wenn eine out << «Player: » << name;
for(initial statement; condition; expression) {
bestimmte Bedingung nicht erfüllt ist: statement; }
#include <cassert> } Da Streams nicht kopiert werden können, sollten sie per Call-by-
T funktionsname(T arg) { Reference an Funktionen übergeben werden
Eine while-Schleife ist dann nützlich, wenn die Anzahl an
assert(condition); // wenn condition == false
} Ausführungen unbekannt ist:
3.8. Überladen von Funktionen
while(condition) { Es können unterschiedliche Funktionen mit demselben Namen
3.1. Aufbau einer Funktion statement;
Eine Funktion ist ein Codeblock, der ausgeführt wird, wenn er }
existieren, wenn diese unterschiedliche Argumente besitzt:
aufgerufen wird. Eine Funktion wird wie folgt definiert: do { T funktionsname(T1 arg) {…}
statement; T funktionsname(T2 arg) {…}
//PRE: …
} while(condition); T funktionsname(T1 arg1, T2 arg2) {…}
//POST: …
T funktionsname(T1 arg1, T2 arg2, …) { Wobei letztere Variante beim ersten Mal bedingungslos ausgeführt
4. Container
statement; wird (praktisch, wenn eine Variable anfangs nicht initialisiert ist)
return …; 4.0. Allgemeines
} 3.5. Sprunganweisungen Ein Container ist eine Sammlung von Objekten desselben
T ist dabei der Rückgabetyp (void besitzt kein return) • break bricht eine Schleife sofort ab
Datentyps
• continue springt zur nächsten Iteration der Schleife
• //PRE:… Definitionsbereich der Funktion (schwach) 4.1. Arrays
• //POST:… Wert und Effekt des Funktionsaufrufes (stark) 3.6. Call & Return Typen Ein Array ist eine Sammlung von Elementen, die sich an
Pass-by-Value: T funktionsname(T arg); zusammenhängenden Speicherplätzen befinden:
3.2. Gültigkeitsbereiche • Für die Funktion wird eine lokale Kopie des Arguments gemacht
• Funktionen können erst nach ihrer Definition aufgerufen • Änderungen an arg beeinflussen das ursprüngliche Objekt nicht
T name[3]; // alloziert drei Speicherplätze
werden. Falls sie vorher benötigt werden, müssen sie oberhalb int name[3] = {1, 3, 5};
Call-by-Reference: T funktionsname(T& arg); int name[] = {1, 3, 5}; // identisch zur obigen Zeile
ihres Aufrufes vorwärts deklariert werden:
• Der Funktion wird eine Referenz auf das Objekt übergeben Die Grösse von Arrays kann nach der Deklaration nicht mehr
T funktionsname(T1 arg1, T2 arg2, …);
• Änderungen an arg beeinflussen das ursprüngliche Objekt direkt verändert werden
• Eine Variable ist nur innerhalb des Scopes {…} sichtbar in der sie
Call-by-Address: T funktionsname(T* arg); Elemente können über Indizes ausgelesen und beschrieben
deklariert wurde (zum Beispiel eine Funktion oder eine Schlaufe)
• Die Adresse des Arguments wird an die Funktion übergeben werden. Das erste Element eines Arrays besitzt den Index 0:
3.3. Anweisungen • Änderungen an arg beeinflussen das ursprüngliche Objekt direkt int name2 = name[2]; // name2 wird der Wert 5
Eine if-else-Anweisung führt eine Anweisung bedingt aus: Return-by-Reference: T& funktionsname(T arg); zugewiesen
if(condition) { • Die Funktion gibt eine Referenz auf ein Objekt zurück Achtung: Der Compiler überprüft nicht, ob der Elementzugriff bei
statement; • Ermöglicht Verschachtlungen von Funktionen einem Array gültig ist
}
else if(condition) { Return-by-Address: T* funktionsname(T arg); 4.2. Vektoren
statement; • Die Funktion gibt einen Zeiger auf ein Objekt zurück Ein Vektor unterscheidet sich dadurch von einem Array, dass seine
}
Grösse angepasst werden kann:
else { 3.7. Streams
statement; std::istream ist der Datentyp für Input-Streams: #include <vector>
} std::vector<T> name = std::vector<T>(n, init_value);
#include <iostream> std::vector<T> name(n, init_value);
Bei einem einzeiligen statement können {…} weggelassen werden: T name; std::vector<T> name = {1, 2, 3}; // auch ohne = gültig
if(condition) statement; std::cin >> name;
while(std::cin >> name) {…} // solange Eingabe wobei n die Länge des Vektors und init_value der Initialwert
Mit einer switch-Anweisung kann eine Variable auf Gleichheit mit vorhanden ist, wird Schleife fortgesetzt seiner Elemente ist
einer Liste von Werten getestet werden: vec.push_back(x); // fügt Element x an vec an
std::ifstream ist der Datentyp zum Auslesen und std::ofstream
switch(arg) { zum Beschreiben einer Datei: vec.pop(); // löscht letztes Element von vec
case value1: statement; break; vec.at(i); // fragt Wert von vec am Index i ab
case value2: statement; break; #include <fstream> vec.size(); // fragt Länge von vec ab
default: statement; std::ifstream reader (“my_file.txt”); vec.empty(); // true, wenn vec leer ist
} T name;
reader >> name; // weist Variable den ersten Wert des Anders als bei vec[i], wird bei vec.at(i) ein Laufzeitfehler
Falls break fehlt, wird der nächste case ausgeführt Dokumentes zu zurückgegeben, wenn ein ungültiger Index aufgerufen wird
4.3. Strings 4.7. Container als Funktionsargumente Dadurch gehen Längeninformationen verloren. Indem ein zweiter
Ein String ist ein Datentyp um Text zu speichern: Container sollten als const an Funktionen übergeben werden: Zeiger end hinter das letzte Element zeigt, wird dies verhindert:
#include <string> function(const int std::vector<int>& vec) {…} T name[5];
std::string name(n, init_value); T* end = &name[5];
std::string name {“nnnnnn”}; // identisch zur obigen 5. Referenzen, Zeiger und Iteratoren Achtung: Die Speicheradresse end gehört nicht mehr zum Array
Zeile, wenn init_value = 6
5.1. Referenzen Die Länge von Containern kann über Zeiger bestimmt werden:
wobei n die Länge des Strings und init_value der Initialwert Eine Referenz T& ist ein Alias einer bestehenden Variablen:
seiner Elemente ist. Der Initialwert besitzt den Datentyp char (*zeiger).size(); // alternativ: zeiger->size()
T variable = value;
name.length(); // fragt Länge von name ab T& reference = variable; // Lese-Schreibe-Alias 5.5. Zeiger-Arithmetik
name.at(i); // fragt Wert von name am Index I ab Zeiger können verschoben werden:
• Referenzen müssen immer initialisiert werden (mit L-Wert)
Strings können mit == verglichen, mit + aneinandergehängt und
• Referenzen können nach ihrer Initialisierung nicht auf ein neues T name[5];
mit std::cout<< ausgegeben werden Objekt gesetzt werden T* begin = name;
begin + 1; // temporär verschieben mit +, -
4.4. Sets • Der Typ der Referenz muss dabei derselbe sein wie der Variable ++begin; // permanent verschieben mit ++, --, +=, -=
Ein Set ist ein Datentyp für Mengen. Elemente kommen dabei nur • Das Objekt, auf das eine Referenz verweist, muss langlebiger
Zeiger können verglichen werden:
einmal vor und sind in aufsteigender Reihenfolge geordnet: sein als die Referenz selbst
#include <set>
• Verändert man den Wert des Alias, so nimmt auch die ptr1 == ptr2; // mit <=, >=, <, >, !=, ==
std::set<T> name (vec.begin(), vec.end()); Ursprungsvariable den neuen Wert an Die Entfernung zwischen zwei Elementen kann bestimmt werden:
// initialisiert Set mit Werten von vec const T variable = value; ptr1 - ptr2;
• Auf Elemente kann nur mit Iteratoren zugegriffen werden const T& reference = variable; // Lese-Alias
Zeiger können iteriert werden:
• Zum Verschieben können nur -- und ++ verwendet werden Auf eine const Variable kann nur eine konstante Referenz const T name[5] = {1, 2, 3, 4, 5};
• Zum Vergleichen sind nur == und != erlaubt T& zeigen. Eine konstante Referenz auf eine nicht-konstante for(T* begin = name; begin < name + 5; ++begin) {…}
• Ungültige Operationen sind [],+, -, <, >, <=, >=, +=, -= Variable verhindert die Variable über die Referenz zu verändern:
5.6. Konstante Zeiger
T variable = value1;
4.5. Mehrdimensionale Container const T& reference = variable; Es gibt konstante Zeiger und Zeiger auf konstante Objekte:
Zweidimensionale Arrays: const T* zeiger; // kein Schreibzugriff auf Target
Konstante Referenzen können auf R-Werte verweisen:
int name[n][m]; // uninitialisiertes nxm Feld T const* zeiger; // identisch zur obigen Zeile
int name[2][3] = { {1, 1, 1},{2, 2, 2} }; const T& reference = 5; T* const zeiger; // kein Schreibzugriff auf Zeiger
const T* const zeiger; // kein Schreibzugriff auf
Zweidimensionale Vektoren: 5.2. Zeiger Target und Zeiger
std::vector<std::vector<T>> vec(rows, Ein Zeiger speichert die Speicheradresse eines Objektes:
Achtung: Unterschied, ob die Referenz oder Variable konstant ist:
std::vector<T>(cols, init_value)); T* zeiger;
T variable1;
wobei rows die Anzahl Reihen und cols die Anzahl Spalten sind Der Nullzeiger zeigt auf nichts: const T* zeiger = &variable1; // valide
Die Länge mehrdimensionaler Container wird wie folgt abgefragt: const T variable2;
T* zeiger = nullptr;
T* zeiger = &variable2; // Compiler-Fehler
name.size(); // Anzahl Reihen von name
5.3. Adress- und Dereferenz-Operatoren
name.at(0).size(); // Anzahl Spalten von name 5.7. Iteratoren
name.at(i).at(j); // Wert von name in der Reihe i und Der Adress-Operator & liefert als R-Wert eine Adresse vom Typ T*:
Ein Iterator durchläuft nicht-zusammenhängenden Speicherplatz:
Spalte j T variable = 3;
#include <vector>
T* zeiger = &variable;
4.6. Standard-Funktionen auf Container std::vector<T> vec = = {1, 2, 3};
Im Folgenden sind b und p zwei Pointer: Ein Zeiger kann nach seiner Initialisierung auf ein neues Objekt std::vector<T>::iterator name1 = vec.begin();
gesetzt werden std::vector<T>::iterator name2 = vec.end();
#include <algorithm>
std::fill(b, p, val); // Wert val ersetzt Werte im Der Dereferenz-Operator * liefert einen L-Wert vom Typ T: wobei .begin() und .end() Iteratoren der Standardbibliothek
Bereich [b,p) sind und auf das erste respektive hinter das letzte Element zeigen
T variable_new = *zeiger;
std::find(b, p, val); // true, wenn sich val im for(std::vector<T>::iterator i = vec.begin(); i <
*zeiger = 3;
Bereich [b,p) befindet vec.end(); ++i) {
zeiger[0] = 3; // identisch zur obigen Zeile
std::sort(b, p); // Bereich [b,p) wird sortiert std::cin >> *i;
std::min(a, b); // Minimum von a und b 5.4. Zeiger auf Container }
std::max(a, b); // Maximum von a und b
Zeiger können auf Container zeigen: Konstante Iteratoren erlauben keinen Schreibzugriff auf das Objekt:
std::min_element(b, p); // Iterator auf kleinsten
Wert im Bereich [b,p) T name[5]; std::vector<T>::const_iterator name = vec.begin();
std::swap(a, b); // vertauscht Werte von a und b T* begin = name; // zeigt auf den Index 0 von name
Für Iteratoren auf Sets gelten nur die Operatoren --, ++, == und !=
6. Rekursion • Auf private Memberfunktionen kann nur in der Class In der Class / im Struct ist die Verwendung von this nicht
Bei einer Rekursion ruft sich eine Funktion selbst auf. Zur zurückgegriffen werden, public Memberfunktionen sind auch zwingend
Terminierung muss ein Basisfall definiert werden, eine Kondition, ausserhalb verfügbar Mit -> wird über einen Pointer auf einen Member zugegriffen:
die nach einer bestimmten Anzahl an Funktionsaufrufen zutrifft: • Memberfunktionen werden mit object->functionname
classname obj;
T recursion(T1 arg1, T2 arg2, …) { aufgerufen classname* zeiger = &obj; // Zeiger auf Container
if(condition) { // condition ist der Basisfall Wird die Memberfunktion innerhalb der Definition der Class nur zeiger->mem1;
return …; // kein erneuter Funktionsaufruf deklariert, erfolgt ihre Initialisierung im Programmcode wie folgt: (*zeiger).mem1; // identisch zur obigen Zeile
}
recursion(…); // erneuter Funktionsaufruf T classname::functionname2(T arg) {…} 9. Operatorüberladungen
return …; Operatorüberladungen ermöglichen es, Operatoren mit nicht-
Konstante Memberfunktionen verändern die Membervariablen
} fundamentalen Datentypen zu nutzen:
des Objektes nicht:
7. EBNF T functionname(T arg) const { return arg; } T operator+ (T a) {…} // unärer Operator
Eine EBNF definiert Grammatik für bestimmte Ausdrücke. Dazu T operator+ (T a, T b) {…} // binärer Operator
8.3. Unterschied Struct und Class bool operator== (T a, T b) {…} // Vergleichsoperator
bedarf es eines Alphabets {… } das die gültigen Schriftzeichen T& operator+= (T& a, T b) {…} // Zuweisungsoperator
sowie Grammatikregeln festlegt: Die Membervariablen und Memberfunktionen einer Class sind per
T& operator++ (T& a) {…} // Präfixoperator
Default private, die eines Structs dagegen public
seq = term | term {term} void operator++ (int) {…} // Postfixoperator
term = “A” | “A” [lowerterm] 8.3. Konstruktor std::ostream& operator<< (std::ostream& out, T a) {
lowerterm = “a” | “a” lowerterm return out << …; // Ausgabeoperator
Der Konstruktor einer Class / eines Structs ist eine gleichnamige }
| steht für Alternative, {…} steht für eine optionale Repetition (0- Funktion, die bei der Deklaration eines Objektes aufgerufen wird: std::istream& operator>> (std::istream& in, T& a) {
bis 𝑛-fach) und […] kennzeichnet optionalen Inhalt (0- oder 1-mal) classname obj_name = new classname (arg1, arg2); return in >> …; // Eingabeoperator
}
8. Structs & Classes Innerhalb der Klasse wird der Konstruktor wie folgt initialisiert:
classname(T arg): mem1(arg) {…} 10. Dynamischer Speicher
8.1. Initialisierung classname(T arg){ mem1 = arg; } // identisch zur
Structs und Classes sind benutzerdefinierte Container. In ihnen obigen Zeile 10.1. Verkettete Listen
werden Elemente mit unterschiedlichen Datentypen gespeichert: classname(): mem1(0), mem2(0) { } // Default- Bei verketteten Listen hängt der Speicherbereich nicht zusammen.
class classname { Konstruktor Eine solche Liste besteht aus Containern, sogenannten Nodes, die
T mem1; Die Initialisierung ausserhalb der Class / des Structs erfolgt mit: aufeinander zeigen. Der Head enthält die Speicheradresse der
T mem2; ersten Node und jede Node kennt über einen Zeiger ihren
classname::classname(T arg): mem1(arg) { }
… Nachfolger
};
struct structname {
8.4. Destruktor 10.2. New & Delete
… // wie bei Class Der Destruktor dient dazu eine Class / einen Struct abzubauen:
Mit new wird ein Objekt erstellt, indem der nötige Speicherplatz
}; ~classname() {…} reserviert und dann ein gegebener Konstruktor aufgerufen wird:
Achtung: Nach der umschliessenden Klammer folgt ein ; Dieser wird beim Abbau automatisch aufgerufen classname* zeiger = new classname(arg);
classname obj_name (arg1, arg2); // Initialisierungen T* name = new T[n]; // dynamische Allokation eines
8.5. Kopier-Konstruktor & Kopier-Zuweisung Arrays mit n Elementen
classname obj_name = {arg1, arg2}; // äquivalent
classname obj_name {arg1, arg2}; // äquivalent Ein Kopier-Konstruktor ist ein spezieller Konstruktor, der ein neues
Objekt als Kopie eines bestehenden Objekts erstellt: Der Rückgabewert ist ein Zeiger auf das neu erstelle Objekt
classname obj_name (arg1); // mem2 = 0
obj_name.mem1; // Zugriff auf Membervariable classname(const classname& arg): mem1(arg.mem1) {…} delete ruft den Destruktor eines Objektes auf, um den
Speicherplatz wieder freizugeben:
8.2. Memberfunktionen Damit eine Class / ein Struct nach der Initialisierung die Kopie eines
anderen werden kann, muss der operator= überladen werden: delete zeiger;
Memberfunktionen sind Funktionen, die als Mitglieder einer Class
delete[] zeiger; // ein mit new[] erstellter Array
oder eines Structs deklariert werden. Ihre Deklaration erfolgt in classname& operator=(const classname& arg) { … return kann nur mit delete[] gelöscht werden
der Definition der Class respektive des Structs: *this; }
• Alle Pointer, die auf das gelöschte Objekt zeigen, sollten auf
class classname {
T mem1;
8.6. Zeiger auf Structs und Classes nullptr gesetzt werden
T mem2; Memberfunktionen einer Class / eines Structs haben ein implizites • Speicherleck: Wenn kein delete den Speicherplatz freigibt,
private: Argument, das aufrufende Objekt. this ist ein Zeiger darauf: bleiben mit new erstellte Objekte über Scope hinweg erhalten
T functionname1(T arg) {mem1 = arg; }
public:
struct structname { • Ein delete ohne new verursacht einen Laufzeitfehler
T mem1;
T functionname2(T arg); T functionname() { return this->mem1; } • Nur dynamisch allozierte, mit new oder new[] erstellte Objekte
}; }; müssen gelöscht werden
Programmieraufgaben Fibonacci-Primzahlen Längste Aufsteigende Teilsequenz
Dezimal zu Binär
Primzahlen
Fakultät
Dynamic Queue
Primfaktorzerlegung
KGV & KGT
Ziffern Umkehren
Rekursion (Dungeon)
Fibonacci-Zahlen
EBNF Zerlegung eines Sets in Intervalle
Seating = ‘H’ Distance Persons ‘E’
Persons = Person {Person}
Person = (Vaccinated | Unvaccinated) Distance
Vaccinated = ‘V’ {Vaccinated}
Unvaccinated = ‘U’
Distance = ‘/’
Smart Pointers
Quicksort
Bereits implementierte Funktionen zur Lösung einer EBNF:
#include <istream>
consume(std::istream& is, ‘arg’); // gibt true
zurück, wenn das nächste Zeichen im Stream arg ist
consume(std::istream& is, «arg»); // ermöglicht es Konstruktor & Destruktor
mehrere Zeichen auf einmal einzulesen
peek(std::istream& is); // nächstes Zeichen wird
zurückgegeben
lookahead(std::istream& is); // gibt erstes Zeichen
zurück, das kein Leerzeichen ist
!lookahead(std::istream& is) // gibt true zurück, wenn
alle Zeichen konsumiert worden sind
stream >> std::ws; // extrahiert führende Leerzeichen
stream >> std::noskipws; // Leerzeichen werden
mitgelesen
Destruktor als Funktion Minimax
Destruktor bei Smart Pointer
Iteratoren
Rule of Three
Operatorüberladung am Beispiel eines Structs
Anhang
ASCII Tabelle
Dezimal Hexal char
48 30 ‘0’
57 39 ‘9’
65 41 ‘A’
90 5A ‘Z’
97 61 ‘a’
122 7A ‘z’
Umrechnungstabelle Zahlensysteme
Hexadez.
Dezimal
Binär
𝟏𝟔𝟏 𝟏𝟔𝟐 𝟏𝟔𝟑
0001 1 1 16 256 4096
0010 2 2 32 512 8192
0011 3 3 48 768 12288
0100 4 4 64 1024 16384
0101 5 5 80 1280 20480
multipliziert mit
0110 6 6 96 1536 24576
0111 7 7 112 1792 28672
1000 8 8 128 2048 32768
1001 9 9 144 2304 36864
1010 10 A 160 2560 40960
1011 11 B 176 2816 45056
1100 12 C 192 3072 49152
1101 13 D 208 3328 53248
1110 14 E 224 3584 57344
1111 15 F 240 3840 61440
16 10000 28 11100 40 101000 52 110100
17 10001 29 11101 41 101001 53 110101
18 10010 30 11110 42 101010 54 110110
19 10011 31 11111 43 101011 55 110111
20 10100 32 100010 44 101100 56 111000
21 10101 33 100001 45 101101 57 111001
22 10110 34 100010 46 101110 58 111010
23 10111 35 100011 47 101111 59 111011
24 11000 36 100100 48 110000 60 111100
25 11001 37 100101 49 110001 61 111101
26 11010 38 100110 50 110010 62 111110
27 11011 39 100111 51 110011 63 111111
2.4. Umrechnungstabelle