9 CPA 7 Exceptions

Wyjątki

wykrywanie problemów za pomocą warunków (przykład z funkcją gdzie wynik to arg z ref)
#include <iostream>
using namespace std;
bool div(float &res, float arg1, float arg2) {
 if(arg2 == 0.0)
  return false;
 res = arg1 / arg2;
 return true;
}
int main(void) {
 float r, a, b;
 while(cin >> a) {
  cin >> b;
  if(div(r,a,b))
   cout << r << endl;
  else
   cout << "Are you kidding me?" << endl;
 }
 return 0;
}


załączyć musimy dyrektywę
#include<exception>

dwa rodzaje błędów, logiczne i runtime

TRY włącza obserwację wyjątków (dzielenie przez 0 nie rzuca!)

catch(what) {        //łapie, ale konkretny
:
:
:
}

THROWN - rzuca konkretny ale może i rzucić string

float div(float a, float b) {
if(b == 0.0)
throw string("I can’t believe - division by zero :(");
return a / b;
}


Zeby złapać ten throw, przekazujemy przez &probl

int main(void) {
 float a, b;
 while(cin >> a) {
  try {
   cin >> b;
   cout << div(a, b) << endl;
  } catch (string &problem) {
   cout << "Look what you did, you bad user!" << endl;
   cout << problem << endl;
  }
 }
 return 0;
}


może być kilka warunków i każdy rzucać inny string do catcha

poniższy przykład zmienna str i str w catch to dwie różne zmienne
int main(void) {

string str;

try {

throw string("1");

} catch(string &str) {

cout << str;

}

return 0;

}


Jeśli Funkcja jest przerwana przez THROW to jest przerwana i nie zwraca wartości, ale obiekty stworzone w funkcji zostaną zamknięte (destruktorem) jak przy normalnym zakończeniu.

THROW może rzucić obiekt! taki obiekt nie zostanie zamknięty(zniszczony destruktorem)  po zamknieciu funkcji tylko po zakonczeniu bloku catch.

clas Class...

