Tutorium #3

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

Pointer & Referenzen

#include <iostream>

void funcValue(int x, int val) {
  x = val;
}

int main() {
  int v = 3;
  funcValue(v, 4);
  std::cout << v << std::endl;
}
#include <iostream>

void funcReference(int& x, int val) {
  x = val;
}

int main() {
  int v = 3;
  funcReference(v, 4);
  std::cout << v << std::endl;
}
#include <iostream>

void funcPointer(int* x, int val) {
  *x = val;
}

int main() {
  int v = 3;
  funcPointer(&v, 4);
  std::cout << v << std::endl;
}
  • Referenzen können nicht NULL sein, Pointer schon
  • Referenzen können wie normale Variablen verwendet werden
  • Bei Referenzen und Pointern wird nur die Adresse übergeben, sonst wird kopiert

const Schlüsselwort


const bezieht sich immer auf den linken Nachbarn. Gibt es keinen, dann auf den rechten Nachbarn.

const Schlüsselwort

const Schlüsselwort



...und was macht das?


int const * const doSomething(const int * const & parameter) const;

static Schlüsselwort


Statische Attribute und Methoden werden pro Klasse nur einmal angelegt, statt wie sonst für jede Instanz.
Zugriff über Klassenname::funktion() bzw. Klassenname::variable
class Product 
{
  private:
    static int count_;
  public:
    Product() { count_++; }
    ~Product() { count_--; }
    static int getProductsCount() { return count_; }
};

int Product::count_ = 0;

int main() 
{
  std::cout << Product::getProductsCount() << std::endl;
  Product p1;
  std::cout << Product::getProductsCount() << std::endl;
} 

Vererbung

class CD 
{
private:
  int capacity_;
  string title_;
  
public:
  CD(string title, int size) 
   : capacity_(size), title_(title) {}
  
  int getCapacity() { 
    return capacity_; 
  }
  
  void printTitle() { 
    cout << title_ << endl; 
  }
};
class MusicCD : public CD 
{
private:
  int tracks_;

public:
  MusicCD(string name, int tracks) 
   : CD(name, 700), tracks_(tracks)
  {}
  
  int getCapacity() { 
    return tracks_; 
  }
  
  void play() {
    cout << "lalalala" << endl;
  }
};
int main() {
  CD cd1("Backup", 700);
  MusicCD cd2("Charts", 21);
  cd1.printTitle();
  cout << cd1.getCapacity() << endl;
  cd2.printTitle();
  cout << cd2.getCapacity() << endl;
  cd2.play();
}
> ./cd_class
Backup
700
Charts
21
lalalala

Virtuelle Methoden - Problem

int main() 
{
  CD cd1("Backup", 700);
  MusicCD cd2("Charts", 21);
  
  vector<CD*> vec;
  vec.push_back(&cd1);
  vec.push_back(&cd2);
  
  for(auto cd : vec)
  {
    cd->printTitle();
    cout << cd->getCapacity() << endl;
  }
}
Soll
Backup
700
Charts
21
    Ist
Backup
700
Charts
700

Virtuelle Methode - Theorie



Destruktoren sollten immer als virtual deklariert sein, damit beim Zerstören des Objekts immer auf den richtigen Destruktor zugegriffen wird.

Virtual - Lösung



class CD 
{
private:
  int capacity_;
  string title_;
  
public:
  CD(string title, int size) 
   : capacity_(size), title_(title) {}
  
  virtual int getCapacity() { 
    return capacity_; 
  }
  
  void printTitle() { 
    cout << title_ << endl; 
  }
};
class MusicCD : public CD 
{
private:
  int tracks_;

public:
  MusicCD(string name, int tracks) 
   : CD(name, 700), tracks_(tracks)
  {}
  
  int getCapacity() { 
    return tracks_; 
  }
  
  void play() {
    cout << "lalalala" << endl;
  }
};

Abstrakte Klassen & Rein Virtuelle Methoden

Speichermanagement: new und delete

Speicheranforderung

Speicherfreigabe

  • Einfache Typen
    int* i = new int;
    string* str = new string;
    
  • Klassen (def. Konstruktor)
    Person* p = new Person;
    
  • Klassen (mit Konstruktor)
    Person* p = new Person("Max");
    
  • Arrays
    int* a = new int[10];    
    
  • Einfache Typen
    delete i;
    delete str;
    
  • Klassen (def. Konstruktor)
    delete p;          
    
  • Klassen (mit Konstruktor)
    delete p;    
    
  • Arrays
    delete[] a;
    

Einfache Klasse III (ball.h)

#ifndef BALL_H_INCLUDED
#define BALL_H_INCLUDED

#include <string>

class Ball
{
private:
  float x_, y_, speed_;
  int direction_;
  std::string color_;
  std::string name_;

public:
  Ball(float x, float y, float speed, int direction,
       std::string name);
  
  virtual void move() = 0;
  
};

#endif

NormalBall und CrazyBall

