// Lösungen zu "Richard Kaiser: C++ mit dem Borland C++Builder 2007", ISBN: 978-3-540-69575-2
// Copyright Richard Kaiser, 2007. http://www.rkaiser.de. All rights reserved.
// Diese Quelltexte dürfen in eigenen Programmen verwendet werden, wenn sie einen Hinweis auf die Quelle (die ersten beiden Zeilen) enthalten. In keinem Fall wird eine Haftung für direkte, indirekte, zufällige oder Folgeschäden übernommen, die sich aus der Nutzung ergeben.
// It is permitted to use this source text in programs of your own, provided that it contains a reference to the source (the first two lines above). All liabilities for use of the code are disclaimed.

#include <vcl.h>
#pragma hdrstop
#include "VirtFktU.h"
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}


// --------------- Aufgaben 6.4.3 --------------------------

// --------------- Aufgabe 1 -------------------------------

#include <cmath> // für fabs und sqrt

class C1DPunkt{
  double x;
 public:
  C1DPunkt(double x_):x(x_){  }
  void setzeX(double x_) {x=x_;}
  double X() const { return x; }

  virtual AnsiString toStr() const
  {
  return "("+FloatToStr(x) + ")";
  }

// c)
  void anzeigen() const
  {
  Form1->Memo1->Lines->Add(toStr());
  }

// e)
  double length() const
  {
  return std::fabs(x);
  }
};

class C2DPunkt:public C1DPunkt{
  double y;
 public:
  C2DPunkt(double x_, double y_):C1DPunkt(x_), y(y_) {  }
  void setzeY(double y_) { y=y_; }
  double Y() const { return y; }

  AnsiString toStr() const
  {
  return "("+FloatToStr(X()) + "|" + FloatToStr(Y())+")";
  }

// e)
  double length()
  {
  return std::sqrt(X()*X() + Y()*Y());
  }
};

class C3DPunkt:public C2DPunkt{
  double z;
 public:
  C3DPunkt(double x_, double y_, double z_):C2DPunkt(x_,y_), z(z_) {  }
  void setzeZ(double z_) { z=z_; }
  double Z() const { return z; }

  AnsiString toStr() const
  {
  return "("+FloatToStr(X()) + "|" + FloatToStr(Y())+"|" + FloatToStr(z)+")";
  }

// e)
  double C3DPunkt::length()
  {
  return std::sqrt(X()*X() + Y()*Y() + z*z);
  }
};

// b)
void show(C1DPunkt& p)
{
Form1->Memo1->Lines->Add(p.toStr());
};

// d)
// Unter Projekt|Optionen|Pfade ...
//    einen Pfad auf das Verzeichnis der Boost Bibliothek setzen
#include <boost/shared_ptr.hpp>


void testVirtuelleFunktionen()
{
// a)
Form1->Memo1->Lines->Add("toStr()");
C1DPunkt* p1=new C1DPunkt(1);
Form1->Memo1->Lines->Add(p1->toStr());
p1=new C2DPunkt(2,3);
Form1->Memo1->Lines->Add(p1->toStr());
p1=new C3DPunkt(4,5,6);
Form1->Memo1->Lines->Add(p1->toStr());

// b)
Form1->Memo1->Lines->Add("show");
C1DPunkt q1(7);
C2DPunkt q2(8,9);
C3DPunkt q3(10,11,12);
show(q1); // C1DPunkt::toStr
show(q2); // C2DPunkt::toStr
show(q3); // C3DPunkt::toStr

// c)
Form1->Memo1->Lines->Add("anzeigen");
q1.anzeigen(); // C1DPunkt::toStr
q2.anzeigen(); // C2DPunkt::toStr
q3.anzeigen(); // C3DPunkt::toStr

// d)
Form1->Memo1->Lines->Add("shared_ptr");
using boost::shared_ptr;
shared_ptr<C1DPunkt> s1(new C1DPunkt(1));
Form1->Memo1->Lines->Add(s1->toStr());
s1.reset(new C2DPunkt(2,3));
Form1->Memo1->Lines->Add(s1->toStr());
s1.reset(new C3DPunkt(4,5,6));
Form1->Memo1->Lines->Add(s1->toStr());
}

void __fastcall TForm1::Button_4_3_1Click(TObject *Sender)
{
testVirtuelleFunktionen();
}

