User Tools

Site Tools


cpp:exception

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
cpp:exception [2019/05/06 07:13] – [Διαχείριση Εξαιρέσεων] gthanoscpp:exception [2019/05/06 07:17] – [Κληρονομικότητα εξαιρέσεων] gthanos
Line 1: Line 1:
-====== Διαχείριση Εξαιρέσεων ======+====== Δημιουργία & Διαχείριση Εξαιρέσεων ======
  
 Ας εξετάσουμε την κλάση **Vector** που είδαμε στην ενότητα της υπερφόρτωση τελεστών. Ο προσδιοριστής //nothrow// σε συνδυασμό με τον τελεστή **new** μας υποχρεώνει να ελέγξουμε την επιστρεφόμενη τιμή του τελεστή **new** για να δούμε έαν έχει αποτύχει η διαδικασία δέσμευσης μνήμης ή όχι και στην περίπτωση που έχουμε αποτυχία τερματίζουμε το πρόγραμμα.  Ας εξετάσουμε την κλάση **Vector** που είδαμε στην ενότητα της υπερφόρτωση τελεστών. Ο προσδιοριστής //nothrow// σε συνδυασμό με τον τελεστή **new** μας υποχρεώνει να ελέγξουμε την επιστρεφόμενη τιμή του τελεστή **new** για να δούμε έαν έχει αποτύχει η διαδικασία δέσμευσης μνήμης ή όχι και στην περίπτωση που έχουμε αποτυχία τερματίζουμε το πρόγραμμα. 
Line 164: Line 164:
         break;         break;
       case 4:       case 4:
-        throw string("C++"); //throw a string+        throw string("C++"); //throw a std::string
         break;         break;
       case 5:       case 5:
Line 170: Line 170:
         break;         break;
       default:       default:
-        c = -10; throw c;  // throw a character (default option)+        c = -10; throw c;  // throw a character
         break;         break;
     }     }
Line 204: Line 204:
 </WRAP> </WRAP>
  