#ifndef NORMALBALL_H_INCLUDED
#define NORMALBALL_H_INCLUDED
#include "ball.h"

class NormalBall : public Ball {
  public:
    NormalBall(float x, float y, float speed, int dir,
               std::string name);
    void move();
};
#endif
#ifndef CRAZYBALL_H_INCLUDED
#define CRAZYBALL_H_INCLUDED
#include "ball.h"

class CrazyBall : public Ball {
  public:
    CrazyBall(float x, float y, float speed, int dir, 
              std::string name);
    void move();
};
#endif

Konstruktor (ball.cpp)

#include <math.h>
#include "ball.h"

Ball::Ball(float x, float y, float speed, int dir, 
      std::string name) : x_(x), y_(y), speed_(speed)
{
  direction_ = dir * M_PI / 180.0;
  name_ = "Ball '" + name + "'";
  color_ = "red";
}

Konstruktor (crazyball.cpp / normalball.cpp)

#include <math.h>
#include "ball.h"

Crazyball::Crazyball(float x, float y, float speed, int dir, 
      std::string name) : Ball(x, y, speed, dir, name)
{
  name_ = "CrazyBall '" + name + "'";
  color_ = "blue";
}
#include <math.h>
#include "ball.h"

Normalball::Normalball(float x, float y, float speed, int dir, 
      std::string name) : Ball(x, y, speed, dir, name)
{
}

move (crazyball.cpp and normalball.cpp)

void NormalBall::move()
{
  x_ = x_ + cos(direction_) * speed_;
  y_ = y_ + sin(direction_) * speed_;  
  if(x_ < 0 || x_ > 800) 
    direction_ = atan2(sin(direction_), -cos(direction_));
  if(y_ < 0 || y_ > 600)
    direction_ = atan2(-sin(direction_), cos(direction_));
}

void CrazyBall::move()
{
  x_ = x_ + cos(direction_) * speed_ + sin(direction_ / 2) * speed_;
  y_ = y_ + (tan((x_ % 180) * M_PI / 180.0) % 4) * speed_;  
  if(x_ < 0 || x_ > 800) 
    direction_ = atan2(sin(direction_), -cos(direction_));
  if(y_ < 0 || y_ > 600)
    direction_ = atan2(-sin(direction_), cos(direction_));
}

Das main-Programm

#include <vector>
#include "ball.h"

int main()
{
  std::vector<Ball*> balls;

  NormalBall b1(200, 225, 5, 10, "#1");
  balls.push_back(&b1);
while(1) { int i; for(i = 0; i < balls.size(); i++) { balls[i]->move(); } drawWindow(); // some magic... } }

C++ 11 Gimmicks  

#include <iostream>
#include <vector>

int main()
{
  std::vector<int> numbers = {1, 2, 3, 4};
  for(const auto& number : numbers)
  {
    std::cout << number << std::endl;
  }
}

C++ 11 nullptr  

#include <iostream>

void foo(int number)
{
  std::cout << "Number: " << number << std::endl;
}

void foo(void* pointer)
{
  std::cout << "Pointer: " << pointer << std::endl;
}

int main()
{
  foo(nullptr); // NULL doesn't work!
}

C++ 11 delete und default  

class Foo
{
public:
  Foo(Foo& foo) = delete;
  Foo& operator=(Foo& foo) = delete;
  Foo() = default; // keep default constructor
  Foo(double number) {}
  Foo(int number) = delete;
};

int main()
{
  Foo f1; // works, default constructor
  Foo f2 = f1; // doesn't work
  Foo f3(0); // doesn't work either
  Foo f4(3.1415); // works again
}

Assignment 2

Angeforderter Speicher ist in jedem Fall wieder freizugeben

Command Pattern

Light
void turnOn();
void turnOff();
Invoker
int invoke(Command& cmd) {
  return cmd.execute(light);
}
Command
int execute(Light& light) = 0;










#>

Command Pattern

class Command
{
  private:
    Command(const Command& original);
    Command& operator=(const Command& original);
    
    std::string command_name_;

  public:
    Command(std::string name);
    virtual ~Command();
        
    virtual int execute(Game& board, std::vector<std::string>& params) = 0;

    const std::string& getName();
};
         

Command Pattern - Beispiel

class PrintHello : public Command
{
  public:
    PrintHello() : Command("PrintHello!"){}
    virtual ~PrintHello();
        
    int execute(Game& board, std::vector<std::string>& params);
};

int PrintHello::execute(Game& board, std::vector<std::string>& params)
{
  std::cout << "Hello" << std::endl;
}
        

Befehle


echo
Gibt beliebig viele übergebene Parameter aus
show
Ausgabe von Spielfeld, Kapital und HP
list
Informationen über alle Türme und Monster ausgeben
next
Runde beenden bzw. n Runden vorspulen
quit
Beendet das Programm

Viel Glück

Nächstes Tutorium: 19./20./21. Mai 2014