// --------------- Aufgabe 2 -------------------------------

void __fastcall TForm1::Button_4_3_2Click(TObject *Sender)
{
AnsiString s=
"Diese Funktionen können nicht virtuell definiert werden, da alle ihre "
"Parameterlisten verschieden sind.";
Memo1->Lines->Add(s);
}

// --------------- Aufgabe 3 -------------------------------

void __fastcall TForm1::Button_4_3_3Click(TObject *Sender)
{
/*
Wenn die Funktion D::f virtuell wäre, würde mit

  C c;
  D d;

der Aufruf

  g(d,c);

zum Aufruf der Funktion

  D::f(c)

führen. Diese Funktion würde auf c.e zugreifen, das in C aber gar nicht existiert.
*/
}

// --------------- Aufgabe 4 -------------------------------

struct C {
  virtual int f(int i=1)  { return i; }
};

struct D:public C {
  virtual int f(int i=2)  { return i; }
};

void testDefaultArgumente()
{
C* pc=new D;
int i=pc->f(); // pc->f(1);
C* pd=new D;
int j=pd->f(); // pd->f(1);
// Das Default-Argument wird immer nach dem statischen Datentyp bestimmt
Form1->Memo1->Lines->Add(i);
Form1->Memo1->Lines->Add(j);
};

void __fastcall TForm1::Button_4_3_4Click(TObject *Sender)
{
testDefaultArgumente();
}


// --------------- Aufgaben 6.4.10 -------------------------

// --------------- Aufgabe 1 -------------------------------

#include <vector>
#include <fstream>
#include <sstream>
#include <string>
using std::endl;

namespace N_Aufg_4_10_1 {

class C2DPunkt{
  double x,y;
 public:
  C2DPunkt(double x_=0, double y_=0):x(x_),y(y_) {  };
//
  virtual ~C2DPunkt() { };

  double X() const {return x;}
  double Y() const {return y;}

  AnsiString toStr() const
  {
  return "("+FloatToStr(x)+"|"+FloatToStr(y)+")";
  }

  // Diese Funktionen sind für die Operationen mit den
  // Figuren notwendig:
  C2DPunkt (ifstream& f)
  { // erzeugt einen C2DPunkt mit den Daten aus dem ifstream
  f>>x>>y;
  }

  friend ostream& operator<<(ostream& f, const C2DPunkt& p);
  friend istream& operator>>(istream& f, C2DPunkt& p);

  void verschiebe_um(const C2DPunkt& pos)
  {
  x=x+pos.x;
  y=y+pos.y;
  };
};

ostream& operator<<(ostream& f, const C2DPunkt& p)
{
return f<<' '<<p.x<<' '<<p.y<<' ';
}

istream& operator>>(istream& f, C2DPunkt& p)
{
f>>p.x>>p.y;
return f;
}

// a) Verwende eine gemeinsame, abstrakte Basisklasse,
//    von der Kreis, Quadrat, Rechteck usw. abgeleitet
//    werden.

class Figur {
 public:
  virtual ~Figur(){}; // den virtuellen Destruktor nicht vergessen
// a)
  virtual double Flaeche()   const =0;
  virtual double Umfang()    const =0;

// b)
  virtual AnsiString toStr() const =0;
// c)
  virtual void zeichne()     const =0;
  virtual void zeichne(TImage* img)     const =0;
// c)
  virtual void write(ofstream& f) const =0;

// d)
  void loesche(TImage* img) const
  {
  TColor StiftFarbe=img->Canvas->Pen->Color;
  img->Canvas->Pen->Color=img->Canvas->Brush->Color; // Hintergrundfarbe
  zeichne(img); // rufe die rein virtuelle Funktion auf
  img->Canvas->Pen->Color=StiftFarbe;
  }

  virtual void verschiebe_Position_um(const C2DPunkt& deltaPos)=0;
};

// c)
class Zeichnung {
  typedef std::vector<Figur*> Container;
  typedef Container::iterator Iterator;
  // Zeiger auf abstrakte Klassen können verwendet werden
  Container c;
 public:
  Zeichnung(){ };

// c)
  void einfuegen(Figur* p)
  {
  c.push_back(p);
  }

// b)
  void zeichne()
  { // schreibt in Memo1
  for (Iterator i=c.begin(); i!=c.end(); i++)
    (*i)->zeichne();
  }

