Dynamische Datentypen

Ähnliche Dokumente
Verschlüsseln eines Bildes. Visuelle Kryptographie. Verschlüsseln eines Bildes. Verschlüsseln eines Bildes

C++ Teil 5. Sven Groß. 13. Mai Sven Groß (IGPM, RWTH Aachen) C++ Teil Mai / 18

Modellierung und Programmierung 1

C++ Teil 6. Sven Groß. 27. Mai Sven Groß (IGPM, RWTH Aachen) C++ Teil Mai / 14

Programmierung mit C Zeiger

Vorkurs C++ Programmierung

Das erste C++ Programm

Programmier-Befehle - Woche 10

C- Kurs 08 Zeiger. Dipl.- Inf. Jörn Hoffmann leipzig.de. Universität Leipzig Ins?tut für Informa?k Technische Informa?

Prof. W. Henrich Seite 1

Programmieren in C++ Überladen von Methoden und Operatoren

C++ Notnagel. Ziel, Inhalt. Programmieren in C++

Überblick. 6. Konstruktor und Destruktor - obligatorische Elementfunktionen einer Klasse

C++ - Container und dynamische Speicherverwaltung

Praxisorientierte Einführung in C++ Lektion: "Smart-Pointer"

Vorkurs C++ Programmierung

Grundlagen der Informatik 11. Zeiger

Einführung in den Einsatz von Objekt-Orientierung mit C++ I

Themen. Statische Methoden inline Methoden const Methoden this Zeiger Destruktor Kopierkonstruktor Überladen von Operatoren

Zeiger in C und C++ Zeiger in Java und C/C++

Advanced Programming in C

Programmieren in C. Speicher anfordern, Unions und Bitfelder. Prof. Dr. Nikolaus Wulff

Zeiger: Der Adressoperator &

Java I Vorlesung 6 Referenz-Datentypen

Grundlagen der Programmierung in C++ Arrays und Strings, Teil 1

Systemnahe Programmierung in C/C++

Vorkurs Informatik: Erste Schritte der Programmierung mit C++

Schwerpunkte. Verkettete Listen. Verkettete Listen: 7. Verkettete Strukturen: Listen. Überblick und Grundprinzip. Vergleich: Arrays verkettete Listen

Vorkurs Informatik: Erste Schritte der Programmierung mit C++

Wertebereich und Genauigkeit der Zahlendarstellung

DAP2 Praktikum Blatt 1

5. Abgeleitete Datentypen

Dr. Monika Meiler. Inhalt

18. Vererbung und Polymorphie

C++ Klassen, Vererbung. Philipp Lucas. Sebastian Hack. Wintersemester 2008/09. saarland.

1. Referenzdatentypen: Felder und Strings. Referenz- vs. einfache Datentypen. Rückblick: Einfache Datentypen (1) 4711 r

C++-Zusammenfassung. H. Schaudt. August 18, 2005

Unterlagen. CPP-Uebungen-08/

Inhalt. 4.5 Arbeit mit Zeigern (engl. Pointer)

Repetitorium Informatik (Java)

Methoden. von Objekten definiert werden, Methoden,, Zugriffsmethoden und Read-Only

Einstieg in die Informatik mit Java

Zusammenfassung des Handzettels für Programmieren in C

Zeiger und dynamischer Speicher

Variablen. Deklaration: «Datentyp» «Variablenname» Datentyp bestimmt Größe in Bytes: sizeof Beispiel: long int v; Größe: 4 Bytes

Kapitel 9: Klassen und höhere Datentypen. Klassen und höhere. Objekte, Felder, Methoden. Küchlin/Weber: Einführung in die Informatik

1.4. Funktionen. Objektorientierte Programmierung mit C++

Praxisorientierte Einführung in C++ Lektion: "Dynamische Speicherverwaltung"

Speicher und Adressraum

Speicherklassen (1) Lokale Variablen

Erste Schritte der Programmierung in C

