User Tools

Site Tools


cpp:exception

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
Next revisionBoth sides next revision
cpp:exception [2018/05/18 11:45] – created gthanoscpp:exception [2019/05/06 07:56] – [Δημιουργία και διαχείριση της εξαίρεσης] gthanos
Line 1: Line 1:
-====== Διαχείριση Εξαιρέσεων ======+====== Δημιουργία & Διαχείριση Εξαιρέσεων ======
  
-Ας εξετάσουμε την κλάση **Vector** που είδαμε στην υπερφόρτωση των τελεστών. Ο προσδιοριστής //nothrow// σε συνδυασμό με τον τελεστή **new** μας υποχρεώνει να ελέγξουμε την επιστρεφόμενη τιμή του τελεστή **new** για να δούμε έαν έχει αποτύχει η διαδικασία δέσμευσης μνήμης ή όχι και στην περίπτωση που έχουμε αποτυχία τερματίζουμε το πρόγραμμα. +Ας εξετάσουμε την κλάση **Vector** που είδαμε στην ενότητα της υπερφόρτωση τελεστών. Ο προσδιοριστής //nothrow// σε συνδυασμό με τον τελεστή **new** μας υποχρεώνει να ελέγξουμε την επιστρεφόμενη τιμή του τελεστή **new** για να δούμε έαν έχει αποτύχει η διαδικασία δέσμευσης μνήμης ή όχι και στην περίπτωση που έχουμε αποτυχία τερματίζουμε το πρόγραμμα. 
  
 <code cpp Vector.cpp> <code cpp Vector.cpp>
 +#include <iostream>
 +#include <cstdlib>
 +
 +using namespace std;
 +
 class Vector { class Vector {
   int *array;   int *array;
-  unsigned int size;+  int size;
      
 public: public:
-  Vector(unsigned int length=0);+  Vector(int length=0);
   ~Vector();   ~Vector();
-  int &valueAt(unsigned int pos) const;  // returns a reference to element at position pos+  int &valueAt(int pos) const;  // returns a reference to element at position pos
 }; };
  
-Vector::Vector(unsigned int length) {+Vector::Vector(int length) {
   size = length;   size = length;
   array = new (nothrow) int[size];   array = new (nothrow) int[size];
Line 29: Line 34:
 } }
  
