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
#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: