From cb40c537de339eaa3737d99a8db8f2c4f1ae046c Mon Sep 17 00:00:00 2001 From: sulzmann Date: Tue, 14 Nov 2023 16:43:43 +0100 Subject: [PATCH] Add files via upload --- lec-cpp-advanced-mem.html | 653 +++++++++++++++++++++++++++++++++++++ lec-cpp-advanced-poly.html | 624 +++++++++++++++++++++++++++++++++++ semWi23-24.html | 302 +++++++++++++++++ 3 files changed, 1579 insertions(+) create mode 100644 lec-cpp-advanced-mem.html create mode 100644 lec-cpp-advanced-poly.html create mode 100644 semWi23-24.html diff --git a/lec-cpp-advanced-mem.html b/lec-cpp-advanced-mem.html new file mode 100644 index 0000000..fdd7589 --- /dev/null +++ b/lec-cpp-advanced-mem.html @@ -0,0 +1,653 @@ + + + + + + + + + Die Programmiersprache C++ - Speicherverwaltung + + + + + +
+

Die Programmiersprache C++ - Speicherverwaltung

+

+Martin Sulzmann +

+
+
+

Übersicht

+

Manuelle Speicherverwaltung in C++ ist trickreich

+ +

Abhilfen

+ +

Wir verwenden ab jetzt C++11

+
+
+

Beispiel (Speicherleck)

+ +

Zu jedem “malloc” Bedarfs es eines “free” zur richtigen Zeit!

+
#include <stdlib.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+
+int length(char *s) {
+  int n = 0;
+  while(*s != '\0') {
+    n++;
+    s++;
+  }
+
+  return n;
+}
+
+char* reverse(char* s) {
+  const int n = length(s);
+  char* t = (char*)malloc(n + 1);
+  int i;
+
+  for(i = 0; i < n; i++) {
+    t[i] = s[n-1-i];
+  }
+  t[n] = '\0';
+
+  return t;
+}
+
+void testReverse() {
+  char s[] = "Hallo";
+
+  cout << "\n" << s;
+
+  cout << "\n" << reverse(s);
+
+  cout << "\n" << reverse(reverse(s));
+
+}
+
+
+void testReverse2() {
+  char s[] = "Hallo";
+
+  cout << "\n" << s;
+
+  char* s1 = reverse(s);
+  cout << "\n" << s1;
+  free(s1);
+
+
+  char* s2 = reverse(s);
+  char* s3 = reverse(s2);
+  cout << "\n" << s3;
+  free(s2);
+  free(s3);
+
+}
+
+int main() {
+
+  testReverse();
+
+  testReverse2();
+
+
+  return 0;
+}
+
+
+

Resource Acquisition Is Initialization (RAII)

+ +

Konstruktor legt Objekt an auf dem “Stack”.

+

Destruktor wird aufgerufen sobald das Objekt freigegeben wird. +Annahme ist, dass das Objekt auf dem “Stack” liegt.

+

Dadurch wird garantiert, dass es zu jedem “new” ein passendes +“delete” gibt.

+

In C++, wird “malloc” ersetzt durch “new” und “free” wird ersetzt +durch “delete”.

+
#include <stdlib.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+// Resource Acquisition Is Initialization (RAII)
+
+class String {
+public:
+  String() {
+    s = new char[1];                // (char*)malloc(1);
+    s[0] = '\0';
+  }
+
+  String(char* t) {
+    int n = length_(t);
+    s = new char[n+1];
+
+    for(int i=0; i<n; i++) {
+      s[i] = t[i];
+    }
+    s[n] = '\0';
+  }
+
+
+  ~String() {
+    delete(s);                      // free(s);
+  }
+
+  int length() {
+    return(length_(this->s));
+  }
+
+  void reverse() {
+    int n = this->length();
+    char* t = new char[n+1];
+
+    for(int i = 0; i < n; i++) {
+      t[i] = s[n-1-i];
+    }
+    t[n] = '\0';
+
+    delete(s);
+    s = t;
+  }
+
+
+
+private:
+  char* s;
+
+  int length_(char *s) {
+       int n = 0;
+       while(*s != '\0') {
+         n++;
+         s++;
+       }
+
+       return n;
+  }
+
+  // Friends can access private attributes.
+  friend ostream& operator<< (ostream &out, String &s);
+
+};
+
+
+ostream& operator<< (ostream &out, String &str) {
+  for(int i=0; i<str.length(); i++) {
+    out << str.s[i];
+  }
+  return out;
+}
+
+void testReverse() {
+  char s[] = "Hallo";
+
+  String str(s);
+
+  cout << "\n" << str;
+
+  str.reverse();
+  cout << "\n" << str;;
+
+
+}
+
+int main() {
+
+  testReverse();
+
+  return 0;
+}
+
+
+

Problem - Kopieren mit Zeigern

+ +
#include <stdlib.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+// Kopieren mit Zeiger
+// Zwei-faches delete!
+
+class String {
+public:
+  String() {
+    s = new char[1];
+    s[0] = '\0';
+  }
+
+  String(char* t) {
+    int n = length_(t);
+    s = new char[n+1];
+
+    for(int i=0; i<n; i++) {
+      s[i] = t[i];
+    }
+    s[n] = '\0';
+  }
+
+
+  ~String() {
+    delete(s);
+  }
+
+private:
+  char* s;
+
+  int length_(char *s) {
+       int n = 0;
+       while(*s != '\0') {
+         n++;
+         s++;
+       }
+
+       return n;
+  }
+
+};
+
+
+void f(String str2) {
+
+}
+// Destruktoraufruf fuer str2
+//  => delete str2.s
+
+int main() {
+  char s[] = "Hallo";
+
+  String str(s);
+
+  f(str); // str2.s = str.s
+
+  return 0;
+}
+// Destruktoraufruf fuer str2
+//  => delete str.s
+// Zweifaches delete!
+
+
+

Copy Semantik

+

Jedesmal wenn ein Objekt (mit einem Zeiger) kopiert wird

+ +

Wie und wann werden Objekte kopiert?

+ +

Beides kann überladen werden.

+
#include <stdlib.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+// Copy Semantik
+
+class String {
+public:
+  String() {
+    s = new char[1];
+    s[0] = '\0';
+  }
+
+  String(char* t) {
+    int n = length_(t);
+    s = new char[n+1];
+
+    copy_(t,n,s);
+    s[n] = '\0';
+  }
+
+  // Copy Konstruktor
+  // Standard Definition ist: this->s = src.s
+  // Gilt auch fuer Zuweisung.
+  String(const String& src) {
+
+    int n = length_(src.s);
+    this->s = new char[n+1];
+    copy_(src.s,n,this->s);
+    this->s[n] = '\0';
+
+  }
+
+  // Copy Zuweisung
+  String& operator=(const String& src) {
+
+    if(this != &src) {
+
+      delete this->s;
+      int n = length_(src.s);
+      this->s = new char[n+1];
+      copy_(src.s,n,this->s);
+      this->s[n] = '\0';
+
+    }
+
+    return *this;
+  }
+
+  ~String() {
+    delete(s);
+  }
+
+private:
+  char* s;
+
+  int length_(char *s) {
+       int n = 0;
+       while(*s != '\0') {
+         n++;
+         s++;
+       }
+
+       return n;
+  }
+
+  void copy_(char* s, int n, char* t) {
+       int i = 0;
+       while(i < n) {
+         t[i] = s[i];
+         i++;
+       }
+}
+
+};
+
+
+void f(String str2) {
+
+}
+
+int main() {
+  char s[] = "Hallo";
+
+  String str(s);
+
+  f(str); // Copy str
+
+  String str2 = str; // Copy str
+
+  f(str2);
+
+  return 0;
+}
+
+
+

Copy + Move Semantik

+

Jedes mal eine (deep)copy durchzuführen ist nicht sehr effizient.

+

Man betrachte

+ +

In solchen Fällen führen einen “move” durch

