Προγραμματισμός Υπολογιστών με C++ ( 2012-13 ) 17η διάλεξη Ίων Ανδρουτσόπουλος http://www.aueb.gr/users/ion/ 1
Τι θα ακούσετε σήμερα Προσθήκη ελέγχου ορίων σε πίνακες χρησιμοποιώντας σχεδιότυπα τάξεων και εξαιρέσεις. Στις βιβλιοθήκες της C++11 υπάρχει παρόμοιο σχεδιότυπο array. Π.χ: array<int,6> a = { 1, 2, 3 ; Εξαιρέσεις κατά τη δυναμική καταχώριση μνήμης. Εισαγωγή στην υλοποίηση διπλά συνδεδεμένης λίστας. 2
Πίνακες με έλεγχο ορίων template <typename Type = int, int size = 2> class Array { Type arr[size]; public: Type& operator[](int i); ; Προεπιλεγμένος τύπος. template <typename Type, int size> Type& Array<Type, size>::operator[](int i) { if(i >= size) { cerr << "Illegal array index: " << i << endl; exit(1); return arr[i]; Θεωρείται σταθερά. Επιτρέπονται μόνο ακέραιοι, δείκτες και αναφορές. Προεπιλεγμένη τιμή: 2. Τερματισμός προγράμματος. int main() { Επιστρέφει 1 στο λειτουργικό. Array<string, 3> a; // Τρεις θέσεις για string. a[0] = "one"; a[1] = "two"; a[2] = "three"; Array<> b; // Δύο θέσεις για int (προεπιλογές). b[0] = 1; b[1] = 2; b[2] = 3; // Λάθος! Τερματίζεται το πρόγραμμα. cout << "test" << endl; // Δεν εκτελείται αυτή η γραμμή. 3
Χρήση εξαίρεσης Ι template <typename Type, int size> Type& Array<Type, size>::operator[](int i) { try { if(i >= size) { throw i; return arr[i]; catch(int j) { // Μπορεί να χειριστεί εξαιρέσεις int. cerr << "Ιllegal index: " << j << endl; exit(1); int main() { Array<string> strarr; // Δύο θέσεις για string. strarr[0] = "one"; strarr[1] = "two"; strarr[2] = "three"; // Τυπώνεται: «Illegal index: 2» cout << "test" << endl; // Δεν εκτελείται. 4
Χρήση εξαίρεσης ΙΙ template <typename Type, int size> Type& Array<Type, size>::operator[](int i) { if(i >= size) { throw i; return arr[i]; int main() { try { Array<string> strarr; // Δύο θέσεις για string. strarr[0] = "one"; strarr[1] = "two"; strarr[2] = "three"; // Προκαλεί throw. cout << "Never printed" << endl; catch(int j) { cerr << "Illegal index:" << j << endl; cout << "Program finished." << endl; Το throw τώρα δεν βρίσκεται μέσα σε try. Ψάχνει να βρει try που να περικλείει την κλήση της μεθόδου μέσα στην οποία βρίσκεται το throw. Η εκτέλεση του προγράμματος συνεχίζεται με τις εντολές της αντίστοιχης catch και κατόπιν με τις εντολές μετά την catch. 5
Χρήση εξαίρεσης ΙΙΙ int main() { try { Array<string> strarr; // Δύο θέσεις για string. strarr[0] = "one"; strarr[1] = "two"; strarr[2] = "three"; // Προκαλεί throw. cout << "Never printed" << endl; catch(string s) { Αν δεν βρεθεί catch ικανό να cerr << "Illegal string:" << s << endl; χειριστεί την εξαίρεση, το catch(int j) { πρόγραμμα cerr << "Illegal index:" << j << endl; τερματίζεται. cout << "Program finished." << endl; Χρησιμοποιείται η catch που ταιριάζει με τον τύπο της εξαίρεσης. Εδώ η catch για string δεν χρησιμοποιείται ποτέ, γιατί το throw της operator[] δημιουργεί εξαίρεση int. 6
Δημιουργία δικών μας τύπων εξαιρέσεων class Exception { // Δεν είναι απαραίτητο να λέγεται «Exception». public: string message; Exception(const string& messagein): message(messagein) { ; class UpperBoundaryException : public Exception { public: unsigned index; UpperBoundaryException(const string& messagein, unsigned indexin): Exception(messageIn), index(indexin) { ; Το όρισμα της throw μπορεί να είναι τιμή ή αντικείμενο οποιουδήποτε τύπου/τάξεως. Συχνά χρησιμοποιούμε αντικείμενα δικών μας τάξεων, που αντιστοιχούν η κάθε μία σε διαφορετική κατηγορία λαθών. 7
Δημιουργία δικών μας τύπων εξαιρέσεων template <typename Type, int size> Type& Array<Type, size>::operator[](int i) { if(i < 0) { throw Exception("Negative index in Array::operator[]."); else if(i >= size) { throw UpperBoundaryException( "Index too large in Array::operator[].", i); return arr[i]; 8
Χειρισμός εξαιρέσεων πολλών τύπων int main() { try { Array<string> strarr; strarr[0] = "one"; strarr[1] = "two"; strarr[2] = "three"; strarr[-1] = "negative"; cout << "Never printed" << endl; catch(upperboundaryexception e) { cerr << e.message << " Index:" << e.index << endl; catch(exception e) { cerr << e.message << endl; cout << "Program finished." << endl; Εδώ εμφανίζεται πρώτα εξαίρεση UpperBoundaryException, που τη χειρίζεται το πρώτο catch. Αν δεν υπήρχε το πρώτο catch, θα την χειριζόταν το δεύτερο, γιατί η UpperBoundaryException είναι υποτάξη της Exception. Αν η σειρά των catch ήταν αντίστροφη, θα χειριζόταν την εξαίρεση το catch(exception e). Αν δεν υπήρχε η εκχώρηση στο strarr[2], θα εμφανιζόταν εξαίρεση Exception στην εκχώρηση στο strarr[-1], που θα τη χειριζόταν το 9 δεύτερο catch.
Δήλωση δυνατών εξαιρέσεων προς τα έξω template <typename Type, int size> Type& Array<Type, size>::operator[](int i) throw(exception, UpperBoundaryException) { if(i < 0) { throw Exception("Negative index in Array::operator[]."); else if(i >= size) { throw UpperBoundaryException( "Index too large in Array::operator[].", i); return arr[i]; Δηλώνουμε ότι οι μόνες εξαιρέσεις που είναι δυνατόν να προκληθούν κατά την εκτέλεση της operator[] και που δεν θα μπορέσει να τις χειριστεί η ίδια η operator[] είναι εξαιρέσεις τύπων Exception και UpperBoundaryException. Στη C++11 μπορούμε να γράψουμε noexcept, για να δηλώσουμε ότι μια 10 συνάρτηση δεν μπορεί να χειριστεί καμία εξαίρεση.
Εξαιρέσεις δυναμικής καταχώρισης μνήμης #include <iostream> #include <new> using namespace std; int main() { unsigned long size; int* ptr; while(true) { cin >> size; try { ptr = new int[size]; cout << "Memory allocated." << endl; delete []ptr; cout << "Memory freed." << endl; catch(bad_alloc e) { cout << "Not enough memory. Try later." << endl; Αν η new δεν καταφέρει να δεσμεύσει την απαιτούμενη χωρητικότητα μνήμης, προκαλεί μια εξαίρεση τύπου bad_alloc. 11 (.< new > (Για να χρησιμοποιήσουμε αυτή τη δυνατότητα, απαιτείται #include
DLList list first last Διπλά συνδεδεμένη λίστα DLList::Node n1 value 10 next prev DLList::Node n2 value 30 next prev 0 DLList::Node n3 value 20 next prev 0 12
Παράδειγμα χρήσης της DLList #include "dllist.h"... int main(int argc, char* argv[]) { DLList list; try { list.putatback(30); list.putatback(40); list.putatfront(20); list.putatfront(10); catch (bad_alloc e) { cerr << argv[0] << ": Could not add some data to the list." << endl; Θα μπορούσαμε να έχουμε και μεθόδους για αφαίρεση στοιχείων από την αρχή ή το τέλος. Επίσης, να δοκιμάσουμε να αντιγράψουμε μια λίστα, να χρησιμοποιήσουμε τον τελεστή εκχώρησης κλπ. Καλές ασκήσεις... 13