  void zeichne(TImage* img)
  { // zeichne auf img
  for (Iterator i=c.begin(); i!=c.end(); i++)
    (*i)->zeichne(img);
  }

// d)
  void loesche(TImage* img)
  {
  for (Iterator i=c.begin(); i!=c.end(); i++)
    (*i)->loesche(img);
  }

  void verschiebe_um(TImage* img, C2DPunkt deltaPos)
  {
  loesche(img);
  for (Iterator i=c.begin(); i!=c.end(); i++)
    (*i)->verschiebe_Position_um(deltaPos);
  zeichne(img);
  }

// e)
  void write(const char* Dateiname)
  {
  ofstream f(Dateiname);
  for (Iterator i=c.begin(); i!=c.end(); i++)
    (*i)->write(f);
  f.close(); // überflüssig, aber nicht falsch
  }

// f)
  void LeseZeichnung(const char* Dateiname);

  void toMemo(TMemo* m)
  {
  for (Iterator i=c.begin(); i!=c.end(); i++)
    m->Lines->Add((*i)->toStr().c_str());
  }

};


class Gerade:public Figur {
  C2DPunkt ap,ep; // Anfangs- und Endpunkt
 public:
  Gerade(C2DPunkt ap_, C2DPunkt ep_):ap(ap_),ep(ep_) {}

  Gerade(ifstream& f):ap(f),ep(f)
  {  } // Erzeugt eine Gerade mit den Daten aus dem ifstream

// a)
  virtual double Flaeche()   const {return 0;}
  virtual double Umfang()    const {return 0;}

// b)
  AnsiString toStr() const
  {
  return "Gerade AP="+ap.toStr()+" EP="+ep.toStr();
  }

// c)
  void zeichne() const
  {
  Form1->Memo1->Lines->Add("zeichne "+toStr());
  };

  virtual void zeichne(TImage* img) const
  {
  img->Canvas->MoveTo(ap.X(),ap.Y());
  img->Canvas->LineTo(ep.X(),ep.Y());
  };

// d)
  void verschiebe_Position_um(const C2DPunkt& deltaPos)
  {
  ap.verschiebe_um(deltaPos);
  ep.verschiebe_um(deltaPos);
  }

// e)
  void write(ofstream& f) const
  {
  f<<class_id<<' '<<ap<<ep<<endl ;
  }

  static const char* class_id;
};

const char* Gerade::class_id="Gerade";


class Kreis:public Figur {
  C2DPunkt M; // Mittelpunkt
  double r;   // Radius
 public:
  Kreis(C2DPunkt M_, double r_):M(M_),r(r_) {}

// a)
  virtual double Flaeche()   const {return r*r*3.14;}
  virtual double Umfang()    const {return 2*3.14*r;}

// b)
  AnsiString toStr() const
  {
  return "Kreis MP="+M.toStr()+" Radius="+FloatToStr(r);
  }

// c)
  void zeichne() const
  {
  Form1->Memo1->Lines->Add("zeichne Kreis: "+toStr());
  };

  virtual void zeichne(TImage* img) const
  {
  img->Canvas->Ellipse(M.X()-r,M.Y()-r,M.X()+r,M.Y()+r);
//  img->Canvas->Arc(M.x-r,M.y-r,M.x+r,M.y+r,M.x+r,M.y+r,M.x+r,M.y+r);
  };

// e)
  void write(ofstream& f) const
  {
  f<<class_id<<' '<<M<<r<<endl;
  }

  static const char* class_id;

// d)
  void verschiebe_Position_um(const C2DPunkt& deltaPos)
  {
  M.verschiebe_um(deltaPos);
  }

// f)
  Kreis(ifstream& f)
  { // erzeugt einen Kreis mit den Daten aus dem ifstream
  f>>M>>r;
  }

};

const char* Kreis::class_id="Kreis";


class Rechteck:public Figur {
  C2DPunkt lu,ro; // Eckpunkte links unten und rechts oben
 public:
  Rechteck(C2DPunkt lu_, C2DPunkt ro_):lu(lu_),ro(ro_) {}

// a)
  virtual double Flaeche()   const {return (ro.Y()-lu.Y())*(ro.X()-lu.X()) ;}
  virtual double Umfang()    const {return 2*(ro.Y()-lu.Y()+ ro.X()-lu.X() );}

// b)
  AnsiString toStr() const
  {
  return "Rechteck links unten="+lu.toStr()+" rechts oben="+ro.toStr();
  }

// c)
  void zeichne() const
  {
  Form1->Memo1->Lines->Add("zeichne "+toStr());
  };