-int &Vector::valueAt(unsigned int pos) const {+int &Vector::valueAt(int pos) const {
   if(pos>=length()) {   if(pos>=length()) {
      cerr << "Invalid access position!\n";      cerr << "Invalid access position!\n";
Line 38: Line 43:
 </code> </code>
  
-Αν και η παραπάνω διαδικασία δεν είναι λανθασμένη, έχει το βασικό μειονέκτημα ότι θα πρέπει να τερματίσουμε το πρόγραμμα, ακόμη και εάν ο λόγος αποτυχίας είναι ότι ο χρήστης της κλάσης επέτρεψε το πέρασμα αρνητικής τιμής ως όρισμα στον κατασκευαστή.+Αν και η παραπάνω διαδικασία δεν είναι λανθασμένη, έχει το βασικό μειονέκτημα ότι θα πρέπει να τερματίσουμε το πρόγραμμα, ακόμη και εάν ο λόγος αποτυχίας είναι ότι ο χρήστης της κλάσης επέτρεψε το πέρασμα αρνητικής τιμής ως όρισμα στον κατασκευαστή. Ο λόγος είναι ότι στην περίπτωση που αποτύχει η δέσμευση της μνήμης, λόγω λανθασμένου ορίσματος, ο κατασκευαστής της κλάσης **Vector** επιστρέφει ένα αντικείμενο το οποίο δεν είναι σωστά αρχικοποιημένο.
  
 <code cpp VectorUse.cpp> <code cpp VectorUse.cpp>
 +#include "Vector.cpp"
 +
 int main() { int main() {
   int size;   int size;
Line 51: Line 58:
 </code> </code>
  
-Η παραγωγή ενός exception μπορεί να επιλύσει πιο αποτελεσματικά το παραπάνω πρόβλημα διότι επιτρέπει την διαχείριση γεγονότων που δεν επιτρέπουν την ομαλή ροή του προγράμματος. Στο παράδειγμα του κατασκευαστή της κλάσης **Vector**, η αποτυχία κλήσης του τελεστή **new** (χωρίς τον προσδιοριστή //notrhow//) παράγει ένα //exception// τύπου [[http://www.cplusplus.com/reference/new/bad_alloc/|std::bad_alloc]], το οποίο μπορούμε να διαχειριστούμε, όπως παρακάτω:+Η παραγωγή ενός //exception// μπορεί να επιλύσει πιο αποτελεσματικά το παραπάνω πρόβλημαδιότι υποστηρίζει τη διαχείριση συμβάντων που δεν επιτρέπουν την ομαλή εκτέλεση του προγράμματος. Στο παράδειγμα του κατασκευαστή της κλάσης **Vector**, η αποτυχία κλήσης του τελεστή **new** (χωρίς τον προσδιοριστή //notrhow//) παράγει ένα //exception// τύπου [[http://www.cplusplus.com/reference/new/bad_alloc/|std::bad_alloc]], το οποίο μπορούμε να διαχειριστούμε στη μέθοδο //main//, όπως παρακάτω:
  
 <code cpp Vector.cpp> <code cpp Vector.cpp>
 +#include <iostream>
 +#include <cstdlib>
 +
 +using namespace std;
 +
 class Vector { class Vector {
   int *array;   int *array;
-  unsigned int size;+  int size;
      
 public: public:
-  Vector(unsigned int length=0);+  Vector(int length=0);
   ~Vector();   ~Vector();
-  int &valueAt(unsigned int pos) const;  // returns a reference to element at position pos+  int &valueAt(int pos) const;  // returns a reference to element at position pos
 }; };
  
-Vector::Vector(unsigned int length) {+Vector::Vector(int length) {
   size = length;   size = length;
-  array = new (nothrow) int[size];+  array = new int[size];
   for(int i=0; i<size; i++)   for(int i=0; i<size; i++)
     array[i] = 0;     array[i] = 0;
Line 75: Line 87:
 } }
  
-int &Vector::valueAt(unsigned int pos) const { +int &Vector::valueAt(int pos) const { 
-  if(pos>=length()) {+  if(pos>=size) {
      cerr << "Invalid access position!\n";      cerr << "Invalid access position!\n";
      return array[size-1];      return array[size-1];
Line 85: Line 97:
  
 <code cpp VectorUse.cpp> <code cpp VectorUse.cpp>
 +#include "Vector.cpp"
 +
 int main() { int main() {
   int size;   int size;
Line 101: Line 115:
 } }
 </code> </code>
 +
 +<WRAP tip 80% center round>
 +Στο παραπάνω απλό παράδειγμα είναι προφανές ότι είναι πιο απλό να ελέγξει κανείς το μέγεθος της παραμέτρου //size// πριν καλέσει τον κατασκευαστή. Σε αυτή την περίπτωση, ο έλεγχος θα πρέπει να γίνεται από το χρήστη της εκάστοτε βιβλιοθήκης, ενώ η βιβλιοθήκη δεν παρέχει καμία εγγύηση για τον τρόπο συμπεριφοράς της εάν περαστούν λανθασμένα ορίσματα. Τα //exceptions// εξασφαλίζουν ότι η βιβλιοθήκη θα παράξει μία εξαίρεση εάν η δέσμευση της μνήμης αποτύχει.
 +</WRAP>
 +
 +===== Τύποι παραγόμενων εξαιρέσεων =====
 +
 +Στη C++ μπορείτε να δημιουργήσετε ένα Exception χρησιμοποιώντας οποιονδήποτε τύπο δεδομένων, δηλαδή δεν απαιτείται τα αντικείμενα που παράγονται να είναι απόγονοι συγκεκριμένης κλάσης. Δείτε μερικά παραδείγματα παραγωγής έγκυρων //exceptions// παρακάτω:
 +
 +<code cpp>
 +throw -1;                     // throw an integer value
 +throw ENUM_INVALID_INDEX;     // throw an enum value
 +throw "Invalid argument!";    // throw a literal C-style (const char*) string
 +double pi=3.14159; throw pi;  // throw a double variable that was previously defined
 +throw MyException("Fatal!");  // Throw an object of class MyException
 +</code>
 +
 +===== Δημιουργία και διαχείριση της εξαίρεσης =====
 +
 +Όπως σε όλες τις γλώσσες αντικειμενοστραφούς προγραμματισμού η παραγωγή μιας εξαίρεσης θα πρέπει να γίνει μέσα σε ένα //try block// και η διαχείριση της μέσα σε ένα //catch block// που ακολουθεί το //try block//. Δείτε το παρακάτω ενδεικτικό παράδειγμα, όπου ανάλογα με την είσοδο που βάζει ο χρήστης παράγεται διαφορετικού τύπου //exception//
 +
 +<code cpp ExceptionHandling.cpp>
 +#include <iostream>
 +using namespace std;
 +
 +class MyException: public std::exception {
 +public:
 +  const char* what() const throw() {
 +    return "Just another std::exception";
 +  }
 +};
 +
 +int main() {
 +  try {
 +    int option;
 +    cout << "Enter option (1-5): ";
 +    cin >> option;
 +    char c;
 +    MyException ex;
 +    switch(option) {
 +      case 1:
 +        throw 10;  // throw an int literal
 +        break;
 +      case 2:
 +        throw 2.5;  //throw a double literal
 +        break;
 +      case 3:
 +        throw "C++"; //throw a char * literal
 +        break;
 +      case 4:
 +        throw string("C++"); //throw a std::string
 +        break;
 +      case 5:
 +        throw ex; //throw a MyException object
 +        break;
 +      default:
 +        c = -10; 
 +        throw c;  // throw a character
 +        break;
 +    }
 +  } catch(int ex) {
 +    cout << "Got '"<< ex <<"'!\n";
 +  } catch(double ex) {
 +    cout << "Got '"<< ex <<"'!\n";
 +  } catch(const char *ex) {
 +    cout << "Got char* '"<< ex <<"'!\n";
 +  } catch(const string &ex) {
 +    cout << "Got string '"<< ex <<"'!\n";
 +  } catch(const MyException &ex) {
 +    cout << "Got '"<< ex.what() <<"'!\n";
 +  } catch(...) {    // catch any exception not caught above!
 +    cout << "Got an exception of unknown type!\n";
 +  }
 +  cout << "Successfully handled the created exception!\n";
 +}
 +</code>
 +
 +<WRAP info 80% center round>
 +Στον παραπάνω κώδικα το //catch block//
 +<code cpp>
 +  } catch(...) {     // catch any exception not caught above!
 +    cout << "Got an exception of unknown type!\n";
 +  }
 +</code>
 +πιάνει όλους τους τύπους //exception// που δεν πιάστηκαν στα προηγούμενα //catch blocks//. Τοποθετώντας ένα //catch block// αυτής της μορφής είναι δυνατόν να εφαρμόσετε ένα τελικό έλεγχο για τύπους εξαιρέσεων που δεν έχετε προβλέψει ότι μπορούν να παραχθούν από τον κώδικα σας.
 +</WRAP>
 +
 +<WRAP tip 80% center round>
 +Στον παραπάνω κώδικα μπορείτε να παρατηρήσετε τα διαφορετικά μηνύματα που παράγονται ανάλογα με τον τύπο της εξαίρεσης. Παρατηρήστε, ότι αν και παράγεται ένα αντικείμενο τύπου //char//, το οποίο χωράει σε ένα //int// δεν γίνεται κάποια αυτόματη μετατροπή τύπου, ώστε το //catch block// που πιάνει τύπους //int// να πιάσει και αντικείμενα τύπου //char//.
 +</WRAP>
 +
 +<WRAP tip 80% center round>
 +Στον παραπάνω κώδικα παρατηρήστε ότι για τα αντικείμενα τύπου //std::string// και //MyException// διαχειριζόμαστε μία αναφορά στον παραγόμενο αντικείμενο και όχι το αντικείμενο το ίδιο. Ο λόγος είναι κατά τη διαχείριση, αντιγράφεται στο //catch block// μόνο η αναφορά (δείκτης προς το αντικείμενο) και όχι το σύνολο του αντικειμένου.
 +</WRAP>
 +
 +
  
cpp/exception.txt · Last modified: 2023/05/15 14:01 by gthanos