//: C10:VirtualConstructor.cpp
// Z knihy "Myslíme v jazyce C++, část 2" od Bruce Eckela a Chucka Allisona.
// (c) 1995-2004 MindView, Inc. Všechna práva vyhrazena.
// Přečtěte si podmínky používání zdrojových kódů umístěné v souboru 
// 'Licence.txt' dodávaném spolu s balíčkem kódu dostupným na www.MindView.net.
#include <iostream>
#include <string>
#include <stdexcept>
#include <stdexcept>
#include <cstddef>
#include <vector>
#include "../purge.h"
using namespace std;

class Tvar {
  Tvar* s;
  // zbránit přiřazení a použití kopírovacího konstruktoru
  Tvar(Tvar&);
  Tvar operator=(Tvar&);
protected:
  Tvar() { s = 0; }
public:
  virtual void vykreslit() { s->vykreslit(); }
  virtual void smazat() { s->smazat(); }
  virtual void test() { s->test(); }
  virtual ~Tvar() {
    cout << "~Tvar" << endl;
    if(s) {
      cout << "Virtuální volání: ";
      s->smazat(); // Virtuální volání
    }
    cout << "mazání s: ";
    delete s; // Polymorfní mazání
    // (smazat 0 je platná operace; nic se nestane)
  }
  class ChybneVytvoreniTvaru : public logic_error {
  public:
    ChybneVytvoreniTvaru(string typ)
    : logic_error("Nelze vytvořit typ " + typ) {}
  };
  Tvar(string typ) throw(ChybneVytvoreniTvaru);
};

class Kruh : public Tvar {
  Kruh(Kruh&);
  Kruh operator=(Kruh&);
  Kruh() {} // Soukromý konstruktor
  friend class Tvar;
public:
  void vykreslit() { cout << "Kruh::vykreslit" << endl; }
  void smazat() { cout << "Kruh::smazat" << endl; }
  void test() { vykreslit(); }
  ~Kruh() { cout << "Kruh::~Kruh" << endl; }
};

class Ctverec : public Tvar {
  Ctverec(Ctverec&);
  Ctverec operator=(Ctverec&);
  Ctverec() {}
  friend class Tvar;
public:
  void vykreslit() { cout << "Ctverec::vykreslit" << endl; }
  void smazat() { cout << "Ctverec::smazat" << endl; }
  void test() { vykreslit(); }
  ~Ctverec() { cout << "Ctverec::~Ctverec" << endl; }
};

Tvar::Tvar(string typ) throw(Tvar::ChybneVytvoreniTvaru) {
  if(typ == "Kruh")
    s = new Kruh;
  else if(typ == "Ctverec")
    s = new Ctverec;
  else throw ChybneVytvoreniTvaru(typ);
  vykreslit();  // Virtuální volání v konstruktoru
}

char* sl[] = { "Kruh", "Ctverec", "Ctverec",
  "Kruh", "Kruh", "Kruh", "Ctverec" };

int main() {
  vector<Tvar*> tvary;
  cout << "Volani virtuálního konstruktoru:" << endl;
  try {
    for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
      tvary.push_back(new Tvar(sl[i]));
  } catch(Tvar::ChybneVytvoreniTvaru e) {
    cout << e.what() << endl;
    purge(tvary);
    return EXIT_FAILURE;
  }
  for(size_t i = 0; i < tvary.size(); i++) {
    tvary[i]->vykreslit();
    cout << "test" << endl;
    tvary[i]->test();
    cout << "end test" << endl;
    tvary[i]->smazat();
  }
  Tvar c("Kruh"); // Vytvořít na zásobníku
  cout << "volání destruktoru:" << endl;
  purge(tvary);
} ///:~