  virtual void zeichne(TImage* img) const
  { // zeichnet ein gefülltes Rechteck, das den Hintergrund verdeckt
  img->Canvas->Rectangle(lu.X(),lu.Y(),ro.X(),ro.Y());
  // Mit dem nächsten Aufruf wird nur der Rand gezeichnet
  // TRect Rect(lu.X(),lu.Y(),ro.X(),ro.Y());
  // img->Canvas->FrameRect(Rect);
  };

// e)
  void write(ofstream& f) const
  {
  f<<class_id<<' '<<lu<<ro<<endl;
  }

// f)
  Rechteck(ifstream& f):lu(f),ro(f)
  { // erzeugt ein Rechteck mit den Daten aus dem ifstream
  //   f>>lu>>ro; // wäre genauso möglich
  // Bei dieser Schreibweise würde dann nicht das Risiko bestehen,
  // dass die Reihenfolge der Datenelemente geändert wird und diese
  // in verkehrt zugeordnet werden.
  }

// d)
  void verschiebe_Position_um(const C2DPunkt& deltaPos)
  {
  lu.verschiebe_um(deltaPos);
  ro.verschiebe_um(deltaPos);
  }

  static const char* class_id;
};

const char* Rechteck::class_id="Rechteck";


// f)
void Zeichnung::LeseZeichnung(const char* Dateiname)
{// Diese Funktion muss bei einer Erweiterung um neue Figuren
 // angepasst werden.
c.clear();
ifstream f(Dateiname);
std::string s;
while (f>>s) // Lese den Namen der Figur
  {
    Figur* pf=0;
    if (s==Gerade::class_id)
      pf=new Gerade(f);
    else if (s==Kreis::class_id)
      pf=new Kreis(f);
    else if (s==Rechteck::class_id)
      pf=new Rechteck(f);
    else
      ShowMessage("unexpected data: "+AnsiString(s.c_str()));
    if (pf!=0)
      c.push_back(pf);
  }
f.close(); // überflüssig, aber nicht falsch
}

// g)
Zeichnung einfache_Zeichnung()
{
Zeichnung z;
z.einfuegen(new Rechteck(C2DPunkt(40,40),C2DPunkt(100,100)));
z.einfuegen(new Gerade(C2DPunkt(40,40),C2DPunkt(70,10)));
z.einfuegen(new Gerade(C2DPunkt(70,10),C2DPunkt(100,40)));
z.einfuegen(new Kreis(C2DPunkt(70,40),5));
return z;

/*
// Erzeuge eine Zeichnung:
Zeichnung z;
for (int i=0; i<10; i++)
  if (i%3==0)
    z.einfuegen(new Gerade(C2DPunkt(i,i+1),C2DPunkt(i+2,i+3)));
  else if (i%3==1)
    z.einfuegen(new Kreis(C2DPunkt(i,i+1),i+2));
  else if (i%3==2)
    z.einfuegen(new Rechteck(C2DPunkt(i,i+1),C2DPunkt(i+2,i+3)));
// Schreibe die Zeichnung in eine Datei:
z.write(Dateiname);
// Zeige die Daten an:
z.toMemo(Form1->Memo1);
return z;
*/
}


// g)
Zeichnung z=einfache_Zeichnung();

void testSchreibeDatei(const char* Dateiname)
{
z.write(Dateiname); // schreibe die Zeichnung in eine Datei
z.toMemo(Form1->Memo1); // zeige die Daten an
}

void testLeseDatei(const char* Dateiname)
{
z.LeseZeichnung(Dateiname); // lese die gespeicherten Daten
z.toMemo(Form1->Memo1); // zeige die Daten an
}

// h)
// Um dieses Programm um neue Figuren zu erweitern, müssen lediglich
// die Klassen für die neuen Figuren definiert werden und die Funktion
// LeseZeichnung erweitert werden.

} // end of namespace N_Aufg_4_10_1