2. Programmierung in C

Übung zur Vorlesung Programmieren in C

Unterprogramme. Funktionen. Bedeutung von Funktionen in C++ Definition einer Funktion. Definition einer Prozedur

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 11/12 1. Kapitel 11. Listen. Listen

Arrays (Felder/Vektoren)

Funktionen in Python

7 Funktionen. 7.1 Definition. Prototyp-Syntax: {Speicherklasse} {Typ} Name ({formale Parameter});

Praxis der Programmierung

AuD-Tafelübung T-B5b

Pointer. Variablen. Pointer. Ein elementares Beispiel. Pointer in C

Gliederung. Tutorium zur Vorlesung. Gliederung. Gliederung. 1. Gliederung der Informatik. 1. Gliederung der Informatik. 1. Gliederung der Informatik

Kapitel 8. Programmierkurs. Methoden. 8.1 Methoden

Meeting C++ C++11 R-Value Referenzen

Elementare Datentypen in C++

Programmiersprache 1 (C++) Prof. Dr. Stefan Enderle NTA Isny

Deklarationen in C. Prof. Dr. Margarita Esponda

Kurzeinführung in C/C++ Elementare Datentypen in C++

Probeklausur: Programmierung WS04/05

Aufbau von Klassen. class punkt {...

3. Wahrheitswerte. Boolesche Funktionen; der Typ bool; logische und relationale Operatoren; Kurzschlussauswertung; Assertions und Konstanten

Algorithmen zur Datenanalyse in C++

Grundlagen der Programmierung Prof. H. Mössenböck. 10. Klassen

Klassendefinitionen verstehen

10. Klassen. Prof. Dr. Markus Gross Informatik I für D-ITET (WS 03/04)

Organisatorisches. drei Gruppen Gruppe 1: 10:10-11:40, Gruppe 2: 11:45-13:15 Gruppe 3: 13:20-14:50

Algorithmen zur Datenanalyse in C++

6. Zeiger Allgemeines Definition eines Zeigers

1. Grundzüge der Objektorientierung 2. Methoden, Unterprogramme und Parameter 3. Datenabstraktion 4. Konstruktoren 5. Vordefinierte Klassen

Strukturen & Math. Strukturen und Vektoren. Allokieren eines Vektors. Zugriff auf Strukturen. Freigeben eines Vektors

Einführung Datentypen Verzweigung Schleifen. Java Crashkurs. Kim-Manuel Klein May 4, 2015

Name: Klausur Informatik III WS 2003/04

Javakurs für Anfänger

Pass by Value Pass by Reference Defaults, Overloading, variable Parameteranzahl

Einstieg in die Informatik mit Java

Einstieg in die Informatik mit Java

Einstieg in die Informatik mit Java

Transkript:

Dynamische Datentypen

Tupel und Folgen o Wertebereich eines Structs / einer Klasse: T1 T2... Tk Werte sind k-tupel

Tupel und Folgen o Wertebereich eines Structs / einer Klasse: T1 T2... Tk Werte sind k-tupel o Heute: o Typ mit Wertebereich T k Werte sind k-folgen

Tupel und Folgen o Wertebereich eines Structs / einer Klasse: T1 T2... Tk Werte sind k-tupel o Heute: o Typ mit Wertebereich T k Werte sind k-folgen o Typ mit Wertebereich T * Werte sind alle Folgen

Tupel und Folgen o Wertebereich eines Structs / einer Klasse: T1 T2... Tk Werte sind k-tupel o Heute: o Typ mit Wertebereich T k Werte sind k-folgen o Typ mit Wertebereich T * Werte sind alle Folgen Speicherbedarf eines Wertes ist bei Kompilierung nicht notwendigerweise bekannt: Konzept dynamischer Speicher

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Streiche alle echten Vielfachen von 2

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 5 7 9 11 13 15 17 Streiche alle echten Vielfachen von 2

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 5 7 9 11 13 15 17 Streiche alle echten Vielfachen von 2 und gehe zur nächsten Zahl

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 5 7 9 11 13 15 17 Streiche alle echten Vielfachen von 3

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 5 7 11 13 17 Streiche alle echten Vielfachen von 3

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 5 7 11 13 17 Streiche alle echten Vielfachen von 3 und gehe zur nächsten Zahl

Felder Das Sieb des Eratosthenes o berechnet alle Primzahlen in [2,n) o Methode: Ausstreichen der Nicht- Primzahlen 2 3 5 7 11 13 17 Am Ende des Streichungsprozesses bleiben genau die Primzahlen übrig!

Felder: Implementierung Sieb des Eratosthenes int main() { // specification of n const unsigned int n = 1000; Primzahlen < n // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool crossed_out[n]; for (unsigned int i = 0; i < n; ++i) crossed_out[i] = false; // computation and output std::cout << "Prime numbers in [0," << n << "): "; for (unsigned int i = 2; i < n; ++i) if (!crossed_out[i]) { // i is prime std::cout << i << " "; // cross out all proper multiples of i for (unsigned int m = 2*i; m < n; m += i) crossed_out[m] = true; } std::cout << "\n"; } return 0;

Felder: Implementierung Sieb des Eratosthenes int main() { // specification of n const unsigned int n = 1000; // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool crossed_out[n]; for (unsigned int i = 0; i < n; ++i) crossed_out[i] = false; // computation and output std::cout << "Prime numbers in [0," << n << "): "; for (unsigned int i = 2; i < n; ++i) if (!crossed_out[i]) { // i is prime std::cout << i << " "; // cross out all proper multiples of i for (unsigned int m = 2*i; m < n; m += i) crossed_out[m] = true; } std::cout << "\n"; Liste: crossed_out[i] gibt an, ob i schon ausgestrichen wurde } return 0;

Felder: Implementierung Sieb des Eratosthenes int main() { // specification of n const unsigned int n = 1000; // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool crossed_out[n]; for (unsigned int i = 0; i < n; ++i) crossed_out[i] = false; // computation and output std::cout << "Prime numbers in [0," << n << "): "; for (unsigned int i = 2; i < n; ++i) if (!crossed_out[i]) { // i is prime std::cout << i << " "; // cross out all proper multiples of i for (unsigned int m = 2*i; m < n; m += i) crossed_out[m] = true; } std::cout << "\n"; Das Sieb: gehe zur jeweils nächsten nichtgestrichenen Zahl (diese ist Primzahl) und streiche alle echten Vielfachen aus } return 0;

Felder: Definition Deklaration eines Feldes (array): T a [expr ] konstanter Ausdruck, dessen Wert die Länge des Feldes angibt. neue Variable des Feld-Typs zugrundeliegender Typ

Felder: Definition Deklaration eines Feldes (array): T a [expr ] Wert k ist bei Kompilierung bekannt konstanter Ausdruck, dessen Wert die Länge des Feldes angibt. neue Variable des Feld-Typs zugrundeliegender Typ Mögliche Werte von a : T k

Felder variabler Länge? Flexibler wäre: int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; } // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool crossed_out[n];...

Felder variabler Länge? Flexibler wäre: int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; } // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool crossed_out[n];... Das geht auch, aber nicht so!

Felder: Speicherlayout o Ein Feld belegt einen zusammenhängenden Speicherbereich

Felder: Speicherlayout o Ein Feld belegt einen zusammenhängenden Speicherbereich Speicher für jeweils einen Wert von T

Felder: Speicherlayout o Ein Feld belegt einen zusammenhängenden Speicherbereich Beispiel: Feld mit 4 Elementen Speicher für jeweils einen Wert von T

Felder: Random Access Der L-Wert Typ T a [ i ] bezieht sich auf das i -te Element des Feldes a (Zählung beginnt bei 0)

Felder: Random Access Der L-Wert Typ T a [ i ] bezieht sich auf das i -te Element des Feldes a (Zählung beginnt bei 0) a[0] a[1] a[2] a[3]

Felder: Random Access o ist sehr effizient: p : Adresse von a s : Speicherbedarf von T

Felder: Random Access o ist sehr effizient: p : Adresse von a p + si : Adresse von a[i] s : Speicherbedarf von T a[i]

Felder sind speziell... o Man kann Felder nicht mit Feldern initialisieren (nur direkt)

Felder sind speziell... o Man kann Felder nicht mit Feldern initialisieren (nur direkt) int a[5] = {4,3,5,2,1}; // array of type int[5]

Felder sind speziell... o Man kann Felder nicht mit Feldern initialisieren (nur direkt) int a[] = {4,3,5,2,1}; // array of type int[5] Grösse kann weggelassen werden, da deduzierbar

Felder sind speziell... o Man kann Felder nicht mit Feldern initialisieren (nur direkt) int a[5] = {4,3,5,2,1}; // array of type int[5] int b[5] = a; // error: we cannot initialize from an array

Felder sind speziell... o Man kann Felder nicht mit Feldern initialisieren (nur direkt) int a[5] = {4,3,5,2,1}; // array of type int[5] int b[5] = a; // error: we cannot initialize from an array o Man kann Feldern keine Felder zuweisen int a[5] = {4,3,5,2,1}; // array of type int[5] int b[5]; b = a; // error: we cannot assign to an array

Felder sind speziell: warum? o Felder sind Erblast der Sprache C und aus heutiger Sicht primitiv

Felder sind speziell: warum? o Felder sind Erblast der Sprache C und aus heutiger Sicht primitiv o Ein Feld merkt sich die Anzahl seiner Elemente nicht! o o kein Problem bei Random Access, aber beim Initialisieren / Zuweisen ist dann nicht klar, wieviele Elemente kopiert werden müssen

Felder sind speziell: warum? o Felder sind Erblast der Sprache C und aus heutiger Sicht primitiv o Ein Feld merkt sich die Anzahl seiner Elemente nicht! o o kein Problem bei Random Access, aber beim Initialisieren / Zuweisen ist dann nicht klar, wieviele Elemente kopiert werden müssen int a[5] = {4,3,5,2,1}; int b[5]; b = a; // oops, how many elements do a and b have?

Felder sind speziell: warum? o Felder sind Erblast der Sprache C und aus heutiger Sicht primitiv o Ein Feld merkt sich die Anzahl seiner Elemente nicht! o o solange legale Indizes verwendet werden! kein Problem bei Random Access, aber beim Initialisieren / Zuweisen ist dann nicht klar, wieviele Elemente kopiert werden müssen int a[5] = {4,3,5,2,1}; int b[5]; b = a; // oops, how many elements do a and b have?

Felder sind trotzdem nützlich o als extrem effiziente Container für viele Daten des gleichen Typs o als Basis für nicht-primitive Felder variabler Länge, die man dann auch kopieren und zuweisen kann (Realisierung mit Klassenkonzept)

Felder als Funktionsparameter // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int a[], int n) { int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; }

Felder als Funktionsparameter // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int a[], int n) { } int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; Wie wird der formale Parameter mit dem Aufrufparameter initialisiert, wenn dies für Felder nicht geht?

Felder als Funktionsparameter // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int a[], int n) { } int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; Antwort: Bei der Initialisierung wird nur die Adresse des Feldes kopiert (wie bei Referenz-Parametern), aber explizit als Zeiger.

Zeiger-Typen T * sprich: Zeiger auf T zugrundeliegender Typ o T * hat als mögliche Werte Adressen von Objekten des Typs T

Feld-nach-Zeiger-Konversion o Es gibt keine formalen Parameter von Feld-Typ T []; dieser Typ wird vom Compiler auf T * angepasst o Bei Aufrufparameter vom Feld-Typ T [] wird der formale Parameter mit der Adresse des ersten Elements des Feldes initialisiert

Feld-nach-Zeiger-Konversion // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int a[], int n) { int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; } gibt s eigentlich gar nicht, wird angepasst

Feld-nach-Zeiger-Konversion // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int* a, int n) { int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; } passender Zeiger-Typ const int*

Feld-nach-Zeiger-Konversion // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int* a, int n) { int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; } gibt s eigentlich gar nicht, wird angepasst auf passenden Zeiger-Typ const int* Aber was heisst das für Zeiger?

Zeiger-Arithmetik Man kann mit Adressen rechnen: o s = Speicherbedarf für Wert von T o ptr = Zeiger vom Typ T * mit Wert p

Zeiger-Arithmetik Man kann mit Adressen rechnen: o s = Speicherbedarf für Wert von T o ptr = Zeiger vom Typ T * mit Wert p o ptr + expr hat Wert p + s i Typ int, Wert i Adresse des Objektes i Speicherplätze weiter rechts

Zeiger-Arithmetik Man kann mit Adressen rechnen: o s = Speicherbedarf für Wert von T o ptr = Zeiger vom Typ T * mit Wert p o ptr [expr ]: Objekt an Adresse p + s i Typ int, Wert i Adresse des Objektes i Speicherplätze weiter rechts

Zeiger-Arithmetik // PRE: a has size n or larger, n > 0 // POST: computes the maximum of the numbers // a[0],...,a[n-1]. int max_n (const int* a, int n) { int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i]; return max; } gleiche Semantik wie beim Feld!

Der Adressoperator o gibt die Adresse eines L-Wertes &expr R-Wert vom Typ T * &expr expr L-Wert vom Typ T

Der Dereferenzierungsoperator o gibt das Objekt hinter einer Adresse *expr L-Wert vom Typ T expr *expr R-Wert vom Typ T *

Der Dereferenzierungsoperator o gibt das Objekt hinter einer Adresse *expr L-Wert vom Typ T expr *expr R-Wert vom Typ T * *(&expr ) == expr

Dynamischer Speicher o wie besorgen wir Speicher, der bei Kompilierung nicht vorhersehbar ist?

Dynamischer Speicher o wie besorgen wir Speicher, der bei Kompilierung nicht vorhersehbar ist? o o Sieb des Eratosthenes mit Eingabe von n Allgemein: Feld nicht-konstanter Grösse

New und Delete new T Ausdruck vom Typ T * (Zeiger) new-operator

New und Delete new T Ausdruck vom Typ T * (Zeiger) new-operator o Semantik: neuer Speicher für ein Objekt vom Typ T wird angelegt; der Wert des Ausdrucks ist dessen Adresse

New und Delete new T [expr ] Ausdruck vom Typ T * (Zeiger) new-operator Typ int, Wert n ; expr nicht notwendigerweise konstant o Semantik: neuer Speicher für ein Feld der Länge n mit zugrundeliegendem Typ T wird angelegt; Wert des Ausdrucks ist Adresse des ersten Elements

New und Delete Mit new erzeugte Objekte haben dynamische Speicherdauer: sie leben, bis sie explizit gelöscht werden.

New und Delete Mit new erzeugte Objekte haben dynamische Speicherdauer: sie leben, bis sie explizit gelöscht werden: delete expr Typ void delete-operator Zeiger vom Typ T *, der auf ein vorher mit new erzeugtes Objekt verweist; Speicher wird wieder freigegeben.

New und Delete Mit new erzeugte Objekte haben dynamische Speicherdauer: sie leben, bis sie explizit gelöscht werden. delete[] expr Typ void delete-operator Zeiger vom Typ T *, der auf ein vorher mit new erzeugtes Feld verweist; Speicher wird wieder freigegeben.

Sieb des Eratosthenes: Erste dynamische Version int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; } // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool* crossed_out = new bool[n];...... delete[] crossed_out; return 0; Berechnungen wie vorher

Probleme mit new / delete o delete kann leicht vergessen werden (Speicherleck; kann auf Dauer zu Speicherüberlauf führen)

Probleme mit new / delete o delete kann leicht vergessen werden (Speicherleck; kann auf Dauer zu Speicherüberlauf führen) o Initialisierung und Zuweisung von Feldern sind weiterhin nicht möglich

Probleme mit new / delete o delete kann leicht vergessen werden (Speicherleck; kann auf Dauer zu Speicherüberlauf führen) o Initialisierung und Zuweisung von Feldern sind weiterhin nicht möglich Lösung mit eigener Feld-Klasse

Eine Klasse für Felder: Daten- Mitglieder und Konstruktor class array { public: typedef bool T; // nested type, easy to change // --- Constructor from int --------------------------- // PRE: n >= 0 // POST: *this is initialized with an array of length n array (int n) : ptr (new T[n]), size (n) {} private: T* ptr; // pointer to first element int size; // number of elements };

Eine Klasse für Felder: Random Access o Indexzugriff mittels Überladung von operator[](als Mitglieds-Funktion)

Eine Klasse für Felder: Random Access o Indexzugriff mittels Überladung von operator[](als Mitglieds-Funktion) // --- Index operator (const) ------------------------- // PRE: the array has length at least i // POST: return value is i-th element (as a copy) const T& operator[] (int i) const { return ptr[i]; }

Eine Klasse für Felder: Random Access o Indexzugriff mittels Überladung von operator[](als Mitglieds-Funktion) // --- Index operator (const) ------------------------- // PRE: the array has length at least i // POST: return value is i-th element (as a copy) const T& operator[] (int i) const { } return ptr[i]; Problem: erlaubt keinen schreibenden Zugriff wie in crossed_out[i] = false;

Eine Klasse für Felder: Random Access o Zwei Überladungen von operator[]! // --- Index operator (const) ------------------------- // PRE: the array has length at least i // POST: return value is i-th element (as a copy) const T& operator[] (int i) const { return ptr[i]; } // --- Index operator (non-const) ------------------------- // PRE: the array has length at least i // POST: return value is i-th element (as a reference) T& operator[] (int i) { return ptr[i]; }

Eine Klasse für Felder: Sieb des Eratosthenes int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] bool* crossed_out = new bool[n];...... delete[] crossed_out; return 0; } Bisher

