Tutorium #4

Softwareentwicklung Praktikum

Gruppe 3 – Jörg Müller prog-tutor-joerg@iicm.tugraz.at
Gruppe 4 – Michael Schwarz prog-tutor-michael@iicm.tugraz.at
Gruppe 5 – Anja Karl prog-tutor-anja@iicm.tugraz.at
Gruppe 8 – Thomas Neff prog-tutor-thomas@iicm.tugraz.at

Ablauf des Tutoriums

Häufigste Fehler in Ass2

Häufige Fehler, die viele Punkte kosten


File-IO - File öffnen

Binärdatei zum Schreiben öffnen

  #include <fstream>
  std::ofstream file("tut4.bin",
    std::ios::out | std::ios::trunc | std::ios::binary);
  if (!file.is_open()) 
  {
    std::cerr << "cannot open file" << std::endl;
  } else file.close(); 

Binärdatei zum Lesen öffnen

  std::ifstream input("tut4.bin", std::ios::in | std::ios::binary);
  if (!input.is_open()) 
  {
    std::cerr << "cannot open file" << std::endl;
  } else input.close();

File-IO - Schreiben

// schreibe "SEPRISK\0" in Datei
file << "SEPRISK";
file << '\0';
// schreibe Zahl 7 als Binärwert -> in char speichern 
// und dann in Datei schreiben
char num = 7;
file << num;
// schreibe Zahl 7 als Ziffer ('7') -> wenn Zahl 
// in int gespeichert ist
int num2 = 7;
file << num2;
// schreibe C-String mit Nullbyte
char* str = "Hallo Welt";
file << str << '\0';

Dateien können behandelt werden wie std::cout und std::cin

File-IO - Lesen

  // lese C-String Zeichenweise bis Nullbyte zurück
  char c;
  while((input >> c) && c != 0) {
   std::cout << c; 
  }
  std::cout << std::endl;
  // lese Binärwert 7 zurück (und zeige als char und integer an)
  char y;
  input >> y;
  std::cout << y << " = " << static_cast<int>(y) << std::endl;
  // lese Zahl 7 als Ziffer ('7') zurück
  int x;
  input >> x;
  std::cout << x << std::endl;
  

Operator Overloading

Operator Overloading - Beispiel (als Member)

class Complex
{
private:
  float real_part_;
  float imaginary_part_;
  
public:
    bool operator==(const Complex &c) const
    {
        return ( c.real_part_ == real_part_ &&
                (c.imaginary_part_ == imaginary_part_) );
    }
    
    Complex operator+(const Complex& right)
    {
      Complex result;
      result.setImaginary(imaginary_part_ + right.getImaginary());
      result.setReal(real_part_ + right.getReal());
      return result;
    }
    
    [...]
};

Casts in C++

static_cast
Funktioniert wie das von C bekannte Casting (typ)variable. Funktioniert nur, wenn der Compiler eine Regel zum Casten kennt.
int a = 16, b = 9;
float ratio = static_cast<float>(a) / b;
const_cast
Nur zum Hinzufügen oder Entfernen (gefährlich) des const Keywords.
dynamic_cast
Casts innerhalb einer Klassenhierarchie von polymorphen Klassen. Aufwändige Type-Checks zur Laufzeit.
// Cast von *CD in *MusicCD
MusicCD *charts = dynamic_cast<*MusicCD>(cd1);
reinterpret_cast
Keine Konvertierung, Daten werden ohne Änderung als neuer Datentyp interpretiert, plattformabhängig, sollte nur bei hardwarenaher Programmierung verwendet werden.

static_cast verlustfreie Regeln


Castings sind nur dann ohne Datenverlust, wenn auf einen größeren Datentyp gecastet wird.

Exceptions


Grundlegendes Konstrukt für Exceptions
try
{
  someFunction() // könnte eine Exception werfen...
  [...]
  throw(e); // ...oder manuelles Werfen einer Exception
}
catch(MyException& e1)
{
  // fange Exception vom Typ MyException
}
catch(std::exception& e)
{
  // fange alles, was vom Typ std::exception abgeleitet ist, 
  // und noch nicht gefangen wurde
}

Exceptions


Nützlich, aber nicht übertreiben → Formale Fehlerquellen
Also immer
 class MyException : public std::exception {
   [...]
 };

Exceptions - Beispiel

#include <exception>
class brick : public std::exception {
  virtual const char* what() const throw()
  {
    return "Achtung Ziegel!\n";
  }
};

int main ()
{
  try
  {
    throw brick();
  } catch (std::exception& ex)
  {
    std::cout << ex.what();
  }
  return 0;
} 

Neu zu beachten: Out of Memory!

int main () 
{
  try
  {
    int* myarray = new int[10000];
  }
  catch (std::bad_alloc& ba)
  {
    std::cout << "Out of Mana!" << std::endl;
  }
  return 0;
}

Neu zu beachten: Out of Memory!

In jedem Fall muss aller Speicher wieder freigegeben werden!

...
try
{
  NextCommand* next = new NextCommand("next");  
}
catch (std::bad_alloc& ba)
{
  std::cout << "Out of Mana!" << std::endl;
  return 0;
}
try
{
  ShowCommand* show = new ŚhowCommand("show");  
}
catch (std::bad_alloc& ba)
{
  delete next;
  std::cout << "Out of Mana!" << std::endl;
  return 0;
}
...

Aber geht das nicht besser???

Besser:

-> Es reicht bad alloc in der Main zu fangen.

Außer: Exceptions im Konstruktor!

Achtung: Exception im Konstruktor

