====== Διαχείριση εξαίρεσης και παραγωγή νέας εξαίρεσης κατά τη διαχείριση της ======
Κάποιες φορές είναι επιθυμητό να διαχειριστούμε μία εξαίρεση προκειμένου να κλείσουμε κάποιο //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
}