15. Zpracovßnφ v²jimek I
  1. C++ Builder podporuje zpracovßnφ v²jimek C++, zpracovßnφ strukturovan²ch v²jimek C a zpracovßnφ v²jimek VCL. P°φklady uvedenΘ v tΘto kapitole pro C++ a C v²jimky mohou b²t p°elo₧eny jako konzolovΘ aplikace.

  2. V²jimky popisujφ v²jimeΦnΘ situace, kterΘ vy₧adujφ specißlnφ zpracovßnφ a mohou popisovat chyby vznikajφcφ za b∞hu aplikace (nap°. d∞lenφ nulou nebo nedostatek volnΘho mφsta v pam∞ti). Zpracovßnφ v²jimek nabφzφ zp∙sob °eÜenφ chyb. Obsluhu v²jimky m∙₧eme pou₧φt pro specifikaci akce, kterß bude provedena po vzniku chyby p°ed ukonΦenφm programu. Jsou zpracovßvßny pouze synchronnφ v²jimky, tzn. v²jimky, kterΘ jsou generovßny naÜφ aplikacφ. Jazyk C++ urΦuje, ₧e vÜechny v²jimky vznikajφ v bloku try (chrßn∞n² blok). Tento blok je nßsledovßn jednφm nebo vφce bloky catch, kterΘ identifikujφ a zpracujφ v²jimky generovanΘ v bloku try.
    Zpracovßnφ v²jimek vy₧aduje pou₧itφ t°φ klφΦov²ch slov: try, catch a throw. Kdy₧ program C++ zp∙sobφ v²jimku, pak °φzenφ je p°edßno na jinou Φßst programu (obsluhu v²jimky), kterß zpracovßvß tento typ v²jimky. Obsluha v²jimku zachycuje.
    Program v²jimku generuje provedenφm p°φkazu throw. P°φkaz throw se obecn∞ vyskytuje uvnit° funkce a mß tvar:
    throw "overflow";
    Tento p°φkaz vytvo°φ objekt, kter² popisuje typ v²jimky (v naÜem p°φpad∞ aritmetickΘ p°eteΦenφ). Jinß Φßst programu m∙₧e zachytit vygenerovan² objekt v²jimky a zpracovat jej. K pou₧itφ zpracovßnφ v²jimek, musφme obklopit nßÜ k≤d konstrukcφ try / catch. Syntaxe tΘto konstrukce je:
    blok-try:
      try slo₧en²-p°φkaz seznam-obsluh
    seznam-obsluh:
      obsluha [seznam-obsluh]
    obsluha:
      catch (deklarace-v²jimky) slo₧en²-p°φkaz
    deklarace-v²jimky:
      seznam-specifikacφ-typ∙ deklarßtor
      seznam-specifikacφ-typ∙ abstraktnφ-deklarßtor
      seznam-specifikacφ-typ∙
    p°φkaz-throw:
      throw [p°i°azovanφ-v²raz]
    KlφΦovß slova try, catch a throw nejsou v jazyku C povolena. Blok try musφ b²t nßsledovßn blokem catch. P°φkazy v bloku try jsou provßd∞ny b∞₧n²m zp∙sobem. Pokud v bloku try je generovßna v²jimka, pak °φzenφ provßd∞nφ programu je p°edßno na p°φsluÜnou obsluhu v²jimky. Obsluha je blok k≤du urΦen² k zpracovßnφ v²jimky. V jazyku C++ musφ b²t za blokem try alespo≥ jedna obsluha. Program musφ obsahovat obsluhu pro ka₧dou v²jimku, kterß m∙₧e b²t programem generovßna.
    P°esto₧e v²jimky mohou b²t libovolnΘho typu, je vhodnΘ vytvß°et objekty v²jimek. Objekty v²jimek jsou brßny jako libovolnΘ jinΘ objekty. V²jimka nese informace z mφsta vzniku v²jimky do mφsta jejφho zachycenφ. Jednß se o informaci, kterou u₧ivatel aplikace pot°ebuje znßt p°i v²skytu chyby za b∞hu aplikace. C++ mß °adu p°eddefinovan²ch v²jimek.
    Blok k≤du, ve kterΘm v²jimka m∙₧e vzniknout, musφ p°edchßzet klφΦovΘ slovo try. Jestli₧e v²jimka nastane, pak b∞h programu je p°eruÜen a program hledß p°φsluÜnou obsluhu v²jimky. Pokud obsluha je nalezena, pak °φzenφ je na nφ p°edßno, nenφ-li nalezena, pak volßnφm set_terminate mohla b²t nastavena funkce, kterß bude provedena; jinak program volß funkci terminate. Pokud ₧ßdnß v²jimka nenastane, pak program je proveden normßln∞.
    Nßsleduje °ada p°φklad∙, ve kter²ch jsou pou₧ity r∙znΘ zp∙soby generovßnφ v²jimek. Nßsledujφcφ p°φklad p°edßvß objekt Out obsluze (je pot°eba jej p°elo₧it jako konzolovou aplikaci):
    #include <conio.h>
    #include <iostream.h>
    bool pass;
    class Out{};
    void festival(bool firsttime){ if(firsttime) throw Out(); }
    int main()
    {
      try
      { pass = true;
        festival(true);
      }
      catch(Out& e){ pass = false; }
      cout << (pass ? "true" : "false") << endl;
      getch();
      return 0;
    }
    Zde v bloku try je volßna funkce festival, kterß generuje objekt Out. Tento objekt je zachycen obsluhou (je zde zm∞n∞na hodnota prom∞nnΘ pass na false). Nßsledujφcφ p°φklad generuje zachycenou v²jimku znova (p°φkaz throw bez parametru). V²jimka ji₧ musφ existovat.
    #include <conio.h>
    #include <iostream.h>
    bool pass;
    class Out{};
    void festival(bool firsttime){ if(firsttime) throw Out(); }
    void test()
    {
      try { festival(true); }
      catch(Out& e){ pass = false; throw; }
    }
    int main()
    {
      try
      { test();
      }
      catch(Out& e){ pass = true; }
      cout << (pass ? "true" : "false") << endl;
      getch();
      return 0;
    }
    Funkce test volß funkci festival, ve kterΘ je generovßna v²jimka Out. V²jimka je zachycena obsluhou v test a op∞tovn∞ generovßna (p°edßna ven z funkce). DalÜφ p°φklad volß nßmi nastavenou ukonΦujφcφ funkci (my_terminate) p°i pokusu o op∞tovnΘ generovßnφ v²jimky, kterß nenastala.
    #include <conio.h>
    #include <iostream.h>
    bool pass;
    class Out{};
    void my_terminate(){
      cout << "Äßdnß v²jimka" << endl;
      getch();
      exit(1);
    }
    void festival(bool firsttime){ if(firsttime) throw Out(); }
    void test()
    {
      try { festival(false); }
      catch(Out& e){ pass = false; }
      throw;  // nelze op∞tovn∞ generovat v²jimku, kterß nenastala
    }
    int main()
    {
      set_terminate(my_terminate);
      try {
        test();
      }
      catch(Out& e){ pass = true; }
      cout << (pass ? "true" : "false") << endl;
      getch();
      return 0;
    }
  3. DalÜφ p°φklad specifikuje seznam v²jimek, kterΘ mohou b²t generovßny jednotliv²mi funkcemi (festival a test). Ve funkci festival m∙₧e vznikat v²jimka Out a ve funkci test ₧ßdnß v²jimka.

  4. #include <conio.h>
    #include <iostream.h>
    bool pass;
    class Out{};
    void festival(bool firsttime) throw(Out) // pouze v²jimka Out
    {
      if(firsttime) throw Out();
    }
    void test() throw() // ₧ßdnß v²jimka
    {
      try {
        festival(true);
      }
      catch(Out& e){ pass = true; }
    }
    int main()
    {
      pass = false;
      test();
      cout<<(pass?"test zpracoval v²jimku":"v²jimka nezpracovßna")<<endl;
      getch();
      return 0;
    }
    Pokud festival generuje jinou v²jimku ne₧ Out, pak je pova₧ovßna za neoΦekßvanou v²jimku a °φzenφ programu je p°edßno na funkci unexpected, jak je ukßzßno v nßsledujφcφm p°φklad∞.
    Nßsledujφcφ p°φklad ukazuje test, kterß nemß generovat ₧ßdnou v²jimku. Pokud n∞jakß funkce (nap°. operßtor new) v t∞le test generuje v²jimku, pak v²jimka musφ b²t zpracovßna uvnit° test. Jinak v²jimka poruÜuje seznam specifikacφ v²jimek pro test. Funkcφ set_unexpected m∙₧eme nastavit jinou funkci pro neoΦekßvanou v²jimku; jinak, je volßna funkce unexpected.
    #include <conio.h>
    #include <iostream.h>
    bool pass;
    class Out{};
    void my_unexpected(){
      cout << "Chyba test" << endl;
      getch();
      exit(1);
    }
    void festival(bool firsttime) throw(Out) // pouze v²jimka Out
    {
      if(firsttime) throw Out();
    }
    void test() throw() // ₧ßdnß v²jimka
    {
      try { festival(true); }
      catch(Out& e){ pass = true; throw; } // op∞tovnΘ generovßnφ Out - chyba
    }
    int main()
    {
      set_unexpected(my_unexpected);
      pass = false;
      test();
      cout<<(pass?"test zpracoval v²jimku":"v²jimka nezpracovßna")<<endl;
      getch();
      return 0;
    }
    Kdy₧ v²jimka nastane, pak p°φkaz-throw inicializuje doΦasn² objekt typu T (typ parametru par) pou₧it² v throw(T par). DalÜφ kopie mohou b²t generovßny podle po₧adavk∙ p°ekladaΦe. Je tedy u₧iteΦnΘ definovat pro objekt v²jimky kopφrovacφ konstruktor, jak je ukßzßno v nßsledujφcφm p°φklad∞.
    #include <conio.h>
    #include <iostream.h>
    class festival
    {
    public:
      festival() { cout << "vytvo°enφ festival" << endl;  }
      festival(const festival&){cout<<"vytvo°enφ kopie festival" << endl;}
      ~festival() { cout << "zruÜenφ festival" << endl; }
    };
    int main()
    {
      try {
        cout << "generovßnφ v²jimky festival" << endl;
        throw(festival());
      }
      catch(festival&){
        cout << "vytvo°enφ festival" << endl;
      }
      getch();
      return 0;
    }
  5. Obsluha v²jimky je specifikovßna klφΦov²m slovem catch, kterΘ je umφst∞no bezprost°edn∞ za blok try. M∙₧e se takΘ vyskytnout bezprost°edn∞ za jin²m blokem catch. Ka₧dß v²jimka generovanß programem musφ b²t zachycena a zpracovßna obsluhou v²jimky. Obsluha zachycuje v²jimku, kdy₧ se jejφ typ shoduje (nebo m∙₧e b²t p°evedena) s typem v p°φkazu catch. P°i shod∞ je °φzenφ p°edßno obsluze. Obsluha urΦuje, kterΘ akce majφ b²t provedeny. Po dokonΦenφ obsluhy, program pokraΦuje za poslednφ obsluhou pro souΦasn² blok try. Äßdnß dalÜφ obsluha pro souΦasnou v²jimku nenφ vyhodnocena. K p°enosu °φzenφ mimo obsluhu m∙₧e b²t pou₧it p°φkaz goto. Pokud vznikne chyba p°i provßd∞nφ obsluhy v²jimky, pak program je ukonΦen. Obsluhu v²jimek si ukß₧eme na nßsledujφcφm p°φklad∞.

  6. #include <conio.h>
    #include <iostream.h>
    class festival{};
    class Harvest : public festival{};
    class Spring : public festival{};
    void ToHaveFun(int i)
    {
      if(i==1) throw(Harvest());
      else     throw(Spring());
    }
    int main()
    {
      try {
        ToHaveFun(0);
      }
      catch(const Harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const Spring&) {cout << "generovßno Spring Festival" << endl; }
      try {
        ToHaveFun(1);
      }
      catch(const Harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const Spring&) {cout << "generovßno Spring Festival" << endl; }
      getch();
      return 0;
    }
    DalÜφ p°φklad ukazuje, ₧e kdy₧ zachytφme v²jimku a tato v²jimka je souΦßstφ hierarchie t°φd, pak je nutno zaΦφt zachytßvßnφm nejvφce odvozenΘ t°φdy v²jimky.
    #include <conio.h>
    #include <iostream.h>
    class festival{};
    class harvest  : public festival{};
    class spring : public festival{};
    void ToHaveFun(int i)
    {
      if (i==1) throw(harvest());
      else if(i==2) throw(spring());
           else throw(festival());
    }
    int main()
    {
      /* P°i zachycovßnφ v²jimek zßle₧φ na po°adφ */
      try { ToHaveFun(0); }
      catch(const harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const spring&) {cout << "generovßno Spring Festival" << endl; }
      catch(const festival& ){cout << "generovßno Festival" << endl; }
      try { ToHaveFun(1); }
      catch(const harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const spring&) {cout << "generovßno Spring Festival" << endl; }
      catch(const festival& ){cout << "generovßno Festival" << endl; }
      try { ToHaveFun(2); }
      catch(const harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const spring&) {cout << "generovßno Spring Festival" << endl; }
      catch(const festival& ){cout << "generovßno Festival" << endl; }
      /* Zachytßvßnφ zßkladnφ t°φdy d°φve ne₧ odvozen²ch t°φd zp∙sobφ, ₧e
         odvozenß v²jimka je zachycena obsluhou zßkladnφ t°φdy v²jimky */
      try { ToHaveFun(1); }
      catch(const festival&){cout<<"generovßno Festival (je sprßvnΘ?)"<<endl;}
      catch(const harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const spring&) {cout << "generovßno Spring Festival" << endl; }
      try { ToHaveFun(2); }
      catch(const festival&){cout <<"generovßno Festival (je sprßvnΘ?)"<<endl;}
      catch(const harvest&){cout << "generovßno Harvest Festival" << endl; }
      catch(const spring&) {cout << "generovßno Spring Festival" << endl; }
      getch();
      return 0;
    }
    V nßsledujφcφm p°φkladu p°φkaz catch (...) zachytφ v²jimku libovolnΘho typu. Tento p°φkaz je jedinß obsluha v²jimky bloku try.
    #include <conio.h>
    #include <iostream.h>
    bool pass;
    class Out{};
    void festival(bool firsttime) throw(Out)
    {
      if(firsttime) throw Out();
    }
    void test() throw()
    {
      try { festival(true); }
      catch(...){ pass = true; }
    }
    int main()
    {
      pass = false;
      test();
      cout<<(pass?"test zpracoval v²jimku":"v²jimka nezpracovßna")<<endl;
      getch();
      return 0;
    }
  7. C++ poskytuje slu₧bu nazvanou specifikace v²jimek, kterß umo₧nφ specifikovat seznam v²jimek, kterΘ funkce m∙₧e generovat. Tato specifikace se zapisuje jako p°φpona deklarace funkce. Pou₧itφ p°φpony specifikacφ v²jimek neovliv≥uje typ funkce. V p°edchozφch p°φkladech specifikace v²jimek ji₧ byla pou₧ita. Nßsledujφ ukßzky deklaracφ funkcφ se specifikacemi v²jimek:

  8. void f1();                // Funkce generuje libovolnΘ v²jimky
    void f2() throw();        // Funkce negeneruje ₧ßdnou v²jimku
    void f3() throw( A, B* ); // Funkce m∙₧e generovat v²jimky odvozenΘ od A
                              // nebo od ukazatele na B
    Pokud funkce generuje v²jimku neuvedenou ve specifikaci, pak program volß unexpected.
  9. Win32 podporuje zpracovßnφ strukturovan²ch v²jimek C, kterΘ se podobajφ v²jimkßm C++. Jsou zde jistΘ odchylky, co₧ vy₧aduje opatrnost p°i mφchßnφ t∞chto dvou typ∙ v²jimek. P°i pou₧φvßnφ strukturovan²ch v²jimek je nutno v aplikacφch C++ Builderu dodr₧ovat tato pravidla:
  10. V C programech pou₧φvßme k implementaci strukturovan²ch v²jimek klφΦovß slova __except, __finally a __try. KlφΦovΘ slovo __try lze pou₧φt pouze v C programech. Pokud chceme zapsat p°enositeln² k≤d, pak strukturovanΘ v²jimky nenφ vhodnΘ pou₧φvat.
    StrukturovanΘ v²jimky mohou b²t zpracovßny pomocφ rozÜφ°enφ zpracovßnφ C++ v²jimek:
    try {
      funkce();
    }
    __except(__expr__) {
      // obsluha
    }
    __expr__ je v²raz, jeho₧ vyhodnocenφ je jedna ze t°ech hodnot: EXCEPTION_CONTINUE_SEARCH (0 - obsluha nenφ zahßjena a OS pokraΦuje v hledßnφ obsluhy v²jimky), EXCEPTION_CONTINUE_EXECUTION (-1 - provßd∞nφ pokraΦuje v bod∞ v²jimky) a EXCEPTION_EXECUTE_HANDLER(1 - obsluha v²jimky je provedena).
    Win32 poskytuje dv∞ funkce, kterΘ mohou b²t pou₧ity k zjiÜt∞nφ informacφ o aktivnφ v²jimce: GetExceptionCode a GetExceptionInformation. Jestli₧e chceme tyto funkce volat jako Φßst v²razu filtru, pak je musφme volat p°φmo v __except. V²raz filtru slou₧φ k filtrovßnφ v²jimek.
  11. Pokud mφchßme C++ a strukturovanΘ v²jimky, pak nesmφme zapomenout na n∞kterΘ v∞ci. I kdy₧ C++ Builder implementuje C++ v²jimky se strukturovan²mi v²jimkami Win32, pak v bloku __except jsou v²jimky C++ transparentnφ. Blok try m∙₧e b²t nßsledovßn jednφm blokem __except nebo alespo≥ jednφm blokem catch. Tyto bloky nelze mφchat, ale je mo₧no vno°it dva bloky try do sebe:

  12. try {
      EXCEPTION_POINTERS *xp;
      try {
        funkce();
      }
      __except(xfilter(xp = GetExceptionInformation())) {
        //...
      }
    }
    catch (...) {
      //...
    }
    Mφchßnφ v²jimek si ukß₧eme na nßsledujφcφm p°φklad∞. P°elo₧te jej jako konzolovou aplikaci a pokuste se pochopit, kdy v²jimky vznikajφ a jak jsou zpracovßny.
    #include <iostream.h>
    #include <conio.h>
    class Exception
    {
    public:
      Exception(char* s = "Unknown"){ what = strdup(s);      }
      Exception(const Exception& e ){ what = strdup(e.what); }
      ~Exception()                  { delete[] what;         }
      char* msg() const             { return what;           }
    private:
      char* what;
    };
    int main()
    {
      float e, f, g;
      try {
        try {
          f = 1.0;
          g = 0.0;
          try {
            cout << "v²jimka: " << endl;
            e = f / g;
          }
          __except(EXCEPTION_EXECUTE_HANDLER) {
            cout << "Zachycenφ strukturovanΘ v²jimky. " << endl;
            throw(Exception("Chyba: D∞lenφ 0"));
          }
        }
        catch(const Exception& e) {
          cout << "Zachycenφ C++ v²jimky: " << e.msg() << endl;
        }
      }
      __finally {
        cout << "C++ umo₧≥uje takΘ pou₧φt __finaly " << endl;
      }
      getch();
      return e;
    }
  13. Jak ji₧ jsme vid∞li v p°edchozφm p°φklad∞, model zpracovßnφ strukturovan²ch v²jimek podporuje ukonΦujφcφ blok (__finally). Tento blok je provßd∞n v₧dy (kdy₧ v²jimka vznikne i kdy₧ nevznikne).

  14. try {
      funkce();
    }
    __finally {
      // zde uvedenΘ p°φkazy jsou provedeny v₧dy
    }
    Pou₧itφ ukonΦovacφho bloku je uvedeno v nßsledujφcφm p°φklad∞.
    #include <conio.h>
    #include <iostream.h>
    int main()
    {
      float e, f, g;
      try {
        f = 1.0;
        g = 0.0;
        try {
          cout << "V²jimka: " << endl;
          e = f / g;
        }
        __except(EXCEPTION_EXECUTE_HANDLER) {
          cout << "Zachycenφ v²jimky. " << endl;
        }
      }
      __finally {
        cout << "Je provedeno takΘ. " << endl;
      }
      try {
        f = 1.0;
        g = 2.0;
        try {
          cout << "Nenφ v²jimka: " << endl;
          e = f / g;
        }
        __except(EXCEPTION_EXECUTE_HANDLER) {
          cout << "Zachycenφ v²jimky. " << endl;
        }
      }
      __finally {
        cout << "Je provedeno takΘ. " << endl;
      }
      getch();
      return e;
    }
15. Zpracovßnφ v²jimek I