====== Διαχείριση εξαίρεσης και παραγωγή νέας εξαίρεσης κατά τη διαχείριση της ====== Κάποιες φορές είναι επιθυμητό να διαχειριστούμε μία εξαίρεση προκειμένου να κλείσουμε κάποιο //resource//, αλλά στη συνέχεια θέλουμε να παράγουμε ξανά την ίδια εξαίρεση προκειμένου η τελική διαχείριση να γίνει παρακάτω. Δείτε το επόμενο απόσπασμα κώδικα από την κλάση PPMImage. Εάν το αρχείο που διαβάζουμε περιέχει κατά λάθος μία αρνητική τιμή θα παραχθεί ένα //std::bad_alloc exception//. Δεν θέλουμε να το διαχειριστούμε μέσα στον κατασκευαστή, διότι σε αυτή την περίπτωση ο κατασκευαστής θα επιστρέψει κανονικά και ο χρήστης δεν θα έιναι σε θέση να γνωρίζει ότι συνέβη σφάλμα. Παρόλα αυτά, θα θέλαμε να διαχειριστούμε εν μέρη την εξαίρεση στον κατασκευαστή, ώστε να κλείσουμε το ανοιχτό //ifstream//, αλλά στη συνέχεια να παράγουμε την ίδια εξαίρεση την οποία θα κληθεί η διαχειριστεί η μέθοδος που δημιουργεί το αντικείμενο. #include #include #include #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::ios_base::failure fex("File not found!"); 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> 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::bad_alloc &ex) { cerr << "std::bad_alloc occured!\n"; in.close(); throw ex; } } ~PPMImage() { for(int row=0; rowgetRaster() != nullptr) { cerr << "imgptr->getRaster() != nullptr\n"; delete imgptr->getRaster(); } else { cerr << "imgptr->getRaster() == nullptr\n"; } delete imgptr; } else { cerr << "imgptr == nullptr\n"; } } delete imgptr; return 0; } Το ενδεικτικό αρχείο εισόδου είναι το παρακάτω: P3 3 -2 255 255 0 0 255 255 0 0 255 255 255 0 255 0 255 0 128 128 128 Από τον παραπάνω κώδικα μπορούμε να συμπεράνουμε τα εξής: - Εφόσον παράγεται ένα //exception// o δείκτης //imgptr// μέσα στο //catch block// της συνάρτησης //main// έχει την αρχική του τιμή, δηλαδή **nullptr**. Αυτό είναι λογικό με βάση τις αρχές του //stack unwinding// που συζητήσαμε προηγούμενα. - Εάν εφαρμόσω τον τελεστή **delete** σε ένα δείκτη που έχει την τιμή **nullptr**, δεν παράγεται κάποιου είδους //exception//, αλλά ο κώδικας συνεχίζει κανονικά. ==== Ένα 2ο παράδειγμα ==== Στο προηγούμενο παράδειγμα κάντε την εξής αλλαγή. Αντικαταστήστε το //catch block// στον κατασκευαστή με το παρακάτω: catch(std::exception &ex) { cerr << "std::exception occured!\n"; in.close(); throw ex; } Το //exception std::bad_alloc// είναι απόγονος της κλάσης //std::exception// επομένως ο παραπάνω κώδικας θα πρέπει να δουλεύει σωστά και μετά την αλλαγή. Εν τούτοις παρατηρούμε ότι το πρόγραμμα αποτυγχάνει με ένα μήνυμα της μορφής $> ./PPMImageSample 3x2.ppm std::bad_alloc occured! terminate called after throwing an instance of 'std::exception' what(): std::exception Aborted (core dumped) Ο λόγος που συμβαίνει το παραπάνω είναι ότι το όταν το //catch block// παράγει και πάλι το //exception//, παράγει ένα αντικείμενο της κλάσης //std::exception// αποκόπτοντας το τμήμα του αντικειμένου που αφορά την απόγονο κλάση //std::bad_alloc//. Στη συνέχεια ένα τέτοιο αντικείμενο δεν μπορεί να το "πιάσει" το //catch block// της συνάρτησης //main//. Εάν πρέπει να παράγεται το ίδιο //exception// πράγμα που ήταν αρχικά επιθυμητό ο κώδικας στο //catch block// του κατασκευαστή θα πρέπει να γραφεί όπως παρακάτω, ώστε να παράγει ως //exception// το ίδιο αντικείμενο που έγινε //catch//. catch(std::exception &ex) { cerr << "std::exception occured!\n"; in.close(); throw; // rethrows the same exception object }