void __fastcall TForm1::Button_4_10_1zeichnenClick(TObject *Sender)
{
N_Aufg_4_10_1::z.zeichne();
N_Aufg_4_10_1::z.zeichne(Form1->Image1);
}

void __fastcall TForm1::Button_4_10_1loeschenClick(TObject *Sender)
{
N_Aufg_4_10_1::z.loesche(Form1->Image1);
}

void __fastcall TForm1::Button_4_10_1verschiebenClick(TObject *Sender)
{
N_Aufg_4_10_1::C2DPunkt delta(10,5);
N_Aufg_4_10_1::z.verschiebe_um(Form1->Image1,delta);
}

const char* Dateiname="Zeichnung.drw";

void __fastcall TForm1::Button_4_10_1schreibenClick(TObject *Sender)
{
N_Aufg_4_10_1::testSchreibeDatei(Dateiname);
}

void __fastcall TForm1::Button_4_10_1lesenClick(TObject *Sender)
{
N_Aufg_4_10_1::testLeseDatei(Dateiname);
}

// --------------- Aufgabe 2 -------------------------------

void __fastcall TForm1::Button_4_10_2Click(TObject *Sender)
{
AnsiString a=
"a) Die Klassen für die speziellen Konten (Sparkonto, Girokonto, "
"Darlehenskonte usw.) werden von einer Basisklasse 'Konto' abgeleitet.";
AnsiString b=
"b) Die Klassen für die speziellen Steuerelemente (Button, Eingabefeld, "
"Anzeigefeld usw.) werden von einer Basisklasse 'Control' abgeleitet.";
AnsiString c=
"c) Die Klassen für die verschiedenen Motoren werden von einer "
"Basisklasse 'Motor' abgeleitet. \r\n"
"Die Klassen für die verschiedenen Temperaturfühler werden von einer "
"Basisklasse 'Temperaturfuehler' abgeleitet. \r\n"
"Die Klasse 'Waschmaschine' enthält dann für den Motor einen Zeiger "
"auf die Basisklasse 'Motor', für den Temperaturfühler einen Zeiger "
"auf die Basisklasse 'Temperaturfühler' usw. \r\n"
"\r\n"
"Der Teil der Steuerung, der für alle Modelle gleich ist, kann in "
"einer rein virtuellen Funktion definiert werden. \r\n"
"Die verschiedenen Waschmaschinenmodelle können dann durch "
"abgeleitete Klassen dargestellt werden, die im Konstruktor "
"einen Zeiger auf den verwendeten Motor, Temperaturfühler usw. "
"setzen. \r\n";
Memo1->Lines->Add(a);
Memo1->Lines->Add(b);
Memo1->Lines->Add(c);
}

// --------------- Aufgabe 6.4.12 --------------------------

void display(AnsiString s)
{
Form1->Memo1->Lines->Add(s);
}

class Basisklasse{
 public:
  virtual void anzeigen()=0;
  virtual void drucken()=0;
  virtual void aendern()=0;
};

class Bestellung : public Basisklasse{
 public:
  void anzeigen()
  {
    display("Best anzeigen");
  };
  void drucken()
  {
    display("Best drucken");
  };
  void aendern()
  {
    display("Best aendern");
  };
};

class Lagerbestand : public Basisklasse{
 public:
  void anzeigen()
  {
    display("LB anzeigen");
  };
  void drucken()
  {
    display("LB drucken");
  };
  void aendern()
  {
    display("LB aendern");
  };
};

enum Aktion {anzeigen,drucken,aendern};

void dispatch1(Aktion a, Basisklasse* p)
{
if (a==anzeigen)     p->anzeigen();
else if (a==drucken) p->drucken();
else if (a==aendern) p->aendern();
}

void dispatch(Aktion a, Basisklasse* p)
{
typedef void (Basisklasse::* ZEFB)();
ZEFB Aktionen[]={&Basisklasse::anzeigen, &Basisklasse::drucken,
                 &Basisklasse::aendern};

(p->*Aktionen[a])();
}


void __fastcall TForm1::Button_4_14_1Click(TObject *Sender)
{
Basisklasse* p;

Bestellung best;
p=&best;
dispatch(anzeigen,p);
dispatch(drucken,p);
dispatch(aendern,p);

Lagerbestand lag;
p=&lag;
dispatch(anzeigen,p);
dispatch(drucken,p);
dispatch(aendern,p);
}