+
#include <stdlib.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+// Copy/Move Semantik
+
+class String {
+public:
+  String() {
+    s = new char[1];
+    s[0] = '\0';
+  }
+
+  String(char* t) {
+    int n = length_(t);
+    s = new char[n+1];
+
+    copy_(t,n,s);
+    s[n] = '\0';
+  }
+
+  // Copy Konstruktor
+  // Standard Definition ist: this->s = src.s
+  // Gilt auch fuer Move und Zuweisung.
+  String(const String& src) {
+
+    int n = length_(src.s);
+    this->s = new char[n+1];
+    copy_(src.s,n,this->s);
+    this->s[n] = '\0';
+
+  }
+
+  // Copy Zuweisung
+  String& operator=(const String& src) {
+
+    if(this != &src) {
+
+      delete this->s;
+      int n = length_(src.s);
+      this->s = new char[n+1];
+      copy_(src.s,n,this->s);
+      this->s[n] = '\0';
+
+    }
+
+    return *this;
+  }
+
+
+  // Move Konstruktor
+  String(String&& src) {
+    this->s = src.s;
+    src.s = nullptr;
+  }
+
+  // Move Zuweisung
+  String& operator=(String&& src) {
+
+    if(this != &src) {
+
+      delete this->s;
+      this->s = src.s;
+      src.s = nullptr;
+
+    }
+
+    return *this;
+  }
+
+  ~String() {
+    delete(s);
+  }
+
+  void print() {
+    cout << this->s;
+  }
+
+private:
+  char* s;
+
+  int length_(char *s) {
+       int n = 0;
+       while(*s != '\0') {
+         n++;
+         s++;
+       }
+
+       return n;
+  }
+
+  void copy_(char* s, int n, char* t) {
+       int i = 0;
+       while(i < n) {
+         t[i] = s[i];
+         i++;
+       }
+}
+
+};
+
+
+void f(String str2) {
+
+}
+
+int main() {
+  char s[] = "Hallo";
+
+  String str(s);
+
+  f(str); // Copy str
+
+  String str3 = str; // Copy str
+
+  f(str3);
+
+  f(String()); // Move eines temporaeren String Objekts
+
+  String str4 = move(str);
+
+  f(str4);
+
+  // str.print();
+  // Liefert segmentation fault
+  // wegen "move"
+
+  return 0;
+}
+
+
+

Zusammenfassung

+ +
    +
  1. Destruktor

  2. +
  3. “copy” Konstruktor (für lvalue refs)

  4. +
  5. “copy” Zuweisung (für lvalue refs)

  6. +
+ +

Definiere (geeignet)

+
    +
  1. Destruktor

  2. +
  3. “copy” Konstruktor (für lvalue refs)

  4. +
  5. “copy” Zuweisung (für lvalue refs)

  6. +
  7. “move” Konstruktor (für rvalue refs)

  8. +
  9. “move” Zuweisung (für rvalue refs)

  10. +
+ +
+ + diff --git a/lec-cpp-advanced-poly.html b/lec-cpp-advanced-poly.html new file mode 100644 index 0000000..4378250 --- /dev/null +++ b/lec-cpp-advanced-poly.html @@ -0,0 +1,624 @@ + + + + + + + + + Polymorphie in C++ + + + + + +
+

Polymorphie in C++

+

+Martin Sulzmann +

+
+
+

Polymorphie

+

Polymorphie (griechisch Vielgestaltigkeit) ist ein +Programmierkonzept. Zweck: Einheitliche Schnittstelle welche auf +verschiedene Typen anwendbar ist. Es gibt verschiedene Formen von +Polymorhpie, z.B.

+ +
+
+

Coercive Subtyping

+

Allgemeines Subtyping Prinzip:

+ +

Hier Coercive Subtyping: char +\leq +int +\leq +float +\leq +double

+

Beispiel

+
int func(float x) {
+    if (x < 1.0) return 1;
+    return 0;
+}
+
+void testCoerce() {
+ int arg = 1;
+ float res;
+
+ // Typkorrekt weil int <= float,
+ // d.h. jeder Wert vom Typ int kann auch an der
+ // Programmstelle verwendet werden, an welcher float erwartet wird.
+ res = func(arg);
+}
+
+
+// Compiler fuegt explizite Coercions ein!
+// Hier wird das ganze simuliert durch die Funktion coerce.
+
+
+float coerce(int x) {
+  return (float)x;
+}
+
+void testCoerceTranslated() {
+ int arg = 1;
+ float res;
+
+ res = coerce(func(coerce(arg)));
+}
+
+
+int main() {
+  testCoerce();
+
+  testCoerceTranslated();
+
+  return 1;
+}
+
+
+

Nominales Subtyping

+ +

Beispiel

+
// B <= A
+// abgeleitet aus Klassendeklaration, in der Literatur als "nominal subtyping" bekannt.
+class A {};
+class B : public A {};
+
+
+void h(A x) {}
+
+void g(B x) {}
+
+void testAB() {
+  A a;
+  B b;
+
+  h(a);     // OK
+  h(b);     // OK, weil B <= A
+  // g(a);     // NICHT OK
+  g(b);     // OK
+
+}
+
+int main() {
+
+  testAB();
+
+  return 1;
+}
+
+
+

Subtyping und Varianz

+

Betrachte Typkonstruktoren, z.B. Pointer Typen.

+

Welche Beziehung gilt zwischen int* und +float*?

+

Was ist mit B* und A*?

+

Pointer Subtyping is kovariant!

+

Falls +tst \leq s +dann +t*s*t* \leq s*.

+