Eine Klasse für Felder: Sieb des Eratosthenes int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] array crossed_out (n); // constructor call...... delete[] crossed_out; return 0; } Neu

Eine Klasse für Felder: Sieb des Eratosthenes int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; } // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] array crossed_out (n); // constructor call...... delete[] crossed_out; return 0; Fehler: crossed_out ist kein Standard-Feld, delete ist nicht verfügbar!

Eine Klasse für Felder: Sieb des Eratosthenes int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; } // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] array crossed_out (n); // constructor call...... return 0; delete weglassen geht, aber dann entsteht ein Speicherleck

Eine Klasse für Felder: Sieb des Eratosthenes int main() { // input of n std::cout << Compute prime numbers in [2, n) for n =? ; unsigned int n; std::cin >> n; // definition and initialization: provides us with // Booleans crossed_out[0],..., crossed_out[n-1] array crossed_out (n); // constructor call...... } return 0; Diese Variable hat automatische Speicherdauer; dynamischer Speicher sollte mit der Variable automatisch verschwinden!

Eine Klasse für Felder: Der Destruktor o spezielle Mitglieds-Funktion (Anti-Konstruktor), die automatisch für jedes freizugebende Klassen-Objekt aufgerufen wird

Eine Klasse für Felder: Der Destruktor o spezielle Mitglieds-Funktion (Anti-Konstruktor), die automatisch für jedes freizugebende Klassen-Objekt aufgerufen wird // --- Destructor ------------------------------------- ~array () { delete[] ptr; } Destruktorname: ~T (keine Parameter)

Probleme mit new / delete o delete kann leicht vergessen werden (Speicherleck; kann auf Dauer zu Speicherüberlauf führen) ok! o Initialisierung und Zuweisung von Feldern sind weiterhin nicht möglich Lösung mit eigener Feld-Klasse

Probleme mit new / delete o delete kann leicht vergessen werden (Speicherleck; kann auf Dauer zu Speicherüberlauf führen) ok! o Initialisierung und Zuweisung von Feldern sind weiterhin nicht möglich Lösung mit eigener Feld-Klasse

Eine Klasse für Felder: Der Copy-Konstruktor o...der Klasse T ist der eindeutige Konstruktor mit Deklaration T (const T& x);

Eine Klasse für Felder: Der Copy-Konstruktor o...der Klasse T ist der eindeutige Konstruktor mit Deklaration T (const T& x); o wird automatisch aufgerufen, wenn Werte vom Typ T mit Werten vom Typ T initialisiert werden

Eine Klasse für Felder: Der Copy-Konstruktor o...der Klasse T ist der eindeutige Konstruktor mit Deklaration T (const T& x); o wird automatisch aufgerufen, wenn Werte vom Typ T mit Werten vom Typ T initialisiert werden o T x = t; // t vom Typ T

Eine Klasse für Felder: Der Copy-Konstruktor o...der Klasse T ist der eindeutige Konstruktor mit Deklaration T (const T& x); o wird automatisch aufgerufen, wenn Werte vom Typ T mit Werten vom Typ T initialisiert werden o T x (t); // t vom Typ T

Eine Klasse für Felder: Der Copy-Konstruktor o...der Klasse T ist der eindeutige Konstruktor mit Deklaration T (const T& x); o wird automatisch aufgerufen, wenn Werte vom Typ T mit Werten vom Typ T initialisiert werden o Initialisierung von formalen Funktionsparametern und Rückgabewerten

Eine Klasse für Felder: Der Copy-Konstruktor // --- Copy Constructor ------------------------------- // POST: *this is initialized from a array (const array& a) : ptr (new T[a.size]), size (a.size) { // now copy the elements of a into *this for (int i = 0; i < size; ++i) ptr[i] = a[i]; }

Eine Klasse für Felder: Der Copy-Konstruktor // --- Copy Constructor ------------------------------- // POST: *this is initialized from a array (const array& a) { } : ptr (new T[a.size]), size (a.size) // now copy the elements of a into *this for (int i = 0; i < size; ++i) ptr[i] = a[i]; Warum ist const array& a hier zwingend notwendig, während array a nicht geht?

Eine Klasse für Felder: Der Copy-Konstruktor // --- Copy Constructor ------------------------------- // POST: *this is initialized from a array (const array& a) { } : ptr (new T[a.size]), size (a.size) // now copy the elements of a into *this for (int i = 0; i < size; ++i) ptr[i] = a[i]; Aufruf eines Copy-Konstruktors mit Deklaration array (array a); müsste den Copy- Konstruktor aufrufen (Initialisierung des formalen Parameters!); unendliche Rekursion!

Eine Klasse für Felder: Der Copy-Konstruktor o Falls kein Copy-Konstruktor deklariert ist, so wird er automatisch erzeugt (und initialisiert mitgliedsweise)

Eine Klasse für Felder: Der Copy-Konstruktor o Falls kein Copy-Konstruktor deklariert ist, so wird er automatisch erzeugt (und initialisiert mitgliedsweise) Das ist für unsere Klasse array nicht das, was wir wollen (es wird nur der Zeiger ptr kopiert, nicht jedoch das dahinterliegende Feld wir erhalten Alias-Semantik)!

Eine Klasse für Felder: Der Copy-Konstruktor Nun können wir zum Beispiel schreiben: array a(3); a[0] = true; a[1] = false; a[2] = false; // a == {true, false, false} array b(a); // b == {true, false, false} b[2] = true; // b == {true, false, true}

Eine Klasse für Felder: Der Copy-Konstruktor Nun können wir zum Beispiel schreiben: array a(3); a[0] = true; a[1] = false; a[2] = false; // a == {true, false, false} Initialisierung von b durch a array b(a); // b == {true, false, false} b[2] = true; // b == {true, false, true}

Eine Klasse für Felder: Der Zuweisungsoperator o Überladung von operator= als Mitglieds-Funktion

Eine Klasse für Felder: Der Zuweisungsoperator o Überladung von operator= als Mitglieds-Funktion o ähnlich wie Copy-Konstruktor ohne Initialisierer, aber zusätzlich o o Freigabe des Speichers für den alten Wert Vermeidung von Selbstzuweisungen

Eine Klasse für Felder: Der Zuweisungsoperator array& operator= (const array& a) { // avoid self-assignments if (this!= &a) { // free old memory, and get new memory of // appropriate size delete[] ptr; ptr = new T[a.size]; // copy the elements of a into *this for (int i = 0; i < size; ++i) ptr[i] = a[i]; } return *this; }

Eine Klasse für Felder: Der Zuweisungsoperator array& operator= (const array& a) { // avoid self-assignments if (this!= &a) { // free old memory, and get new memory of // appropriate size delete[] ptr; ptr = new T[a.size]; // copy the elements of a into *this for (int i = 0; i < size; ++i) ptr[i] = a[i]; } return *this; } Test auf Selbstzuweisung ist wichtig, um versehentliches Löschen des zuzuweisenden Wertes zu vermeiden.

Eine Klasse für Felder: Der Zuweisungsoperator o Falls kein Zuweisungsoperator deklariert ist, so wird er automatisch erzeugt (und weist mitgliedsweise zu) Das ist für unsere Klasse array nicht das, was wir wollen (es wird nur der Zeiger ptr kopiert, nicht jedoch das dahinterliegende Feld wir erhalten Alias-Semantik)!

Eine Klasse für Felder: Der Zuweisungsoperator Nun können wir zum Beispiel schreiben: array a(3); a[0] = true; a[1] = false; a[2] = false; // a == {true, false, false} Zuweisung von b an a array b(2); b = a; // b == {true, false, false} b[2] = true; // b == {true, false, true}

Eine Klasse für Felder o o o array.h array.c Bibliothek eratosthenes2.c Anwendung

Dynamischer Datentyp o Typ, der dynamischen Speicher verwaltet (z.b. unsere Klasse für Felder)

Dynamischer Datentyp o Typ, der dynamischen Speicher verwaltet (z.b. unsere Klasse für Felder) o andere typische Anwendungen: o o o o Listen Stapel Bäume Graphen

Dynamischer Datentyp o sollte immer mindestens o o o o Konstruktoren Destruktor Copy-Konstruktor Zuweisungsoperator haben.

Vektoren

Vektoren o sind die Felder der Standardbibliothek

Vektoren o sind die Felder der Standardbibliothek o std::vector<t > für zugrundeliegenden Typ T

Vektoren o sind die Felder der Standardbibliothek o std::vector<t > für zugrundeliegenden Typ T o können noch mehr, z.b: o Elemente am Ende anhängen und löschen

Vektoren o sind die Felder der Standardbibliothek o std::vector<t > für zugrundeliegenden Typ T o können noch mehr, z.b: o Elemente am Ende anhängen und löschen o repräsentieren damit die Menge T * aller endlichen Folgen über T

Vektoren o sind die Felder der Standardbibliothek o std::vector<t > für zugrundeliegenden Typ T o können noch mehr, z.b: o Elemente am Ende anhängen und löschen o repräsentieren damit die Menge T * aller endlichen Folgen über T eratosthenes3.c