1 ΗΓλώσσαΠρογραµµατισµού C++ (The C++ Programming Language) ηµήτριος Κατσαρός, Ph.D. Ελένη Τουσίδου, Ph.D. Χειµώνας 2006 ιάλεξη 9η Ιστοσελίδα του µαθήµατος 2 http://skyblue.csd.auth.gr/~dimitris/courses/cpp_fa ll06.htm Θα τοποθετούνται οι διαφάνειες του επόµενου µαθήµατος Επικοινωνία: dimitris@skyblue.csd.auth.gr etousido@uth.gr Περιεχόµενα 3 Πολυµορφισµός & Εικονικές συναρτήσεις (Polymorphism & Virtual functions) ιαχείριση εξαιρέσεων (Exception Handling) 1
Στόχοι εκµάθησης Τα βασικά των εικονικών συναρτήσεων Καθυστερηµένη σύνδεση (late binding) Υλοποίηση εικονικών συναρτήσεων Πότε χρησιµοποιούµε εικονικές συναρτήσεις Abstract κλάσεις και καθαρά εικονικές συναρτήσεις είκτες και εικονικές συναρτήσεις Συµβατότητα εκτεταµένων τύπων Downcasting και upcasting C++ κάτωαπότοκάλυµµα µε εικονικές συναρτήσεις 4 Βασικά των εικονικών συναρτήσεων 5 Πολυµορφισµός (Polymorphism) Συσχέτιση πολλών εννοιών µε µια ΜΙΑ συνάρτηση Οι εικονικές συναρτήσεις παρέχουν αυτή τη δυνατότητα Θεµελιώδης αρχή του αντικειµενοστραφούς προγραµµατισµού! Εικονικός ή Υπερβατικός (Virtual) Υπάρχει στην ουσία, αλλά όχι στην πράξη Εικονική ή υπερβατική συνάρτηση (virtual function) Μπορεί να χρησιµοποιηθεί πριν οριστεί Παράδειγµα µε Σχήµατα (1/2) 6 Ας το δούµε µέσα από ένα παράδειγµα: Κλάσεις για διάφορα είδη σχηµάτων Ορθογώνια, κύκλοι, ελλείψεις, κ.τ.λ. Κάθε σχήµα είναιένααντικείµενο διαφορετικής κλάσης εδοµένα ορθογωνίου: height, width, center point εδοµένα κύκλου: center point, radius Όλες παράγονται από µια βασική κλάση: Figure Απαιτούν τη συνάρτηση: draw() ιαφορετικές εντολές σχεδίασης για κάθε σχήµα 2
Παράδειγµα µε Σχήµατα (2/2) 7 Κάθε κλάση χρειάζεται διαφορετική συνάρτηση draw Μπορούµενατηνονοµάσουµε "draw" σε κάθε κλάση, έτσι: Rectangle r; Circle c; r.draw(); //Καλεί τη draw της κλάσης Rectangle c.draw(); // Καλεί τη draw της κλάσης Circle Μέχρι στιγµής δεν παρουσιάστηκε κάτι διαφορετικό από αυτά που γνωρίζουµε Παράδειγµα µε Σχήµατα: center() 8 Η γονική κλάση Figure περιέχει συναρτήσεις που εφαρµόζονται σε όλα τα figures; Θεωρήστε τη: center(): µετακινεί ένα figure στοκέντροτης οθόνης ιαγράφει το πρώτο, κατόπιν το ξανασχεδιάζει Έτσι η Figure::center() θα χρησιµοποιούσε τη συνάρτηση draw() για να ξανασχεδιάσει Επιπλοκές! Ποια συνάρτηση draw()? Ποιας κλάσης? 9 Παράδειγµα µε Σχήµατα: νέο Figure Θεωρήστε ότι ένα νέο είδος figure εµφανίζεται: Triangle class που παράγεται από την κλάση Figure Ησυνάρτησηcenter() κληρονοµείται από την κλάση Figure Θα δουλέψει για τα triangles? Χρησιµοποιεί τη draw(), η οποία είναι διαφορετική για κάθε figure! Εάν χρησιµοποιήσει τη Figure::draw() δεν θα δουλέψει για τα triangles Θέλουµε ηκληρονοµούµενη συνάρτηση center() να χρησιµοποιήσει τη συνάρτηση Triangle::draw() ΌΧΙ τη συνάρτηση Figure::draw() Αλλά η κλάση Triangle ΕΝ ΕΙΧΕ ΓΡΑΦΤΕΙ όταν γράφτηκε η Figure::center()! Η τελευταία δεν γνωρίζει triangles! 3
Παράδειγµα µε Σχήµατα: Virtual! 10 Ηαπάντησηείναιοιεικονικές συναρτήσεις Λένε στον compiler: εν γνωρίζω πώς υλοποιείται η συνάρτηση Περίµενε µέχρι να χρησιµοποιηθεί στο πρόγραµµα Τότε, πάρε την υλοποίηση από το αντικείµενο όπου ανήκει Αποκαλείται καθυστερηµένη σύνδεση (late binding) ή δυναµική σύνδεση (dynamic binding) Οι εικονικές συναρτήσεις υλοποιούν τον µηχανισµό τουlate binding Εικονικές συναρτήσεις: άλλο παράδειγµα 11 Πρόγραµµα record-keeping για κατάστηµα ανταλλακτικών αυτοκινήτων Κρατά τις πωλήσεις (sales) εν γνωρίζει ακόµα όλες τις πωλήσεις Πρώτα:µόνο τις τακτικές retail πωλήσεις Αργότερα: Discount sales, mail-order, κ.τ.λ. Εξαρτάται και από άλλους παράγοντες εκτός από την τιµή, φόρο 12 Εικονικές συναρτήσεις: ανταλλακτικά Το πρόγραµµαπρέπει: Να υπολογίζει τις καθηµερινές µεικτές πωλήσεις Υπολογίζει µεγαλύτερη/µικρότερη πώληση της ηµέρας Ίσως µέσηπώλησηανάµέρα Όλα προέρχονται από επιµέρους λογαριασµούς Αλλά πολλές συναρτήσεις για υπολογισµό λογαριασµών θα προστεθούν αργότερα! Όταν προστεθούν διαφορετικοί τύποι πωλήσεων! Έτσι, ησυνάρτησηπουθαυπολογίζειένα λογαριασµό θα είναι εικονική! 4
Ορισµός κλάσης Sale 13 class Sale public: Sale(); Sale( double theprice ); double getprice( void ) const; virtual double bill( void ) const; double savings( const Sale& other ) const; ; private: double price; Μέλη: savings και τελεστής < 14 double Sale::savings(const Sale& other) const return (bill() other.bill()); bool operator <(const Sale& first, const Sale& second) return (first.bill() < second.bill()); Παρατηρήστε ότι ΚΑΙ ΟΙ ΥΟ χρησιµοποιούν τη συνάρτηση-µέλος bill()! Κλάση Sale 15 Αναπαριστά πωλήσεις ενός αντικειµένου χωρίς επιπρόσθετες εκπτώσεις ή επιβαρύνσεις Παρατηρήστε τη δεσµευµένη λέξη "virtual" στη δήλωση της συνάρτησης-µέλος bill Επίδραση: αργότερα, οι παραγόµενες από τη Sale κλάσεις µπορούν να ορίσουν τις ΙΚΕΣ ΤΟΥΣ εκδόσεις της συνάρτησης bill Οι άλλες συναρτήσεις-µέλη της Sale θα χρησιµοποιήσουν εκείνη την έκδοση της bill, ανάλογα µετοαντικείµενο της παραγόµενης κλάσης! εν θα χρησιµοποιήσουν αυτόµατα την έκδοση της κλάσης Sale! 5
Ορισµός παραγόµενης κλάσης DiscountSale class DiscountSale : public Sale public: DiscountSale(); DiscountSale(double theprice,double thediscount); double getdiscount( void ) const; void setdiscount( double newdiscount ); double bill( void ) const; 16 ; private: double discount; Υλοποίηση της συνάρτησης bill() της κλάσης DiscountSale (1/2) 17 double DiscountSale::bill( void ) const double fraction = discount/100; return (1 fraction)*getprice(); Ο προσδιοριστής "virtual" δεν µπαίνει στην υλοποίηση της συνάρτησης Γίνεται αυτόµατα εικονική και στην παραγόµενη κλάση Ούτε η δήλωση (στο interface) απαιτείται να έχει τη λέξη "virtual" (αλλά συνήθως τη βάζουµε) Υλοποίηση της συνάρτησης bill() της κλάσης DiscountSale (2/2) 18 Εικονική συνάρτηση σε βασική κλάση: Γίνεται αυτόµατα εικονική στην παραγόµενη κλάση Στη δήλωση της παραγόµενης κλάσης (στο interface) εν χρειάζεται να υπάρχει η δεσµευµένη λέξη "virtual" Αλλά συνήθως τη βάζουµε, για να διευκολύνουµε την αναγνωσιµότητα 6
Η παραγόµενη κλάση DiscountSale 19 Στην κλάση DiscountSale, ησυνάρτηση-µέλος bill() υλοποιείται διαφορετικά από ότι στην κλάση Sale Εξειδικευµένα για "discounts" Οι συναρτήσεις-µέλη savings και "<" Θα χρησιµοποιήσουν αυτό τον ορισµό τηςbill() για όλατααντικείµενα της κλάσης DiscountSale! Αντί να χρησιµοποιήσουν την εξ ορισµού έκδοση που υπάρχει στην κλάση Sales! Virtual: Ισχυρός µηχανισµός! 20 Θυµηθείτε ότι η κλάση Sale γράφτηκε πολύ πριν την παραγόµενη κλάση DiscountSale Οι συναρτήσεις-µέλη savings και "<" µεταγλωττίστηκαν πριν ακόµα συλλάβουµετηνιδέα της κλάσης DiscountSale Πάραυτα, µια κλήση όπως η: DiscountSale d1, d2; d1.savings(d2); Στην savings() ηκλήσηστηνbill() γνωρίζει ότι πρέπει να χρησιµοποιήσει τον ορισµό τηςbill() που υπάρχει στην κλάση DiscountSale Ισχύς! Virtual: Πώς? 21 Η συγγραφή C++ προγραµµάτων: Υποθέτουµε ότι γίνεται µε µαγικό τρόπο! Αλλά η εξήγηση αφορά την καθυστερηµένη σύνδεση (late binding) Οι εικονικές συναρτήσεις υλοποιούν τον µηχανισµό του late binding Λένε στον compiler να περιµένει µέχρι η συνάρτηση να χρησιµοποιηθεί στο πρόγραµµα Αποφασίζει ποιον ορισµό ναχρησιµοποιήσει µεβάση το καλούν αντικείµενο Πολύ σηµαντική αρχή του OOP! 7
Υπέρβαση (overriding) 22 Οορισµός µιας εικονικής συνάρτησης αλλάζει σε µια παραγόµενη κλάση Λέµε ότι έχει γίνει υπέρβασή της Παρόµοιο µε τονεπαν-ορισµό Θυµηθείτε: για τις τυπικές συναρτήσεις Έτσι: Αλλαγή εικονικών συναρτήσεων: overridden Αλλαγή µη-εικονικών συναρτήσεων: redefined 23 Εικονικές συναρτήσεις:γιατί όχι όλες? Καθαρή υπεροχή των εικονικών συναρτήσεων, όπως έχουµε δει Ένα, αλλά σηµαντικό, µειονέκτηµα: overhead! Χρησιµοποιεί περισσότερο αποθηκευτικό χώρο Late binding είναι µια διαδικασία "on the fly", έτσι το πρόγραµµα εκτελείταιπιοαργά Έτσι, εάν οι εικονικές συναρτήσεις δεν χρειάζονται πραγµατικά, δενθαπρέπειναχρησιµοποιούνται Καθαρά εικονικές συναρτήσεις (pure virtual functions) 24 Στηβασικήκλάση, ίσως να µην έχει νόηµα να υπάρχει ορισµός για κάποιες ή όλες από τις συναρτήσεις-µέλη της! Ο σκοπός της είναι απλά για να παράγονται άλλες κλάσεις από αυτή Θυµηθείτε την κλάση Figure Όλα τα figures είναι αντικείµενα παραγόµενων κλάσεων Rectangles, circles, triangles, κ.τ.λ. ΗκλάσηFigure δεν γνωρίζει πώς να σχεδιάσει (draw)! Την κάνουµε pure virtual function: virtual void draw() = 0; 8
Abstract βασικές κλάσεις 25 Οι pure virtual functions δεν χρειάζονται ορισµό Εξαναγκάζονται όλες οι παραγόµενες κλάσεις να ορίσουν τις δικές τους εκδόσεις των συναρτήσεων Μια κλάση που έχει µια ή περισσότερες pure virtual functions είναι µια: abstract base class Μπορεί να χρησιµοποιηθεί µόνο ως βασική κλάση εν µπορούν να δηµιουργηθούν αντικείµενα από αυτή Αφού δεν έχει πλήρεις ορισµούς όλων των µελών της! Εάν µια παραγόµενη κλάση δεν µπορέσει να ορίσει όλα τα pure που κληρονόµησε, τότε: Είναι και αυτή µια abstract base class Συµβατότητα εκτεταµένων τύπων 26 Έστω ότι: ηκλάσηderived παράγεται από τη Base Τα αντικείµενα Derived µπορούν να ανατεθούν σε αντικείµενα τύπου Base Αλλά ΌΧΙ ανάποδα! Θεωρήστε το προηγούµενο παράδειγµα: Ένα DiscountSale Είναι ένα Sale, αλλά το αντίστροφο δεν είναι αληθές 27 Παράδ. Συµβατότητα εκτεταµένων τύπων class Pet public: string name; virtual void print() const; ; class Dog : public Pet public: string breed; virtual void print() const; ; 9
Κλάσεις Pet και Dog 28 Έστωσαν τώρα οι δηλώσεις: Dog vdog; Pet vpet; Παρατηρήστε ότι οι µεταβλητές-µέλη name και breed είναι public Για χάρην του παραδείγµατος Χρήση των κλάσεων Pet και Dog 29 Ο,τιδήποτε είναι dog Eίναι ένα pet: vdog.name = "Tiny"; vdog.breed = "Great Dane"; vpet = vdog; Αυτές οι αναθέσεις είναι επιτρεπτές Μπορούµενααναθέσουµε τιµές σε τύπογονέα, αλλά όχι το αντίστροφο Ένα pet εν είναι ένα dog (όχι αναγκαστικά) Το πρόβληµα Slicing 30 Παρατηρήστε ότι η τιµή που ανατίθεται στο vpet χάνει το πεδίο της breed! cout << vpet.breed; Παράγει ERROR µήνυµα! Αποκαλείται Πρόβληµα Slicing Ίσως φαίνεται αποδεκτό Το dog µετακινήθηκε στη µεταβλητή Pet, και συνεπώς θα έπρεπε να αντιµετωπίζεται ως ένα Pet Και εποµένως δεν θα πρέπει να έχει τις ιδιότητες ενός "dog" Προκαλεί ενδιαφέρουσα φιλοσοφική συζήτηση 10
ιορθώνοντας το Πρόβληµα Slicing 31 Στη C++, το πρόβληµα slicing είναι µπελάς Θα θέλαµενααναφερόµαστε στο πεδίο breed, παρόλο που αντιµετωπίζεται ως ένα Pet Μπορούµε νατοκάνουµε αυτόµε τους δείκτες σε δυναµικές µεταβλητές Παράδειγµαπροβλήµατος Slicing 32 Pet *ppet; Dog *pdog; pdog = new Dog; pdog->name = "Tiny"; pdog->breed = "Great Dane"; ppet = pdog; εν µπορούµεναπροσπελάσουµε τοπεδίο breed του αντικειµένου που δείχνεται από τον δείκτη ppet: cout << ppet->breed; //ILLEGAL! Παράδειγµαπροβλήµατος Slicing 33 Πρέπει να χρησιµοποιήσουµε την εικονική συνάρτηση-µέλος: ppet->print(); Καλεί την συνάρτηση-µέλος print της κλάσης Dog! Επειδή είναι virtual Η C++ περιµένει ναδεισετιείδους αντικείµενο δείχνει ο δείκτης ppet πριν κάνει τη σύνδεση µε µια συγκεκριµένη κλήση συνάρτησης 11
Εικονικοί Destructors 34 Θυµηθείτε: οι destructors πρέπει να αποδεσµεύσουν τη δυναµικά δεσµευµένη µνήµη Θεωρήστε τη δήλωση: Base *pbase = new Derived; delete pbase; Θα καλέσει τον destructor της βασικής κλάσης παρόλο που δείχνει σε αντικείµενο κλάσης Derived! Εάν κάνουµετονdestructor virtual τότε λύνουµετο πρόβληµα! Καλή πολιτική για όλους τους destructors να είναι εικονικοί Προσαρµογή (casting) 35 Θεωρήστε το: Pet vpet; Dog vdog; vdog = static_cast<dog>(vpet); //ILLEGAL! εν µπορούµενακάνουµε cast ένα pet ώστε να γίνει ένα dog, αλλά: vpet = vdog; // Legal! vpet = static_cast<pet>(vdog); //Also legal! Το upcasting είναι OK Από τύπο-απογόνου σε τύπο-προγόνου Downcasting 36 Το downcasting είναι επικίνδυνο! Κάνοντας casting απότύποπρογόνουσετύποαπογόνου Υποθέτει ότι προστίθεται πληροφορία Μπορεί να γίνει µε dynamic_cast: Pet *ppet; ppet = new Dog; Dog *pdog = dynamic_cast<dog*>(ppet); Έγκυρο, αλλά επικίνδυνο! Σπάνια εφαρµόζουµε downcasting εξαιτίας των παγίδων Πρέπει να προσέχουµε την πληροφορία που προστίθεται Όλες οι συναρτήσεις-µέλη πρέπει να είναι εικονικές 12
Εσωτερική λειτουργία των Εικονικών Συναρτήσεων Χρειάζεται απλώς να ξέρουµε πώςνατις χρησιµοποιήσουµε! Αρχή της Απόκρυψης Πληροφορίας Πίνακας Εικονικών Συναρτήσεων (Virtual function table) Τον δηµιουργεί ο compiler Έχει δείκτες για κάθε εικονική συνάρτηση-µέλος είχνει στη θέση του σωστού κώδικα για τη συνάρτηση αυτή Τα αντικείµενα αυτών των κλάσεων έχουν επίσης ένα δείκτη είχνει στον Πίνακα Εικονικών Συναρτήσεων 37 Περίληψη 1 38 Ηκαθυστερηµένη σύνδεση αναβάλλει την απόφαση για το ποια συνάρτηση-µέλος θα κληθεί µέχρι την εκτέλεση του προγράµµατος Στη C++, οι εικονικές συναρτήσεις χρησιµοποιούν καθυστερηµένη σύνδεση Οι καθαρά εικονικές συναρτήσεις δεν έχουν ορισµό Οι κλάσεις µε µια τουλάχιστον εικονική συνάρτηση είναι/λέγονται abstract εν µπορούµε ναδηµιουργήσουµεαντικείµενα µιας abstract κλάσης Χρησιµοποιούνται µόνο ως βασική κλάση για να παραχθούν άλλες Περίληψη 2 39 Αντικείµενα µιας παραγόµενες κλάσης µπορούν να ανατεθούν σε αντικείµενα µιας βασικήςτουςκλάσης Τα χαρακτηριστικά της παραγόµενης κλάσης χάνονται; Πρόβληµα slicing Ανάθεση δεικτών και δυναµικά αντικείµενα Επιτρέπουν διόρθωση του προβλήµατος slicing Κάνουµε όλους τους destructors εικονικούς Καλή προγραµµατιστική τακτική Εγγυάται σωστή αποδέσµευση της µνήµης 13
Περιεχόµενα 40 Πολυµορφισµός & Εικονικές συναρτήσεις (Polymorphism & Virtual functions) ιαχείριση εξαιρέσεων (Exception Handling) Στόχοι εκµάθησης 41 Τα βασικά της ιαχείρισης Εξαιρέσεων (exception handling) Ορίζοντας κλάσεις εξαιρέσεων Πολλαπλά throws και catches Καθορισµοί για exception Τεχνικές προγραµµατισµού για ιαχείριση Εξαιρέσεων Πότε να στέλνουµε exceptions Ιεραρχίες κλάσεων Exceptions Εισαγωγικά 42 Τυπική προσέγγιση στην ανάπτυξη: Γράφουµεπρογράµµαταυποθέτονταςότιταπράγµατα θα πάνε όπως τα σχεδιάσαµε Φτιάχνουµε τον πυρήνα της λειτουργικότητας Κατόπιν, λαµβάνουµε µέριµνα για εξαιρετικές περιπτώσεις Λειτουργίες exception-handling στη C++ Χειρισµός εξαιρετικών καταστάσεων Μηχανισµός σηµατοδοτεί ασυνήθη κατάσταση Άλλο µέρος του κώδικα χειρίζεται την exception 14
Τα βασικά του exception-handling 43 Υπονοείται ότι πρέπει να χρησιµοποιούνται σπάνια Σε ειδικές περιπτώσεις ύσκολα διδάσκονται τόσο µεγάλα παραδείγµατα Προσέγγιση: Απλά παιδικά παραδείγµατα, στα οποία φυσιολογικά δεν θα χρησιµοποιούνταν exception-handling Να έχουµε στοµυαλό µας τη µεγάλη εικόνα Απλό παράδειγµα 44 Φανταστείτε: σπάνια ξεµένουµε από γάλα: cout << "Enter number of donuts:"; cin >> donuts; cout << "Enter number of glasses of milk:"; cin >> milk dpg = donuts/static_cast<double>(milk); cout << donuts << "donuts.\n"; << milk << "glasses of milk.\n"; << "You have " << dpg << "donuts for each glass of milk.\n"; Ο βασικός κώδικας υποθέτει ότι δεν θα ξεµείνουµε από γάλα Απλό παράδειγµα if-else 45 Παρατηρήστε: Εάν τελειώσει το γάλα διαίρεση µε τοµηδέν! Το πρόγραµµαθαπρέπειναλαµβάνει µέριµνα για την περίπτωση που ξεµείνουµε απόγάλα Μπορούµεαπλάναχρησιµοποιήσουµε τηδοµή if-else: if (milk <= 0) cout << "Go buy some milk!\n"; else Παρατηρήστε: όχι exception-handling εδώ 15
Παράδειγµα Exception Handling 46 Συζήτηση του παραδείγµατος 47 Κώδικας µεταξύ των δεσµευµένων λέξεων try και catch Ίδιος κώδικας όπως και στην κανονική έκδοση, εκτός απότοότιηδήλωσηif είναι απλούστερη: if( milk <= 0 ) throw donuts; Καθαρότερος κώδικας Εάν "no milk" κάνε κάτι κατ εξαίρεση Αυτό το κάτι κατ εξαίρεση παρέχεται µετά από τη δεσµευµένη λέξη catch Συζήτηση του παραδείγµατος 48 Το µπλοκ try Χειρίζεται την κανονική κατάσταση Το µπλοκ catch Χειρίζεται την κατ εξαίρεση κατάσταση Παρέχει διαχωρισµό του κανονικού από τον κατ εξαίρεση κώδικα Βέβαια για το απλό αυτό παράδειγµα δεν είναι κάτι σπουδαίο, αλλά στη γενική περίπτωση είναι σηµαντικός µηχανισµός 16
Το µπλοκ try 49 Ηβασικήµέθοδος της διαχείρισης εξαιρέσεων είναι το try-throw-catch Το µπλοκ try: try Some_Code; Περιέχει τον κώδικα για τον βασικό αλγόριθµο, όταν όλα πηγαίνουν καλά throw 50 Μέσα στο µπλοκ try, όταν συµβεί κάτι ασυνήθιστο: try Code_To_Try if (exceptional_happened) throw donuts; More_Code Ηδεσµευµένη λέξη throw ακολουθείται από τον τύπο της εξαίρεσης Αποκαλείται "throwing an exception" Το µπλοκ catch 51 Όταν κάτι is thrown πηγαίνει κάπου Στη C++, η ροή του ελέγχου πηγαίνει από το try-block στο catch-block Έξοδος από το try-block και ο έλεγχος µεταφέρεται στο catch-block Η εκτέλεση του catch block αποκαλείται called "catching the exception" Η διαχείριση των εξαιρέσεωνς πρέπει να γίνεται σε κάποιο catch block 17
Περισσότερα για το catch-block 52 Θυµηθείτε: catch( int e ) cout << e << " donuts, and no milk!\n"; << " Go buy some milk.\n"; Μοιάζει µεορισµό συνάρτησηςµε παράµετρο int! εν είναι συνάρτηση, αλλά λειτουργεί παρόµοια Το throw είναι όπως η κλήση συνάρτησης Ηπαράµετρος στο catch-block 53 Θυµηθείτε: catch(int e) Το "e" αποκαλείται παράµετρος του catch-block Κάθε catch block µπορείναέχειτοπολυµια παράµετρο Κάνει δυο πράγµατα: 1. Το όνοµα του τύπου καθορίζει τo είδος που µπορεί να πιάσει το catch-block 2. Παρέχει ένα όνοµα γιατηντιµήπου συλλαµβάνειται, ώστε να µπορούµενακάνουµε διάφορα πράγµατα µετηντιµήαυτή Ορίζοντας κλάσεις από exceptions 54 Ηδήλωσηthrow µπορεί να πετάξει τιµές οποιουδήποτε τύπου Κλάση exception Περιέχει αντικείµενα µεπληροφορίαπουis thrown Μπορεί να έχει διαφορετικούς τύπους αναγνωρίζοντας κάθε πιθανή κατάσταση εξαίρεσης Παρόλ αυτά είναι µια κλάση Ονοµάζεται "exception class" εξαιτίας του πώς χρησιµοποιείται 18
ΠαράδειγµακλάσηςException 55 Θεωρήστε το: class NoMilk public: NoMilk() NoMilk( int howmany ) : count( howmany ) int getcount() const return count; private: int count; ; throw NoMilk(donuts); Ενεργοποεί τον constructor της κλάσης of NoMilk Πολλαπλά throws και catches 56 Το try-block τυπικά µπορεί να πετάξει οποιοδήποτε αριθµό exceptions, διαφορετικών τύπων Φυσικά, πετάει µόνο µια κάθε φορά Αφού η δήλωση throw τερµατίζει το try-block Αλλά διαφορετικοί τύποι µπορεί να πεταχτούν Κάθε catch block πιάνει µόνο έναν τύπο Είναι σύνηθες να τοποθετούµε πολλά catch-blocks µετά από κάθε try-block Για να πιάσουµε όλεςτιςπιθανέςexceptions που µπορεί να πεταχτούν Catching 57 Ησειράτωνcatch blocks είναι σηµαντική Τα catch-blocks δοκιµάζονται µετησειρά εµφάνισής τους µετά το try-block Το πρώτο ταίριασµα είναι αυτό που χειρίζεται την εξαίρεση! Θεωρήστε το: catch ( ) Αποκαλείται "catch-all", "default" exception handler Πιάνει οποιαδήποτε εξαίρεση Βεβαιωθείτε ότι το catch-all τοποθετείται ΜΕΤΑ από τις πιο ειδικές exceptions! Ή οι άλλες δεν θα πιαστούν ποτέ! 19
Τετριµµένη Exception κλάση 58 Θεωρήστε το: class DivideByZero εν έχει µεταβλητές-µέλη εν έχει συναρτήσεις-µέλη (εκτός από τον default constructor) Τίποτε άλλο, εκτός από το όνοµάτης Όπου δεν χρειάζεται να κάνουµεκάτιµετηντιµήτης εξαίρεσης Χρησιµοποιείται απλά για να πιάνεται στο catch block Μπορούµε να παραλείπουµετηνπαράµετρο του catch block 59 Throwing exception από συνάρτηση Μια συνάρτηση µπορεί να πετάξει µια exception Οι καλούντες µπορεί να έχουν διαφορετικές αντιδράσεις Μερικές µπορεί να επιθυµούν τερµατισµό του προγράµµατος Μερικές µπορεί να συνεχίζουν, ή να κάνουν κάτι άλλο Είναι λογικό να πιάνουµε µια exception στην καλούσα συνάρτηση Τοποθετούµετονκώδικαµέσα σε try-block Χειριζόµαστε την exception στο catch-block µετά το try-block 60 Παράδειγ. exception από συνάρτηση Θεωρήστε το: try quotient = safedivide(num, den); catch (DivideByZero) ΗσυνάρτησηsafeDivide() πετάει µια exception DividebyZero Την οποία χειρίζεται το catch-block της καλούσας συνάρτησης 20
Καθορισµός των exception 61 Οι συναρτήσεις που δεν πιάνουν exceptions Θα πρέπει να προειδοποιούν τους χρήστες τους ότι θα µπορούσαν να πετάξουν µια exception Αλλάδεντηνπιάνουνοιίδιες! Θα πρέπει να απαριθµούµε αυτέςτιςexceptions: double safedivide(int top, int bottom) throw (DividebyZero); Αποκαλείται "exception specification" ή "throw list" Θα πρέπει να εµφανίζεται στη δήλωση και στον ορισµό Εάν δεν υπάρχει throw list όλοι οι τύποι Λίστα των throw 62 Εάν µια συνάρτηση πετάξει µια exception την οποία δεν καθορίζει στη λίστα των throws: εν υπάρχει λάθος (compile ή run-time) Καλείται αυτόµατα η συνάρτηση unexpected() Εξ ορισµού συµπεριφορά είναι το τερµατισµός Μπορούµενατροποποιήσουµετησυµπεριφορά Το ίδιο αποτέλεσµα εάνδενβρεθείcatchblock Περίληψητηςλίσταςτωνthrow 63 void somefunction() throw(dividebyzero, OtherException); //Exception types DividebyZero or OtherException //treated normally. All others invoke unexpected() void somefunction() throw (); //Empty exception list, all exceptions invoke //unexpected() void somefunction(); //All exceptions of all types treated normally 21
Παραγόµενες κλάσεις 64 Θυµηθείτε: τα αντικείµενα µιας παραγόµενης κλάσης είναι επίσης αντικείµενα της βασικής κλάσης Θεωρήστε το: ηκλάσηd παράγεται από την κλάση B Εάν η B είναι στη λίστα των throws Αντικείµενα της κλάσης D που πετάγονται ως exceptions, θα αντιµετωπίζονται κανονικά, αφού είναι επίσης αντικείµενα της κλάσης B Σηµειώστε: δεν γίνεται αυτόµατο type casting: Το να πεταχτεί double δεν είναι το ίδιο µετονα πεταχτεί ένας int Ησυνάρτησηunexpected() 65 Εξ ορισµού ενέργεια: τερµατισµός του προγράµµατος εν χρειάζονται ειδικά includes ή directives using Συνήθως δεν χρειάζεται να την ξαναορίσουµε Αλλά, έχουµεαυτήτηδυνατότητα: Χρησιµοποιούµετηset_unexpected() Οι λεπτοµέρειες είναι πέρα από το αντικείµενο αυτού του µαθήµατος Πότε πετάµε exceptions 66 Σύνηθες να διαχωρίζουµε throws και catches Σε διαφορετικές συναρτήσεις Throwing συνάρτηση: Απαριθµήστε τις exceptions στην λίστα των throws Στηδήλωσηκαιστονορισµό Catching συνάρτηση: ιαφορετική συνάρτηση, ίσως σε διαφορετικό αρχείο 22
Προτιµώµενη throw-catch: throw 67 void functiona() throw (MyException) throw MyException(arg); Η συνάρτηση πετάει exception όπως δήλωσε Προτιµώµενη throw-catch: catch Τότε κάποια άλλη συνάρτηση: void functionb( void ) try functiona(); catch( MyException e ) // Handle exception 68 Exceptions που δεν πιάστηκαν 69 Πρέπει να πιάνουµε κάθεexception Εάν όχι το πρόγραµµατερµατίζεται Καλείται η συνάρτηση terminate() Θυµηθείτε για τις συναρτήσεις Εάν η exception δεν υπάρχει στην λίστα των throw: καλείται η unexpected() Με τη σειρά της καλεί την terminate() Έτσι, έχουµε τοίδιοαποτέλεσµα 23
Κατάχρηση των exceptions 70 Οι exceptions αλλάζουν τη ροή του ελέγχου Χάνεται οέλεγχοςτηςροής Θα πρέπει να χρησιµοποιούνται µε µέτρο Καλός κανόνας: Εάν επιθυµούµε µια "throw": εξετάστε το ενδεχόµενο να γράψετε τον κώδικα χωρίς αυτή Εάν υπάρχει εναλλακτική ακολουθήστε τη Ιεραρχίες κλάσεων exceptions 71 Είναι χρήσιµες; Θεωρήστε το: ΗκλάσηDivideByZero παράγεται από τη: κλάση exception ArithmeticError Όλα τα catch-blocks για την ArithmeticError θα πιάσουν και την DivideByZero Εάν η ArithmeticError είναι δηλωµένη στην λίστα των throws, τότε η DividebyZero θα εξεταστεί εκεί Έλεγχος διαθέσιµης µνήµης Ο τελεστής new πετάει exception bad_alloc εάν δεν υπάρχει αρκετή µνήµη: try NodePtr pointer = new Node; catch (bad_alloc) cout << "Ran out of memory!"; // Can do other things here as well Στη βιβλιοθήκη <new>, και namespace std 72 24
Ξαναπετώντας µια exception 73 Είναιέγκυροναπετάειοκώδικαςµια exception ΜΕΣΑ σε ένα catch-block! Μόνο σε σπάνιες περιπτώσεις Την πετάει σε κάποιο catch-block υψηλότερα στην αλυσίδα εκτέλεσης Μπορούµε να ξαναπετάξουµε την ίδια ή νέα exception rethrow; Throws same exception again throw newexceptionup; Throws new exception to next catch-block 25