Middle+
Αναφέρετε τις προκλήσεις της ρίψης εξαιρέσεων (throw exception) στους κατασκευαστές και τους καταστροφείς.
sobes.tech AI
Απάντηση από AI
Η κλήση εξαίρεσης στον κατασκευαστή μπορεί να οδηγήσει σε διαρροή πόρων εάν κάποιοι πόροι ή αντικείμενα έχουν ήδη δεσμευτεί πριν από την εξαίρεση, και ο αντίστοιχος καταστροφέας δεν καλείται. Για την αποφυγή διαρροών, συνιστάται η χρήση RAII (Resource Acquisition Is Initialization), όπως έξυπνοι δείκτες ή περιτυλιγμένες κλάσεις που διασφαλίζουν την απελευθέρωση πόρων.
Η κλήση εξαίρεσης στον καταστροφέα οδηγεί σε απροσδιόριστη συμπεριφορά. Αν ο καταστροφέας καλείται ως αποτέλεσμα άλλης εξαίρεσης (stack unwinding), και ο ίδιος ρίξει μη επεξεργασμένη εξαίρεση, το πρόγραμμα θα τερματίσει με std::terminate. Σύμφωνα με το πρότυπο C++, οι καταστροφείς πρέπει να είναι noexcept.
#include <iostream>
#include <memory> // Για std::unique_ptr
struct Resource {
Resource() { std::cout << "Πόρος αποκτήθηκε\n"; }
~Resource() { std::cout << "Πόρος απελευθερώθηκε\n"; }
};
struct BadConstructor {
Resource res; // Πόρος θα δεσμευτεί πριν από την εξαίρεση
BadConstructor() {
std::cout << "Κατασκευαστής BadConstructor\n";
throw std::runtime_error("Σφάλμα στον κατασκευαστή"); // Ρίχνει εξαίρεση
// Ο καταστροφέας Resource δεν θα κληθεί αν η εξαίρεση ριχτεί πριν από το τέλος του κατασκευαστή
}
~BadConstructor() noexcept { // Οι καταστροφείς πρέπει να είναι noexcept
std::cout << "Καταστροφέας BadConstructor\n";
}
};
struct GoodConstructor {
std::unique_ptr<Resource> res; // Χρήση έξυπνου δείκτη για RAII
GoodConstructor() : res(std::make_unique<Resource>()) {
std::cout << "Κατασκευαστής GoodConstructor\n";
// Αν ριχτεί εξαίρεση, το unique_ptr διασφαλίζει την κλήση του καταστροφέα του Resource κατά την έξοδο από το scope
// throw std::runtime_error("Σφάλμα στον κατασκευαστή");
}
~GoodConstructor() noexcept {
std::cout << "Καταστροφέας GoodConstructor\n";
}
};
struct BadDestructor {
BadDestructor() { std::cout << "Κατασκευαστής BadDestructor\n"; }
~BadDestructor() {
std::cout << "Καταστροφέας BadDestructor\n";
// Δεν πρέπει να ρίχνει εξαίρεση από τον καταστροφέα, ειδικά κατά το unwind
// throw std::runtime_error("Σφάλμα στον καταστροφέα");
}
};
int main() {
// Παράδειγμα με κατασκευαστή (θα οδηγήσει σε διαρροή πόρων χωρίς RAII)
try {
// BadConstructor bc; // Αποσχολιάστε για να δείτε τη διαρροή
} catch (const std::exception& e) {
std::cout << "Πιάστηκε εξαίρεση: " << e.what() << "\n";
}
// Παράδειγμα με καλό κατασκευαστή (χρησιμοποιεί RAII)
try {
// GoodConstructor gc; // Αποσχολιάστε για να δείτε, δεν θα υπάρχει διαρροή
} catch (const std::exception& e) {
std::cout << "Πιάστηκε εξαίρεση: " << e.what() << "\n";
}
// Παράδειγμα με καταστροφέα (μπορεί να οδηγήσει σε std::terminate)
try {
// BadDestructor bd; // Αποσχολιάστε αν θέλετε να δείτε το πιθανό terminate
// throw std::runtime_error("Άλλη εξαίρεση"); // θα προκαλέσει unwind
} catch (const std::exception& e) {
std::cout << "Πιάστηκε εξαίρεση: " << e.what() << "\n";
}
return 0;
}
Πίνακας:
| Τοποθεσία κλήσης εξαίρεσης | Πιθανά προβλήματα | Συνιστώμενη λύση |
|---|---|---|
| Κατασκευαστής | Διαρροή πόρων | Χρησιμοποιήστε RAII (έξυπνοι δείκτες, περιτυλιγμένες κλάσεις) |
| Καταστροφέας | Απροσδιόριστη συμπεριφορά, std::terminate κατά το unwind |
Οι καταστροφείς πρέπει να είναι noexcept, αποφεύγετε την ρίψη εξαίρεσης |