diff --git a/index.html b/index.html new file mode 100644 index 0000000..d42ef99 --- /dev/null +++ b/index.html @@ -0,0 +1,290 @@ + + + + + + + + Vorlesungsunterlagen Softwareprojekt + + + + +
+

Vorlesungsunterlagen Softwareprojekt

+

Martin Sulzmann

+
+

Inhalt

+

Übersicht

+ +

Unterlagen auf Deutsch und Englisch als html mit komplettem +Programmcode.

+

Kurz und knapp

+ +

Einführung in C

+

Teil 1

+ +

Teil 2

+ +

Teil 3

+ +

Einführung in C++

+

Teil 1

+

C++ kompakt

+ +

Teil 2

+

Nochmal Zeiger und Resourcen Kontrolle.

+ +

Teil 3

+

Polymorphie

+

Teil 4

+

Funktionaler Programmierstil

+

Teil 5

+

QuickCheck: Automatisiertes Testen

+

Laboraufgaben und Beispiele

+

Aufgabensammlung

+

Weitere Beispiele

+

Scheinaufgaben

+

Übung (Schein)

+

Weitere Themen

+ +

Software Entwicklung

+ +

Referenzen/Literatur

+ +

C++ online tutorials

+ +

C++ reference

+ + + diff --git a/lec-cpp-compact.html b/lec-cpp-compact.html new file mode 100644 index 0000000..c1316c1 --- /dev/null +++ b/lec-cpp-compact.html @@ -0,0 +1,611 @@ + + + + + + + + + Die Programmiersprache C++ - Kurz und knapp + + + + + +
+

Die Programmiersprache C++ - Kurz und knapp

+

+Martin Sulzmann +

+
+
+

Übersicht

+ +
+
+

Sichtbarkeitsbereiche und Referenzübergabe

+
#include <stdio.h>
+
+// Sichtbarkeitsbereiche ("scope")
+// In C gibt es nur 2 Sichtbarkeitsbereiche.
+// (Im neusten C Standard gilt die gleiche Regel wie in C++)
+// In C++ sind beliebige Schachtelungen moeglich.
+// Jeder Codeblock { ... } entspricht einem Sichtbarkeitsbereich.
+
+void scopeInC() {
+  float i = 1.0;
+  int x[5] = {1,2,3,4,5};
+  int i_2;
+  int j;
+  int x_3;
+
+  for(i_2 = 0; i_2 < 5; i_2++) {
+    j = x[i_2];
+    x_3 = j;
+    printf("%d",x_3);
+  }
+
+}
+
+// B = Begin. E = End.
+// Markiert Codeblock.
+void scopeInCPP() { // B1
+  float i = 1.0;
+  int x[5] = {1,2,3,4,5};
+
+  for(int i=0; i < 5; i++) { // B2
+    int j = x[i];
+    { // B3
+      int x = j;
+      printf("%d", x);
+    } // E3
+  } // E2
+
+} // E1
+
+// Referenzübergabe.
+// Syntaktischer Zucker in C++.
+
+
+struct Pair {
+  int left;
+  int right;
+};
+
+// Variante 1.
+void swapPair_Ref(struct Pair& p) {
+  int tmp = p.left;
+  p.left = p.right;
+  p.right = tmp;
+}
+
+// Variante 2.
+void swapPair_Pointer(struct Pair* p) {
+  int tmp = p->left;
+  p->left = p->right;
+  p->right = tmp;
+}
+
+
+void testSwap() {
+  struct Pair p = {1,2};
+  struct Pair q = {1,2};
+
+
+  printf("\nPair: p = (%d,%d)", p.left, p.right);
+  swapPair_Ref(p);
+  printf("\nPair swapped: p = (%d,%d)", p.left, p.right);
+
+  printf("\nPair: q = (%d,%d)", q.left, q.right);
+  swapPair_Pointer(&q);
+  printf("\nPair swapped: q = (%d,%d)", q.left, q.right);
+
+}
+
+
+int main() {
+
+
+  scopeInC();
+  scopeInCPP();
+  testSwap();
+}
+
+
+

IO streams und Strings

+ +
#include <stdio.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+
+void IO_in_CPP() {
+
+  float f = 1.0;
+  // Entspricht printf("%f",f)
+  cout << f;
+
+  int i = 5;
+  // Entspricht printf("%d",i)
+  cout << i;
+
+  // "<<" ist ein binaerer Operator. Links ist das Ausgabeziel und rechts der Ausgabewert.
+  // "<<" ist ueberladen, auf der rechten Seiten koennen int, float, .... Werte stehen.
+
+
+  // Sequenz von Ausgaben.
+  cout << f << "  " << i;
+
+
+  // "<<" ist links assoziativ.
+  ((cout << f) << "  ") << i;
+  // Beobachtung:
+   // cout << f, rechter Operand ist vom Typ float.
+  // (...) << "  ",  rechter Operand ist vom Typ string.
+  // (...) << i, rechter Operand ist vom Typ int.
+
+  cout << i << "Hallo" << f;
+
+  ((cout << i) << f) << i << f << "Hallo";
+}
+
+
+void strings_cpp() {
+  string s = "\nHallo";
+
+  cout << s;
+
+  string s2;
+
+  s2 = s + s; // String Konkatenation
+
+  cout << s2;
+
+  string s3 = "Hallo";
+
+  cout << "\n" << s.length();
+  cout << "\n" << s2.length();
+  cout << "\n" << s3.length();
+
+
+}
+
+int main() {
+
+  IO_in_CPP();
+  strings_cpp();
+}
+
+
+

Überladene Operatoren/Funktionen

+ +
#include <stdio.h>
+#include <iostream>
+#include <string>
+using namespace std;
+
+
+// Eigene Instanz fuer "<<" Operator.
+// Man spricht hier von "Überladung".
+struct Pair {
+  int left;
+  int right;
+};
+
+
+ostream& operator<< (ostream &out, struct Pair &p) {
+  out << "(" << p.left << "," << p.right << ")";
+
+  return out;
+}
+
+
+// Transformation des Compilers.
+// Generiere eindeutigen Namen, normalerweise Hash-Index
+// basierend auf Argumenttypen.
+// Wir verwenden einfach "_Pair".
+
+ostream& outStream_Pair (ostream &out, struct Pair &p) {
+  out << "(" << p.left << "," << p.right << ")";
+
+  return out;
+}
+
+
+void IO_in_CPP() {
+
+  struct Pair p = {1,2};
+  cout << p;   // Verwende obige "out stream" Instanz.
+  // Compiler Transformation.
+  // Verwende eindeutigen Funktionsaufruf.
+  outStream_Pair(cout,p);
+
+}
+
+
+
+// Beachte:
+//  - swapPair ist eine überladene Funktion.
+//  - Mehrfache Definition, aber für verschiedene Argumenttypen.
+
+// Variante 1.
+void swapPair(struct Pair& p) {
+  int tmp = p.left;
+  p.left = p.right;
+  p.right = tmp;
+}
+
+// Intern passiert folgendes.
+
+// Annahme, der Funktionsname swapPair_Ref ist eindeutig.
+void swapPair_Ref(struct Pair& p) {
+  int tmp = p.left;
+  p.left = p.right;
+  p.right = tmp;
+}
+
+// Weitere Transformation.
+// Uebersetze call-by-reference in call-by-value + pointer
+
+void swapPair_Ref_Transformed(struct Pair* p) {
+  int tmp = p->left;
+  p->left = p->right;
+  p->right = tmp;
+}
+
+// Variante 2.
+void swapPair(struct Pair* p) {
+  int tmp = p->left;
+  p->left = p->right;
+  p->right = tmp;
+}
+
+void swapPair_Pointer(struct Pair* p) {
+  int tmp = p->left;
+  p->left = p->right;
+  p->right = tmp;
+}
+
+// Variante 3 (call-by-value)
+/*
+
+"'swapPair' is ambiguous"
+
+
+void swapPair(struct Pair p) {
+  int tmp = p.left;
+  p.left = p.right;
+  p.right = tmp;
+}
+
+*/
+
+
+void testSwap() {
+  struct Pair p = {1,2};
+  struct Pair q = {1,2};
+
+  cout << "\nPair: " << p;
+  swapPair(p);
+  // Compiler verwendet hier Variante 1 mit explizitem eindeutigem Namen.
+  // 1ter Transformationsschritt.
+  swapPair_Ref(p);
+  // 2ter Transformationsschritt.
+  swapPair_Ref_Transformed(&p);
+  cout << "\nPair swapped: " << p;
+
+  cout << "\nPair: " << q;
+  swapPair(&q);
+  swapPair_Pointer(&q);
+  cout << "\nPair swapped: " << q;
+
+}
+
+
+int main() {
+
+    IO_in_CPP();
+    testSwap();
+
+}
+
+
+

Klassen

+ +
#include <iostream>
+#include <string>
+using namespace std;
+
+///////////////////////
+// Klassen
+
+class Point2D {
+  float x, y; // private
+public:
+  Point2D(float x2=0, float y2=0) {
+  x = x2;  y = y2;
+  }
+  void scale(float f) {
+  x = f * x; y = f * y;
+ }
+  float getX() { return x; }
+  float getY() { return y; }
+  void setXY(float x2, float y2) {
+    x = x2; y = y2;
+  }
+
+  string show() {
+
+    // + Konkatenation zweier Strings.
+    // Konvertierung von float nach String via to_string.
+    // C++ unterstuetzt Ueberladung (overloading).
+    // D.h wir koennen z.B. to_string auf Werte vom Typ float,
+    // als auch auf Werte vom Typ int anwenden.
+    // Konkret.
+    // string str = to_string(1) + to_string(1.0);
+    return ("\nx = " + to_string(x) + "\ny = " + to_string(y));
+
+  }
+
+}; // Point2D
+
+
+void testPoint2D() {
+  Point2D p1; // Wir haben den Default Auruf
+              // p1 = Point2D();
+              // Aber wir haben Default Werte definiert.
+              // Deshalb wir obige Anweisung interpretiert als
+              // p1 = Point2D(0,0);
+  Point2D p2 = Point2D(1,2);
+  Point2D p3(3,4); // Verschieden Schreibweise fuer einen Konstruktoraufruf.
+  Point2D p4 = Point2D(1); // Point2D(1,0)
+
+  // Beachte. In Java werden Objekte immer mit Hilfe von "new" angelegt!
+  // In Java liegen alle Objekte auf dem Heap.
+  // In C++ koennen Objekte auf dem Stack ("kein new") oder dem Heap ("new") liegen.
+  // Im obigen Beispiel, liegen die Objekte p1, p2 und p3 auf dem Stack.
+
+  cout << p1.show();
+  cout << p2.show();
+  p3.scale(2);
+  cout << p3.show();
+}
+
+
+
+// Erweiterung der Klassenhierarchie ("extends").
+class Point3D : public Point2D {
+  float z;
+public:
+  Point3D(float x2=0, float y2=0, float z2=0) : Point2D(x2,y2) {
+    z = z2;
+  }
+
+  void scale(float f) {
+    this->setXY(f * this->getX(), f * this->getY());
+    z = f * z;
+ }
+
+  string show() {
+    return ("\nx = " + to_string(getX())
+      + "\ny = " + to_string(getY())
+      + "\nz = " + to_string(z));
+
+  }
+
+};
+
+void testPoint3D() {
+  Point3D p = Point3D(1,2,3);
+
+  cout << p.show();
+
+  p.scale(2);
+
+  cout << p.show();
+
+  // Point3D <= Point2D,
+  // bedeutet jedes Objekt der Klasse Point3D kann auch an einer Stelle verwendet werden,
+  // an welcher ein Objekt der Klasse Point2D erwartet wird.
+  Point2D q = p;
+
+  cout << q.show();
+
+}
+
+
+int main() {
+  testPoint2D();
+  testPoint3D();
+}
+
+
+

Virtuelle (dynamisch) versus nicht-virtuelle (statisch) +Methoden(auswahl) und static

+

Virtuelle Methoden:

+ +

Nicht-virtuelle Methoden:

+ +

static:

+ +
#include <iostream>
+#include <string>
+using namespace std;
+
+class Vehicle {
+public:
+  static int number;
+  static int getNo() { return number; }
+  virtual int maxSpeed() { return 0; }
+  string info() { return "I'm a vehicle"; }
+};
+
+int Vehicle::number = 0;
+
+// car <= Vehicle
+class car : public Vehicle {
+public:
+  car() { Vehicle::number++; }
+  int maxSpeed() { return 100; }
+  string info() { return "I'm a car"; }
+};
+
+
+// Bike <= Vehicle
+class Bike : public Vehicle {
+public:
+  Bike() { number++; }
+  int maxSpeed() { return 30; }
+  string info() { return "I'm a bicycle"; }
+};
+
+
+void test1(Vehicle v) {
+  cout << "\nInfo: " << v.info();
+  cout << "\nSpeed: " << v.maxSpeed();
+}
+
+void test2(Vehicle* v) {
+  cout << "\nInfo: " << v->info();
+  cout << "\nSpeed: " << v->maxSpeed();
+}
+
+void test3(car v) {
+  cout << "\nInfo: " << v.info();
+  cout << "\nSpeed: " << v.maxSpeed();
+}
+
+
+int main() {
+  Vehicle::number = 0;
+  Bike b = Bike();
+  car c = car();
+
+     test1(c); // car <= Vehicle
+               // das car Objekt c wird konvertiert
+               // in ein Vehicle Objekt
+
+     test2(&c); // *car <= *Vehicle
+                // Die Referenz auf ein car Objekt
+                // wird uebergeben.
+
+     test3(c);
+
+     test1(b);
+     test2(&b);
+
+  cout << "\n There are "
+       // << Vehicle::number
+       << Vehicle::getNo()
+       << " vehicles";
+}
+
+ +