cpp:exception
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| cpp:exception [2019/05/06 07:16] – [Διαχείριση Εξαιρέσεων] gthanos | cpp:exception [2023/05/15 14:01] (current) – gthanos | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Δημιουργία | + | ====== Δημιουργία |
| Ας εξετάσουμε την κλάση **Vector** που είδαμε στην ενότητα της υπερφόρτωση τελεστών. Ο προσδιοριστής //nothrow// σε συνδυασμό με τον τελεστή **new** μας υποχρεώνει να ελέγξουμε την επιστρεφόμενη τιμή του τελεστή **new** για να δούμε έαν έχει αποτύχει η διαδικασία δέσμευσης μνήμης ή όχι και στην περίπτωση που έχουμε αποτυχία τερματίζουμε το πρόγραμμα. | Ας εξετάσουμε την κλάση **Vector** που είδαμε στην ενότητα της υπερφόρτωση τελεστών. Ο προσδιοριστής //nothrow// σε συνδυασμό με τον τελεστή **new** μας υποχρεώνει να ελέγξουμε την επιστρεφόμενη τιμή του τελεστή **new** για να δούμε έαν έχει αποτύχει η διαδικασία δέσμευσης μνήμης ή όχι και στην περίπτωση που έχουμε αποτυχία τερματίζουμε το πρόγραμμα. | ||
| - | <code cpp Vector.cpp> | + | <code cpp Vector.hpp> |
| #include < | #include < | ||
| #include < | #include < | ||
| Line 11: | Line 11: | ||
| class Vector { | class Vector { | ||
| int *array; | int *array; | ||
| - | | + | |
| | | ||
| public: | public: | ||
| - | Vector(int length=0); | + | Vector(long length=0); |
| ~Vector(); | ~Vector(); | ||
| int & | int & | ||
| }; | }; | ||
| + | </ | ||
| - | Vector:: | + | <code cpp Vector.cpp> |
| + | #include " | ||
| + | Vector:: | ||
| size = length; | size = length; | ||
| array = new (nothrow) int[size]; | array = new (nothrow) int[size]; | ||
| Line 35: | Line 38: | ||
| int & | int & | ||
| - | if(pos>=length()) { | + | if(pos>=size) { |
| cerr << " | cerr << " | ||
| | | ||
| Line 43: | Line 46: | ||
| </ | </ | ||
| - | Αν και η παραπάνω διαδικασία δεν είναι λανθασμένη, | + | Αν και η παραπάνω διαδικασία δεν είναι λανθασμένη, |
| <code cpp VectorUse.cpp> | <code cpp VectorUse.cpp> | ||
| - | #include " | + | #include " |
| int main() { | int main() { | ||
| - | | + | |
| cout << "Enter verctor size: "; | cout << "Enter verctor size: "; | ||
| cin >> size; | cin >> size; | ||
| Line 60: | Line 63: | ||
| Η παραγωγή ενός // | Η παραγωγή ενός // | ||
| - | <code cpp Vector.cpp> | + | <code cpp Vector.hpp> |
| #include < | #include < | ||
| #include < | #include < | ||
| Line 68: | Line 71: | ||
| class Vector { | class Vector { | ||
| int *array; | int *array; | ||
| - | | + | |
| | | ||
| public: | public: | ||
| - | Vector(int length=0); | + | Vector(long length=0); |
| ~Vector(); | ~Vector(); | ||
| int & | int & | ||
| }; | }; | ||
| - | Vector:: | + | </ |
| + | |||
| + | <code cpp Vector.cpp> | ||
| + | #include " | ||
| + | |||
| + | Vector:: | ||
| size = length; | size = length; | ||
| array = new int[size]; | array = new int[size]; | ||
| Line 97: | Line 105: | ||
| <code cpp VectorUse.cpp> | <code cpp VectorUse.cpp> | ||
| - | #include " | + | #include " |
| int main() { | int main() { | ||
| - | | + | |
| - | | + | |
| - | cout << "Enter verctor size: "; | + | cout << "Enter verctor size: "; |
| - | cin >> size; | + | cin >> size; |
| - | try { | + | try { |
| - | Vector v(size); | + | Vector v(size); |
| - | } catch(std:: | + | } catch(std:: |
| - | cout << "Vector size should be a positive integer! Retry...\n"; | + | std::cout << "Allocation failure!\n"; |
| - | continue; | + | |
| - | } | + | } |
| - | | + | |
| - | v.valueAt(i) = 100-1; | + | |
| - | } while(size< | + | |
| } | } | ||
| </ | </ | ||
| <WRAP tip 80% center round> | <WRAP tip 80% center round> | ||
| - | Στο παραπάνω απλό παράδειγμα είναι προφανές ότι είναι πιο απλό να ελέγξει κανείς το μέγεθος της παραμέτρου //size// πριν καλέσει τον κατασκευαστή. Σε αυτή την περίπτωση, | + | Στο παραπάνω απλό παράδειγμα είναι προφανές ότι είναι πιο απλό να ελέγξει κανείς το μέγεθος της παραμέτρου //size// πριν καλέσει τον κατασκευαστή. Σε αυτή την περίπτωση, |
| </ | </ | ||
| + | |||
| ===== Τύποι παραγόμενων εξαιρέσεων ===== | ===== Τύποι παραγόμενων εξαιρέσεων ===== | ||
| Line 151: | Line 158: | ||
| cout << "Enter option (1-5): "; | cout << "Enter option (1-5): "; | ||
| cin >> option; | cin >> option; | ||
| - | | + | |
| MyException ex; | MyException ex; | ||
| switch(option) { | switch(option) { | ||
| Line 170: | Line 177: | ||
| break; | break; | ||
| default: | default: | ||
| - | c = -10; throw c; // throw a character | + | c = -10; |
| + | | ||
| break; | break; | ||
| } | } | ||
| Line 197: | Line 205: | ||
| } | } | ||
| </ | </ | ||
| - | πιάνει όλους τους τύπους | + | διαχειρίζεται όλους τους τύπους |
| - | </ | + | |
| - | <WRAP tip 80% center round> | + | Εφόσον, χρησιμοποιήσετε |
| - | Στον παραπάνω κώδικα μπορείτε να παρατηρήσετε τα διαφορετικά μηνύματα που παράγονται ανάλογα με τον τύπο της εξαίρεσης. Παρατηρήστε επίσης ότι αν και παράγεται ένα αντικείμενο τύπου //short int//, το οποίο | + | |
| </ | </ | ||
| - | |||
| - | ===== Κληρονομικότητα εξαιρέσεων ===== | ||
| - | |||
| - | Ας υποθέσουμε ότι έχουμε τη σχέση κληρονομικότητας μεταξύ των κλάσεων **BaseException** και **DerivedException**, | ||
| - | |||
| - | <code cpp BaseException.h> | ||
| - | using namespace std; | ||
| - | |||
| - | class BaseException: | ||
| - | protected: | ||
| - | int a; | ||
| - | public: | ||
| - | BaseException(int a) { this->a = a; } | ||
| - | const char* what() const throw() { | ||
| - | //char s[64]; | ||
| - | char *s = new char [64]; | ||
| - | sprintf(s, " | ||
| - | return s; | ||
| - | } | ||
| - | }; | ||
| - | </ | ||
| - | |||
| - | <code cpp DerivedException.h> | ||
| - | #include " | ||
| - | using namespace std; | ||
| - | |||
| - | class DerivedException: | ||
| - | int b; | ||
| - | public: | ||
| - | DerivedException(int a, int b): BaseException(a) { this->b = b; } | ||
| - | const char* what() const throw() { | ||
| - | char *s = new char [64]; | ||
| - | sprintf(s, " | ||
| - | return s; | ||
| - | } | ||
| - | }; | ||
| - | </ | ||
| - | |||
| - | <code cpp ExceptionUse.cpp> | ||
| - | #include < | ||
| - | #include " | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | try { | ||
| - | int option; | ||
| - | cout << "Enter option (1-2): "; | ||
| - | cin >> option; | ||
| - | BaseException bex(-2); | ||
| - | DerivedException dex(4,5); | ||
| - | switch(option) { | ||
| - | case 1: | ||
| - | throw bex; | ||
| - | break; | ||
| - | case 2: | ||
| - | throw dex; | ||
| - | break; | ||
| - | } | ||
| - | } catch(BaseException ex) { | ||
| - | cout << ex.what(); | ||
| - | } catch(DerivedException ex) { | ||
| - | cout << ex.what(); | ||
| - | } | ||
| - | return 0; | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | O παραπάνω κώδικας παράγει το παρακάτω //warning// κατά τη μεταγλώττιση: | ||
| - | < | ||
| - | ExceptionUse.cpp: | ||
| - | } catch(DerivedException &ex) { | ||
| - | ^ | ||
| - | ExceptionUse.cpp: | ||
| - | } catch(BaseException &ex) { | ||
| - | ^ | ||
| - | </ | ||
| - | |||
| - | το οποίο εν συντομία λέει ότι ένα exception τύπου // | ||
| - | |||
| - | Εκτελέστε όμως τον παραπάνω κώδικα (παρά το warning) δίνοντας ορίσμα τους αριθμούς 1 και 2. Το αποτέλεσμα είναι το εξής: | ||
| - | < | ||
| - | gthanos@gthanos-DESKTOP: | ||
| - | Enter option (1-2): 1 | ||
| - | BaseException, | ||
| - | gthanos@gthanos-DESKTOP: | ||
| - | Enter option (1-2): 2 | ||
| - | BaseException, | ||
| - | </ | ||
| - | |||
| - | Παρατηρήστε ότι ενώ στην 2η περίπτωση παράγεται ένα // | ||
| - | |||
| - | Η απάντηση στο παραπάνω ερώτημα είναι ότι αν και παράγεται ένα αντικείμενο τύπου // | ||
| - | |||
| - | Ο τρόπος για να δουλέψει σωστά ο παραπάνω κώδικας είναι μέσα στο //catch block// να μην περάσουμε το αντικείμενο γιατί δημιουργείται αντίγραφο, | ||
| - | |||
| - | <code cpp ExceptionUse.cpp> | ||
| - | #include < | ||
| - | #include " | ||
| - | using namespace std; | ||
| - | |||
| - | int main() { | ||
| - | try { | ||
| - | int option; | ||
| - | cout << "Enter option (1-2): "; | ||
| - | cin >> option; | ||
| - | BaseException bex(-2); | ||
| - | DerivedException dex(4,5); | ||
| - | switch(option) { | ||
| - | case 1: | ||
| - | throw bex; | ||
| - | break; | ||
| - | case 2: | ||
| - | throw dex; | ||
| - | break; | ||
| - | } | ||
| - | } catch(BaseException &ex) { | ||
| - | cout << ex.what(); | ||
| - | } catch(DerivedException &ex) { | ||
| - | cout << ex.what(); | ||
| - | } | ||
| - | return 0; | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Πλέον το αποτέλεσμα της εκτέλεσης είναι το αναμενόμενο | ||
| - | < | ||
| - | gthanos@gthanos-DESKTOP: | ||
| - | Enter option (1-2): 1 | ||
| - | BaseException, | ||
| - | gthanos@gthanos-DESKTOP: | ||
| - | Enter option (1-2): 2 | ||
| - | DerivedException, | ||
| - | </ | ||
| <WRAP tip 80% center round> | <WRAP tip 80% center round> | ||
| - | Το πιάσιμο | + | Στον παραπάνω κώδικα μπορείτε να παρατηρήσετε τα διαφορετικά μηνύματα που παράγονται ανάλογα με τον τύπο |
| </ | </ | ||
| <WRAP tip 80% center round> | <WRAP tip 80% center round> | ||
| - | Είναι προφανές ότι η σειρά των //catch blocks// θα έπρεπε να είναι | + | Στον παραπάνω κώδικα παρατηρήστε ότι για |
| </ | </ | ||
| - | |||
| - | ===== Stack Unwinding ===== | ||
| - | |||
| - | Κατά την δημιουργία ενός // | ||
| - | |||
| - | <code cpp StackUnwinding.cpp> | ||
| - | #include < | ||
| - | // called by FFF() | ||
| - | void FFFF() { | ||
| - | std::cout << "Start FFFF\n"; | ||
| - | std::cout << "FFFF throwing int literal exception\n"; | ||
| - | throw 100; | ||
| - | std::cout << "End FFFF\n"; | ||
| - | |||
| - | } | ||
| - | // called by FF() | ||
| - | void FFF() { | ||
| - | std::cout << "Start FFF\n"; | ||
| - | FFFF(); | ||
| - | std::cout << "End FFF\n"; | ||
| - | } | ||
| - | // called by F() | ||
| - | void FF() { | ||
| - | std::cout << "Start FF\n"; | ||
| - | try { | ||
| - | FFF(); | ||
| - | } catch(char) { | ||
| - | std::cerr << "FF caught double exception\n"; | ||
| - | } | ||
| - | std::cout << "End FF\n"; | ||
| - | } | ||
| - | // called by main() | ||
| - | void F() { | ||
| - | std::cout << "Start F\n"; | ||
| - | try { | ||
| - | FF(); | ||
| - | } catch (int) { | ||
| - | | ||
| - | } catch (char) { | ||
| - | | ||
| - | } | ||
| - | std::cout << "End F\n"; | ||
| - | } | ||
| - | |||
| - | int main() { | ||
| - | std::cout << "Start main\n"; | ||
| - | try { | ||
| - | F(); | ||
| - | } catch (int) { | ||
| - | std::cerr << "main caught int exception\n"; | ||
| - | } | ||
| - | std::cout << "End main\n"; | ||
| - | return 0; | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Το αποτέλεσμα που εκτυπώνεται στην οθόνη είναι το εξής: | ||
| - | < | ||
| - | Start main | ||
| - | Start F | ||
| - | Start FF | ||
| - | Start FFF | ||
| - | Start FFFF | ||
| - | FFFF throwing int literal exception | ||
| - | F caught int exception | ||
| - | End F | ||
| - | End main | ||
| - | </ | ||
| - | |||
| - | H εξέλιξη του //program stack// στο παρακάτω πρόγραμμα δίνεται στο παρακάτω διάγραμμα. Παρατηρήστε ότι το exception παράγεται στη συνάρτηση **FFFF()**, αλλά " | ||
| - | |||
| - | {{ : | ||
| - | |||
| - | ===== Διαχείριση μίας εξαίρεσης και παραγωγή νέας εξαίρεσης κατά την διαχείριση της ===== | ||
| - | |||
| - | Κάποιες φορές είναι επιθυμητό να διαχειριστούμε μία εξαίρεση προκειμένου να κλείσουμε κάποιο // | ||
| - | |||
| - | <code cpp PPMImageSample.cpp> | ||
| - | #include < | ||
| - | #include < | ||
| - | #include <ios> | ||
| - | #include < | ||
| - | |||
| - | using namespace std; | ||
| - | |||
| - | class PPMImage { | ||
| - | int width, height, colordepth; | ||
| - | int **raster; | ||
| - | public: | ||
| - | PPMImage(char *filename) { | ||
| - | string str; | ||
| - | unsigned char red, green, blue; | ||
| - | ifstream in(filename); | ||
| - | if(!in.is_open()) { | ||
| - | std:: | ||
| - | throw fex; | ||
| - | } | ||
| - | try { | ||
| - | in >> str; | ||
| - | in >> str; | ||
| - | width = atoi(str.c_str()); | ||
| - | in >> str; | ||
| - | height = atoi(str.c_str()); | ||
| - | in >> str; | ||
| - | colordepth = atoi(str.c_str()); | ||
| - | raster = new int*[height]; | ||
| - | for(int row=0; row< | ||
| - | raster[row] = new int[width]; | ||
| - | for(int row=0; row< | ||
| - | for(int col=0; col< | ||
| - | cin >> str; | ||
| - | red = (unsigned char) atoi(str.c_str()); | ||
| - | cin >> str; | ||
| - | green = (unsigned char) atoi(str.c_str()); | ||
| - | cin >> str; | ||
| - | blue = (unsigned char) atoi(str.c_str()); | ||
| - | raster[row][col] = 0; | ||
| - | raster[row][col] = (red << 16) | (green << 8) | blue; | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | catch(std:: | ||
| - | cerr << " | ||
| - | in.close(); | ||
| - | throw ex; | ||
| - | } | ||
| - | } | ||
| - | | ||
| - | ~PPMImage() { | ||
| - | for(int row=0; row< | ||
| - | delete raster[row]; | ||
| - | delete raster; | ||
| - | } | ||
| - | | ||
| - | int **getRaster() { return raster; } | ||
| - | | ||
| - | }; | ||
| - | |||
| - | int main(int argc, char *argv[]) { | ||
| - | PPMImage *imgptr=nullptr; | ||
| - | try{ | ||
| - | imgptr = new PPMImage(argv[1]); | ||
| - | } | ||
| - | catch(ios_base:: | ||
| - | cerr << "File '" | ||
| - | } | ||
| - | catch(bad_alloc &ex) { | ||
| - | cerr << " | ||
| - | if (imgptr!=nullptr) { | ||
| - | cerr << " | ||
| - | if(imgptr-> | ||
| - | cerr << " | ||
| - | delete imgptr-> | ||
| - | } | ||
| - | else { | ||
| - | cerr << " | ||
| - | } | ||
| - | delete imgptr; | ||
| - | } | ||
| - | else { | ||
| - | cerr << " | ||
| - | } | ||
| - | } | ||
| - | delete imgptr; | ||
| - | return 0; | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Το ενδεικτικό αρχείο εισόδου είναι το παρακάτω: | ||
| - | |||
| - | <code cpp 3x2.ppm> | ||
| - | P3 | ||
| - | 3 -2 255 | ||
| - | 255 0 0 255 255 0 0 255 255 | ||
| - | 255 0 255 0 255 0 128 128 128 | ||
| - | </ | ||
| - | |||
| - | Από τον παραπάνω κώδικα μπορούμε να συμπεράνουμε τα εξής: | ||
| - | - Εφόσον παράγεται ένα // | ||
| - | - Εάν εφαρμόσω τον τελεστή **delete** σε ένα δείκτη που έχει την τιμή **nullptr**, | ||
| - | |||
| - | ==== Ένα 2ο παράδειγμα ==== | ||
| - | |||
| - | Στο προηγούμενο παράδειγμα κάντε την εξής αλλαγή. Αντικαταστήστε το //catch block// στον κατασκευαστή με το παρακάτω: | ||
| - | |||
| - | <code cpp> | ||
| - | catch(std:: | ||
| - | cerr << " | ||
| - | in.close(); | ||
| - | throw ex; | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Το //exception std:: | ||
| - | < | ||
| - | $> ./ | ||
| - | std:: | ||
| - | terminate called after throwing an instance of ' | ||
| - | what(): | ||
| - | Aborted (core dumped) | ||
| - | </ | ||
| - | |||
| - | Ο λόγος που συμβαίνει το παραπάνω είναι ότι το όταν το //catch block// παράγει και πάλι το // | ||
| - | |||
| - | <code cpp> | ||
| - | catch(std:: | ||
| - | cerr << " | ||
| - | in.close(); | ||
| - | throw; | ||
| - | } | ||
| - | </ | ||
| - | ===== Δήλωση των εξαιρέσεων που παράγουν οι συναρτήσεις (exception specifiers) ===== | ||
| - | |||
| - | Κατά τη δήλωση μίας συνάρτησης είναι δυνατόν να ορίσετε εάν αυτή μπορεί να παράγει κάποιου είδους // | ||
| - | - '' | ||
| - | - '' | ||
| - | - '' | ||
| - | |||
| - | Τους παραπάνω ορισμούς είναι δυνατόν να τους συναντήσετε στις // | ||
| - | |||
| - | /* | ||
| - | ===== Function try blocks ===== | ||
| - | */ | ||
| - | |||
| - | |||
| - | |||
| - | |||
| - | |||
cpp/exception.1557126986.txt.gz · Last modified: 2019/05/06 06:16 (external edit)