Wieso kovariant? Damit man “generischen” Code schreiben kann, siehe +Generische Datentypen und Funktionen +in C

+

Beispiel

+
// B <= A
+// abgeleitet aus Klassendeklaration, in der Literatur als "nominal subtyping" bekannt.
+class A {};
+class B : public A {};
+
+
+void h(A x) {}
+
+void g(B x) {}
+
+void testAB() {
+  A a;
+  B b;
+
+  h(a);     // OK
+  h(b);     // OK, weil B <= A
+  // g(a);     // NICHT OK
+  g(b);     // OK
+
+}
+
+// Abgeleitete Subtypbeziehung:
+// B <= A   impliziert B* <= A*
+void h2(A* x) {}
+
+void g2(B* x) {}
+
+void testAB_2() {
+  A* a = new A();
+  B* b = new B();
+
+  h2(a);     // OK
+  h2(b);     // OK, weil B <= A und daher auch B* <= A*
+  // g2(a);     // NICHT OK
+  g2(b);     // OK
+
+  delete a;
+  delete b;
+}
+
+// Das gleiche gilt auch fuer Arrays.
+void h3(A x[]) {}
+
+void g3(B y[]) {}
+
+void testAB_3() {
+  A a[] = { A(), A(), B() };  // B <= A
+
+  B b[] = { B() };
+
+  h3(b);  // OK, weil aus B <= A folgt B[] <= A[]
+
+  g3(b);
+  // g3(a);  // Nicht OK
+}
+
+
+int main() {
+
+  testAB();
+  testAB_2();
+  testAB_3();
+
+  return 1;
+}
+

Vergleich zu Java

+

Array Subtyping in Java ist auch kovariant.

+

Das folgende Programm “crashes”.

+
void main() {
+String[] s;
+s = new String[10];
+test(s);
+}
+
+void test (Object [] a) {
+  a[1] = new Point(10,20); // Crash!
+}
+
+
+

Overloading

+

Gleicher Funktions-/Operator-Name aber verschiedene Verwendung +(Anzahl Argumente und deren Typen).

+

C++ Regel: Instanz muss eindeutig bestimmbar sein via +Argumenttypen.

+

Beispiel

+
#include <iostream>
+#include <string>
+using namespace std;
+
+int funny(int x, int y) {
+  return x;
+}
+
+char funny(char x, char y) {
+  return y;
+}
+
+void testFunny() {
+
+  cout << funny(1,2);
+  cout << funny('a', 'b');
+
+  // cout << funny(1, 'a');  // Ambiguous, nicht klar welche Instanz wir wollen
+
+}
+
+/*
+
+// Folgendes ist nicht erlaubt, weil
+// "functions that differ only in their return type cannot be overloaded"
+
+int funny2() {
+  return 1;
+}
+
+bool funny2() {
+  return true;
+}
+
+void testFunny2() {
+
+  bool x = funny2();
+
+  int y = funny2();
+
+}
+
+
+*/
+
+
+int main() {
+  testFunny();
+}
+
+
+

C++ Templates und Vergleich zu Java generics

+

C++ verwendet “Monomorphisation”: Für jede Instanz dupliziere den +Programmcode.

+ +

Java verwendet ein “generisches” Übersetzungsscheme: Ersetze jeden +Typparameter durch Object.

+ +

Beispiel