-===== Κληρονομικότητα εξαιρέσεων ===== 
- 
-Ας υποθέσουμε ότι έχουμε τη σχέση κληρονομικότητας μεταξύ των κλάσεων **BaseException** και **DerivedException**, όπως παρακάτω: 
- 
-<code cpp BaseException.h> 
-using namespace std; 
- 
-class BaseException: public std::exception { 
-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, "BaseException, a: %d\n", a); 
-    return s; 
-  } 
-}; 
-</code> 
- 
-<code cpp DerivedException.h> 
-#include "BaseException.h" 
-using namespace std; 
- 
-class DerivedException: public BaseException { 
-  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, "DerivedException, a: %d, b: %d\n", a, b); 
-    return s; 
-  } 
-}; 
-</code> 
- 
-<code cpp ExceptionUse.cpp> 
-#include <iostream> 
-#include "DerivedException.h" 
-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; 
-} 
-</code> 
- 
-O παραπάνω κώδικας παράγει το παρακάτω //warning// κατά τη μεταγλώττιση: 
-<code> 
-ExceptionUse.cpp:22:5: warning: exception of type ‘DerivedException’ will be caught 
-   } catch(DerivedException &ex) { 
-     ^ 
-ExceptionUse.cpp:20:5: warning:    by earlier handler for ‘BaseException’ 
-   } catch(BaseException &ex) { 
-     ^ 
-</code> 
- 
-το οποίο εν συντομία λέει ότι ένα exception τύπου //DerivedException// θα "πιαστεί" από το 1ο //catch block// και το 2ο //catch block// δεν θα λειτουργήσει ποτέ. Το παραπάνω είναι λογικό διότι ένα αντικείμενο της κλάσης //DerivedException// είναι και //BaseException// με βάση τις αρχές της κληρονομικότητας. 
- 
-Εκτελέστε όμως τον παραπάνω κώδικα (παρά το warning) δίνοντας ορίσμα τους αριθμούς 1 και 2. Το αποτέλεσμα είναι το εξής: 
-<code> 
-gthanos@gthanos-DESKTOP:~/Downloads/C++$ ./ExceptionUse  
-Enter option (1-2): 1 
-BaseException, a: -2 
-gthanos@gthanos-DESKTOP:~/Downloads/C++$ ./ExceptionUse  
-Enter option (1-2): 2 
-BaseException, a: 4 
-</code> 
- 
-Παρατηρήστε ότι ενώ στην 2η περίπτωση παράγεται ένα //DerivedException// το αντικείμενο που τελικά λαμβάνουμε είναι τύπου //BaseException//. Εδώ θα πρέπει να τονίσουμε ότι η συνάρτηση //what()// είναι //virtual// πράγμα που σημαίνει ότι θα πρέπει να καλείται η κατάλληλη έκδοση της συνάρτησης με βάση τον τύπο του αντικειμένου για το οποίο καλείται, ανεξάρτητα από τον τύπο της η οποία δείχνει στο αντικείμενο (δες [[cpp:polymorphism|δυναμικό πολυμορφισμό]]). 
- 
-Η απάντηση στο παραπάνω ερώτημα είναι ότι αν και παράγεται ένα αντικείμενο τύπου //DerivedException// αυτό γίνεται //catch// από το πρώτο //catch block//. Μέσα στο //catch block// το αρχικό αντικείμενο αντιγράφεται σε ένα άλλο αντικείμενο τύπου //BaseException//, διότι έχουμε κλήση με τιμή στο //catch block//. Πρακτικά αυτό σημαίνει ότι από το αρχικό αντικείμενο κρατάμε οτιδήποτε ανήκει στην κλάση //BaseException// και απορρίπτουμε το υπόλοιπο. 
- 
-Ο τρόπος για να δουλέψει σωστά ο παραπάνω κώδικας είναι μέσα στο //catch block// να μην περάσουμε το αντικείμενο γιατί δημιουργείται αντίγραφο, αλλά να περάσουμε μία αναφορά σε αυτό, όπως παρακάτω: 
- 
-<code cpp ExceptionUse.cpp> 
-#include <iostream> 
-#include "DerivedException.h" 
-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; 
-} 
-</code> 
- 
-Πλέον το αποτέλεσμα της εκτέλεσης είναι το αναμενόμενο 
-<code> 
-gthanos@gthanos-DESKTOP:~/Downloads/C++$ ./ExceptionUse  
-Enter option (1-2): 1 
-BaseException, a: -2 
-gthanos@gthanos-DESKTOP:~/Downloads/C++$ ./ExceptionUse  
-Enter option (1-2): 2 
-DerivedException, a: 4, b: 5 
-</code> 
- 
-<WRAP tip 80% center round> 
-Το πιάσιμο μιας εξαίρεσης με χρήση αναφοράς για αντικείμενα σύνθετου τύπου (όχι char, int, long, double κλπ), διότι //α)// αποφεύγουμε την αντιγραφή του αντικειμένου μέσα στο //catch block// (πιο γρήγορος κώδικας) και //β)// αποφεύγουμε την "αποκοπή" μέρους του αντικειμένου της εξαίρεσης λόγω του χειρισμού της από ένα //catch block// βασικότερου τύπου από τον τύπο του αντικειμένου της εξαίρεσης. 
-</WRAP> 
- 
-<WRAP tip 80% center round> 
-Είναι προφανές ότι η σειρά των //catch blocks// θα έπρεπε να είναι η αντίστροφη (πρώτα το //catch block// για την αναφορά τύπου //DerivedException// και στη συνέχεια το //catch block// για την αναφορά τύπου //BasedException//. Ο λόγος είναι ότι εάν παραμείνει η σειρά των //catch blocks// ως έχει το 2o catch block δεν εκτελείται ποτέ. 
-</WRAP> 
  
 ===== Stack Unwinding ===== ===== Stack Unwinding =====
cpp/exception.txt · Last modified: 2023/05/15 14:01 by gthanos