#include <memory>
#include <iostream>
#include <vector>
class Foo {
private:
    std::vector<int*> list;
public:
    Foo() {
        list.push_back(new int);
        list.push_back(new int);
        throw 1;
    }
    
    ~Foo() {
      for(auto it : list) {
          delete it;
      }
    }
};
int main() {
    Foo f;
    return 0;
}

Resource Acquisition Is Initialization (RAII)

Kein RAII - Beispiel

int median(int* numbers, int count) {
  int* data = new int[count];
  std::memcpy(data, numbers, count * sizeof(int));
  // error handling
  if(numbers == NULL) {
    delete[] data; // <---
    return 0;
  }
  if(count == 0) {
    delete[] data; // <---
    return 0;
  }
  if(count == 1) {
    delete[] data; // <---
    return *numbers;
  }
  // sort
  if(mySortFunction(data, count) == false) {
    delete[] data; // <---
    return 0;
  }
  // return median
  int result = data[count / 2];
  delete[] data; // <---
  return result;  
}

RAII - Beispiel

int median(int* numbers, int count)
{
  Data data(count);
  data.setData(numbers, count);
  // error handling
  if(numbers == NULL)
    return 0;
  if(count == 0) 
    return 0;
  if(count == 1)
    return *numbers;
    
  // sort
  if(mySortFunction(data.getData(),
       count) == false) 
    return 0;
  
  return data.getData()[count / 2];  
}
class Data 
{
private:
  int* data_;
public:
  Data(int count) { 
    data_ = new int[count]; 
  }
  int* getData() const {
    return data_; 
  }
  void setData(int* data, 
               int count) 
  {
    std::memcpy(data_, data, 
      count * sizeof(int));             
  }
  ~Data() { 
    delete[] data_; 
  }
};

Smart Pointer



"Stirbt" der Smart-Pointer, wird der reservierte Speicher (durch delete) freigegeben.

Smart Pointer - Beispiel

#include <iostream>
#include <memory>

class Foo {
public:
  void bar() {
    std::cout  << "foobar" << std::endl;
  }
};

std::unique_ptr<Foo> createFoo() {
  Foo* bar = new Foo();
  return std::unique_ptr<Foo>(bar);
}

int main() {
    std::unique_ptr<Foo> ptr = createFoo();
    
    std::vector<std::unique_ptr<Foo>> foos;
    
    foos.push_back(std::unique_ptr<Foo>(new Foo()));
    foos.push_back(std::unique_ptr<Foo>(new Foo()));
    foos.push_back(std::unique_ptr<Foo>(new Foo()));
    
    ptr->bar();
}

Templates

Templates - Beispiel (Funktionstemplate)


template <typename Type>
Type max(Type a, Type b) 
{
    if(a > b) return a;
    else return b;
}
int main()
{
  // ruft max<int> auf (wird an Argumenten erkannt)
  std::cout << max(3, 7) << std::endl;
  // ruft max<double> (wird an Argumenten erkannt)
  std::cout << max(3.0, 7.0) << std::endl;
  // Argumente nicht eindeutig, Typ muss angegeben werden (<double>)
  std::cout << max<double>(3, 7.0) << std::endl;
  return 0;
}

Templates - Beispiel (Klassentemplate)

template <typename T>
class Math {
public:
  T getMin(T x1, T x2) {
    if(x1 < x2) return x1;
    else return x2;
  }
  
  T getMax(T x1, T x2) {
    if(x1 > x2) return x1;
    else return x2;
  }
};
int main() {
  Math<int> int_math;
  Math<std::string> str_math;
  
  std::cout << "max(2, 4): " << int_math.getMax(2, 4) << std::endl;
  std::cout << "min(\"abc\", \"bcd\"): " 
    << str_math.getMin("abc", "bcd") << std::endl;
}

Assignment 3




http://towersep.appspot.com/

Übernommene Befehle


show
Ausgabe von Spielfeld, Kapital und HP
next
Runde beenden bzw. n Runden vorspulen
quit
Beendet das Programm

Neue Befehle


stats
Zeigt Spielstatistiken an
shop
Turm-Shop aufrufen
buy
Turm kaufen und errichten
show target
Zeigt an, was sich auf einem Feld befindet
upgrade (Bonus)
Bonus: Turm upgraden auf besseren Turm

Ratio


Rationale Zahl, bestehend aus zwei teilerfremden Ganzzahlen.
int main() {
  Ratio ratio_1(7,3);
  Ratio ratio_2(3,2);
  std::cout << "Ratios: " << std::endl;
  std::cout << ratio_1 << std::endl;
  std::cout << ratio_2 << std::endl;

  Ratio sum = ratio_1 + ratio_2;

  std::cout << "Sum:" << std::endl;
  std::cout << sum << std::endl;

  sum = sum - 2;
  std::cout << "Sum - 2:" << std::endl;
  std::cout << sum << std::endl;
  if(sum > 1)
    std::cout << "Ratio: " << sum << " is > 1" << std::endl;
  else
    std::cout << "Ratio: " << sum << " is < 1" << std::endl;
}

Ratio


Ausgabe
Ratios:
7 / 3
3 / 2
Sum:
23 / 6
Sum - 2:
11 / 6
Ratio: 11 / 6 is > 1

SVG

SVG - Aufbau


Header
<svg xmlns="http://www.w3.org/2000/svg" 
  xmlns:xlink="http://www.w3.org/1999/xlink">  
Objekte
<image x="0" y="0" width="1016px" height="638px" 
  xlink:href="base.svg"> </image>
[...]
Footer
</svg>

SVG - Einfache Objekte

Viel Glück