+
// requires C++11
+
+#include <iostream>
+#include <string>
+using namespace std;
+
+
+
+// C++ Templates und Vergleich zu Java generics
+/////////////////////////////////////////////////
+
+
+/////////////////////////////
+// Templates
+
+template<typename T>
+void mySwap(T& x, T&y) {
+  T tmp;
+
+  tmp = x; x = y; y = tmp;
+}
+
+void testTmp() {
+  int x = 1;
+  int y = 2;
+
+  mySwap<int>(x,y); // Instanzierung
+
+
+  float u = 1.0;
+  float v = 2.0;
+
+  mySwap(u,v); // Instanz <float> kann inferriert werden mit Hilfe der Argumente.
+
+}
+
+/*
+
+C++ verwendet "Monomorphisation".
+
+ - Für jede Instanz dupliziere den Programmcode
+
+ - Vorteil. Effizient, da keine Typecasts
+   notwendig sind. Typ-spezifischen Optimierungen etc.
+
+ - Nachteil. Codeduplikation
+
+Hier am Beispiel von oben.
+*/
+
+void mySwap_int(int& x, int&y) {
+  int tmp;
+
+  tmp = x; x = y; y = tmp;
+}
+
+void testTmp_mono() {
+  int x = 1;
+  int y = 2;
+
+  mySwap_int(x,y);
+
+}
+
+
+
+// Weiteres Template Beispiel.
+
+template<typename T>
+class Elem {
+  T val;
+public:
+  Elem(T init) {
+    val = init;
+  }
+  void print() {
+    cout << "\n" << val;
+  }
+  void replace(T x) {
+    val = x;
+  }
+};
+
+void testElem() {
+  Elem<int> e(1);
+  Elem<string> s("Hello");
+
+  e.print();
+  e.replace(2);
+  e.print();
+  s.print();
+  s.replace("Hallo");
+  s.print();
+
+}
+
+// Beachte.
+// Kein Typchecking von templates!
+// Nur Typchecking von Code erhalten
+// durch Monomorphisierung.
+
+struct Point {
+  int x;
+  int y;
+};
+
+
+void testElem2() {
+  Elem<struct Point> p({1,2}); // (P)
+
+  // Kein Typfehler.
+  // Erst durch Hinzunahme folgender Codezeile.
+
+  // p.print();
+
+}
+
+
+
+// Beachte:
+// Templates sind verschieden von "generics" in Java.
+// In C++
+//  - Monomorpization
+// In Java
+//  - Generische Uebersetzung
+
+// Java's generisches Uebersetzungs Schema am Beispiel.
+
+// 1. Ersetze alle Typparameter durch void*.
+// (In Java wird Object verwendet).
+
+class Elem_G {
+  void* val;
+public:
+  Elem_G(void* init) {
+    val = init;
+  }
+  void print() {
+    cout << "\n" << val;
+  }
+  void replace(void* x) {
+    val = x;
+  }
+};
+
+// Passt alles?
+
+void testElem_G() {
+  int i = 1;
+  Elem_G e(&i);
+  // int* <= void*
+
+  e.print();
+  int j = 2;
+  e.replace(&j);
+  e.print();
+}
+
+
+// Beachte.
+// Keine korrekte Behandlung von print.
+// Benoetigen "run-time type info"
+// im Falle von "<<"
+
+
+
+enum TYPE {
+  INT,
+  BOOL,
+  // and so on
+};
+
+class Elem_GG {
+  void* val;
+  enum TYPE t;
+public:
+  Elem_GG(void* init, enum TYPE ty) {
+    val = init;
+    t = ty;
+  }
+  void print() {
+    // Entspricht "instanceof" in Java.
+    switch (t) {
+      case INT: {
+    int* p = (int*)(val);
+          cout << "\n" << *p;
+          break;
+      }
+      case BOOL: {
+    bool* p = (bool*)(val);
+          cout << "\n" << *p;
+          break;
+      }
+    }
+  }
+  void replace(void* x) {
+    val = x;
+  }
+};
+
+
+void testElem_GG() {
+  int i = 1;
+  Elem_GG e(&i, INT);
+  // int* <= void*
+
+  e.print();
+  int j = 2;
+  e.replace(&j);
+  e.print();
+
+  bool b = true;
+  Elem_GG f(&b,BOOL);
+  f.print();
+}
+
+/*
+
+Zusammengefasst.
+
+Monomorphization in C++
+   + Effizient
+   - Code Duplikation
+
+Generische Uebersetzung in Java
+   + Generischer Code
+   +- Typchecks zur Laufzeit sind notwendig
+
+ */
+
+
+
+int main() {
+  // testAdd();
+  testElem();
+  testElem_G();
+  testElem_GG();
+}
+
+ + diff --git a/semWi23-24.html b/semWi23-24.html new file mode 100644 index 0000000..63e26d3 --- /dev/null +++ b/semWi23-24.html @@ -0,0 +1,302 @@ + + + + + + + + Softwareprojekt - Winter Semester 23/24 + + + + +
+

Softwareprojekt - Winter Semester 23/24

+

Martin Sulzmann

+
+

Beachte

+

Online test im ilias System sind verfügbar

+

Evaluation

+

1.Schriftliche Klausur, ohne Hilfsmittel, 90min

+
    +
  1. Übungsschein bei erfolgreicher Abgabe aller Übungen und Teilnahme +mit Ergebnis “Bestanden” der relevanten Online Tests. Details siehe hier

  2. +
  3. 10% Bonuspunkte sind maximal erreichbar durch Teilnahme an Online +Tests C++ Teil 1-5.

  4. +
+

Beachte:

+ +

Inhalt

+ +

Vorlesungsunterlagen

+

Siehe hier

+

Semesterablauf

+ +

Ablauf siehe unten.

+ + +