void DoCalculations(int i) {
if(i == 0)
throw Class("exception 1");
...

try {

   DoCalculations(i);

} catch (Class &exc) {
   cout << exc.msg << endl;
}




Funkcja MOŻE zawierać informację jakim wyjątkiem będzie rzucać:

void function(void) throw (Class) {
throw Class("object");

albo nawet kilka rodzai:
int doit(int i) throw(int, string, Class);

jeśli obiecamy jeden rodzaj a w kodzie zwróci inny to się skompiluje, ale podczas wykonywania bedzie błąd.


Jeśli funkcja rzuca różne rodzaje to możemy łapać to w różnych miejscach:
#include <iostream>
using namespace std;
class Class {
public:
 string msg;
 Class(string txt) : msg(txt) {}
};
void function(int i) throw (string,Class) {
    switch(i) {
 case 0 : throw string("string");
 case 1 : throw Class("object");
 default: cout << "OK" << endl;
    }
}
void level(int i) throw(Class) {
    try {
 function(i);
    } catch(string &exc) {
 cout << "String [" << exc << "] caught in level()" << endl;
    }    
}
int main(void) {
    for(int i = 0; i < 2; i++) {
        cout << "-------" << endl;
 try {
     level(i);
 } catch(Class &exc) {
     cout << "Object [" << exc.msg << "] caught in main()" << endl;
 }
    }
    return 0;
}



Jeśli program trafia na unexpected exception to przerywa się, ale można wywoać ostatnią funkcję. Wystarczy w main ustawić:

set_unexpected(lastchance);

i napisać funkcję lastchance np:

void lastchance(void) {
cout << "See what you've done! You've thrown an illegal exception!" << endl;
}


EXPLICIT


Jednoznaczne słowo kluczowe może zostać umieszczone przed deklaracją konstruktora klasy. Chroni konstruktor przed użyciem w dowolnym kontekście wymagającym użycia niejawnych konwersji. Ten konstruktor może być używany tylko w jawny sposób;

dlatego takie wywołanie spowoduje błąd:

class A {  
public:
   explicit A(int) {}
};

class B {  
public:
   B(int) {}
};
int main(void) {
 A a = 1;  // compilation error!
 B b = 1;
 return 0;
}

ale jak stworzymy obiekt w ten sposób, to będzie OK

A a(1);


Stworzenie takiej funkcji w przypadku zastosowania explicit też się nie powiedzie

A fun(void) { return 0; }

funkcja zwraca obiekt klasy A o wartości 0   (pytanie gdyby zwracała obiekt czy było by ok?)7.3.2



‘exception’ class

mamy klasę wyjątków zwana exception (bazowa) która ma funkcję WHAT zwracająca opis (string) wyjątku

try {
  dynamic_cast<AA &>(a).aa();
 } catch (exception ex) {
  cout << "[" << ex.what() << "]" << endl;
 }

tutaj nic nie rzuca, gdy mamy obszar kodu który się wywala możemy dać try, potem powyższy catch i opisze nam dlaczego się wywala


Klasy dziedziczące po sobie pierwsza po exception

logic_error  - logika algorytmu
domain_error   - wyjatki w zakresie liczb
invalid_argument - wyjątki niepoprawnego arguemntu do metody, funkcji, klasy
length_error - niepoprawna długośc bitowa
out_of_range - zły index do kolekcji danych
runtime_error _ różne uruchomieniowe wyjątki
range_error - obliczeniowe przekroczenie zakresu
overflow_error - za duży wynik do zmiennej
underflow_error  0 za mały wynik
bad_exception - kiedy funkcja próbuje rzucić wyjątek nieokreślony

Wszystkie powyższe można łapać za pomocą

catch(exception &ex)

należy pamiętać aby dopisać

set_unexpected(unexp);   (czy tylko w ostatnim przypadku bad exception?)

przykład kodu:
#include <iostream>
#include <exception>
using namespace std;

void unexp(void) {
 cout << "Unexpected exception arrived!" << endl;
 throw;
}

void function(void) throw(int,bad_exception) {
 throw 3.14;
}
int main(void) {
    set_unexpected(unexp);
 try {
  function();
 } catch(double f) {
  cout << "Got double" << endl;
 } catch(exception &ex) {
  cout << "It's so bad..." << endl;
 }
 cout << "Done" << endl;
 return 0;
}


równie dobrze można złapać zamiast exception &xx to konkretnie np  (bad_exception bad)



Można łapać w sposób uniwersalny:
#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;
void function(int i) {
 switch(i) {
 case 0: throw out_of_range("0");
 case 1: throw overflow_error("1");
 case 2: throw domain_error("2");
 }
}
int main(void) {
 for(int i = 0; i < 3; i++) {
  try {
   function(i);
  }
  catch(...) {
   cout << "Exception caught!" << endl;
  }
 }
 return 0;
}

ale wtedy nie rozpozna wyjątku:

albo przekazać i wyświetlic numer (przekazany w nawiasie)

catch(exception &ex) {
cout << "Exception caught: " << ex.what() << endl;
}


ale wtedy rzucony case 3 bez numeru:
case 3: throw exception();

rzuci:

Exception caught: Unknown exception


ALBO konkretnie !
int main(void) {
 for(int i = 0; i < 4; i++) {
  try {
   function(i);
  }
  catch(out_of_range &ofr) {
   cout << "Out of range: " << ofr.what() << endl;
  }
  catch(overflow_error &ovf) {
   cout << "Overflow: " << ovf.what() << endl;
  }
  catch(domain_error &dmn) {
   cout << "Domain: " << dmn.what() << endl;
  }
  catch(exception &ex) {
   cout << "Exception: " << ex.what() << endl;
  }
 }
 return 0;
}

i na koniec listy dopisać łapanie ogólne:

case 4: throw "so bad";
......
catch(...) {
cout << "Something bad happened" << endl;
}

Kolejność, od szczegółu do ogółu


Mozna zagnieżdżać Try, np try w funkcji i try w około wywołania. To czego nie wyłapie wewnątrz złapie catch zewnętrzny

A Jeśli chcemy żeby funkcja przechwyciła i potem rzuciła to samo dalej to:

catch(exception ex) {

throw ex;

}

STOS

przykład standardowego:
class Stack {
    private:
 int *stackstore;
 int stacksize;
 int SP;
    public:
 Stack(int size = 100);
 ~Stack();
 void push(int value);
 int pop(void);
};
Stack::Stack(int size) { 
 stackstore = new int[size];
 stacksize = size;
 SP = 0;
}
Stack::~Stack(void) {
 delete []stackstore;
}
void Stack::push(int value) {
 stackstore[SP++] = value;
}
int Stack::pop(void) { 
 return stackstore[--SP]; 
}

#include <iostream>
using namespace std;
int main(void) {
 Stack stk;
 stk.push(1);
 cout << stk.pop() << endl;
 return 0;
}

przykład mojego stosu w C
#include <stdio.h>
#include <stdlib.h>

typedef struct wezel {
   int val;
   struct wezel *next;
   struct wezel *head;
} wezel1;

wezel1 *head = NULL;



//dodawanie elementow stosu
void dodaj(int val) 
{
   //tworzy nowy tymczasowy,czyli nowy wezel1 o adresie temp
   //malloc alokuje pamiec o podanej wielkosci i zwraca wskaznik
   wezel1 *temp = malloc(sizeof(wezel1));

   //tymczasowy.val=val
   temp->val = val;

   //tymczasowy.next=head czyli tworzy do tylu
   temp->next = head;

   //tymczasowy staje sie nowym wezlem
   head = temp;
   printf("dodalem na stos: %d\n",val);
}


//wyswietlanie
void zdejmij() 
{

      if (head != NULL)  
      {
      wezel1 *temp = head;
      printf("Zdejmuje ze stosu :");
           
      printf(" %d\n",temp->val);
      head = temp->next;
      }
      else
      printf("Stos PUSTY, mozesz tylko dodac\n");
}


int main() {
   dodaj(5);
   dodaj(8);
   dodaj(13);
   zdejmij();
   zdejmij();
   dodaj(20);
   dodaj(80);
   zdejmij();
   zdejmij();
   zdejmij();
   zdejmij();
   dodaj(300);


   
   return 0;
}


możemy stworzyć własne wyjątki do złapania dziedziczące po takich jak chcemy:

deklaracje wyjątków:
#include <iostream>
#include <exception>
#include <stdexcept>

class stack_size_error : public std::length_error {
public:
 explicit stack_size_error(const std::string &msg);
};
class stack_bad_alloc : public std::bad_alloc {
public:
 explicit stack_bad_alloc(void);
};
class stack_overflow : public std::logic_error {
public:
 explicit stack_overflow(const std::string &msg);
};
class stack_empty : public std::logic_error {
public:
 explicit stack_empty(const std::string &msg);
};


definicje wyjatków
stack_size_error::stack_size_error(const std::string &msg) : std::length_error(msg) {
};
stack_bad_alloc::stack_bad_alloc(void) : std::bad_alloc() {
};
stack_overflow::stack_overflow(const std::string &msg) : std::logic_error(msg) {
};
stack_empty::stack_empty(const std::string &msg) : std::logic_error(msg) {
};

implementacja w klasie:
class Stack {
    private:
 int *stackstore;
 int stacksize;
 int SP;
    public:
 Stack(int size = 100) throw(stack_size_error, stack_bad_alloc);
 ~Stack();
 void push(int value) throw(stack_overflow);
 int pop(void) throw(stack_empty);
};

nowy konstruktor:
Stack::Stack(int size) throw(stack_size_error, stack_bad_alloc) { 
 if(size <= 0)
  throw stack_size_error("size must be >= 0");
 try {
  stackstore = new int[size];
 } catch(std::bad_alloc ba) {
  throw stack_bad_alloc();
 }
 stacksize = size;
 SP = 0;
}


nowa metoda push
void Stack::push(int value) throw(stack_overflow) {
 if(SP == stacksize)
  throw stack_overflow("stack size exceeded");
 stackstore[SP++] = value;
}

nowa metoda pop
int Stack::pop(void) throw(stack_empty) { 
 if(SP == 0)
  throw stack_empty("stack is empty");
 return stackstore[--SP]; 
}


ROZBICIE NA PLIKI na przykładzie stosu:

tworzymy plik nagłówkowy z deklaracjami (bez definicji)

nazwany  mystack.h

zawartość:
#ifndef __MYSTACK__
#define __MYSTACK__

#include <iostream>
#include <exception>
#include <stdexcept>

class stack_size_error : public std::length_error {
public:
 explicit stack_size_error(const std::string &msg);
};

class stack_bad_alloc : public std::bad_alloc {
public:
 explicit stack_bad_alloc(void);
};

class stack_overflow : public std::logic_error {
public:
 explicit stack_overflow(const std::string &msg);
};

class stack_empty : public std::logic_error {
public:
 explicit stack_empty(const std::string &msg);
};

class Stack {
    private:
 int *stackstore;
 int stacksize;
 int SP;
    public:
 Stack(int size = 100) throw(stack_size_error, stack_bad_alloc);
 ~Stack();
 void push(int value) throw(stack_overflow);
 int pop(void) throw(stack_empty);
};

#endif

plik z definicjami     mystack.cpp
zawartośc
#include "mystack.h"

stack_size_error::stack_size_error(const std::string &msg) : std::length_error(msg) {
};

stack_bad_alloc::stack_bad_alloc(void) : std::bad_alloc() {
};

stack_overflow::stack_overflow(const std::string &msg) : std::logic_error(msg) {
};

stack_empty::stack_empty(const std::string &msg) : std::logic_error(msg) {
};

Stack::Stack(int size) throw(stack_size_error, stack_bad_alloc) { 
 if(size <= 0)
  throw stack_size_error("size must be >= 0");
 try {
  stackstore = new int[size];
 } catch(std::bad_alloc ba) {
  throw stack_bad_alloc();
 }
 stacksize = size;
 SP = 0;
}

Stack::~Stack(void) {
 delete stackstore;
}

void Stack::push(int value) throw(stack_overflow) {
 if(SP == stacksize)
  throw stack_overflow("stack size exceeded");
 stackstore[SP++] = value;
}

int Stack::pop(void) throw(stack_empty) { 
 if(SP == 0)
  throw stack_empty("stack is empty");
 return stackstore[--SP]; 
}



plik main.cpp
#include "mystack.h"
#include <iostream>

using namespace std;

int main(void) {
 Stack stk;
 stk.push(1);
 cout << stk.pop() << endl;
 return 0;
}



kompilacja......................................................................


ZADANIA:
Zad1

#include <iostream>
    #include <exception>
 
    using namespace std;
 
    int main() {
        try {
            throw 2./4;
        }
        catch(int i) {
            cout << i;
        }
        return 0;
    }

Wynik. Błąd wykonania, rzuca zmienno przecinkowo łapie int

Zad2

#include <iostream>
    #include <exception>
    
    using namespace std;
    
    int main() {
        try {
            throw 3.14;
        }
        catch(double x) {
            x *= 2;
        }
        cout << x;
        return 0;
    }

Wynik. Błąd kompilacji. x poza catch nie istnieje


Zad3:


    #include <iostream>
    using namespace std;
    class X {
    public:
        X(void) { cout << 1; }
        ~X(void) { cout << 2; }
    };     
    X *exec() {
        X *x = new X();       //tu tworzy obiekt czyli wysw.1
        throw string("0");
        return x;
    }
 
    int main(void) {
        X *x;                   //tu nie tworzy obiektu !!!!
        try {
            delete exec();      //delete niepotrzebne, wywołanie funkcji
        } catch(string &s) {
            cout << s;           //tu łapie string "0" i wyswietla
        }
        return 0;
    }

Wynik 10


Zad4.

    #include <iostream>
    using namespace std;
    class X {
    public:
        X(void) { cout << 0; }
        ~X(void) { cout << 2; }
    };

    int main(void) {
        try {
            X *x = new X();    //tu wypisze z konstruktora 0
        throw true;
            delete x;       //tego nie wykona bo skacze do catch
        } catch(bool s) { 
            cout << s;     //łapie zmienna boolowska true czyli wypisze 1
        }
        return 0;
    }

Wynik 01. 

Zad5: