Περιεχόμενα Εισαγωγή 25 1 Η γενική εικόνα 33 Γιατί χρειαζόμαστε τον αντικειμενοστρεφή προγραμματισμό;...34 Διαδικασιακές γλώσσες...34 Η αντικειμενοστρεφής προσέγγιση...37 Χαρακτηριστικά των αντικειμενοστρεφών γλωσσών...40 Αντικείμενα...40 Κλάσεις...42 Κληρονομικότητα...42 Ανακύκλωση...45 Δημιουργία νέων τύπων δεδομένων...45 Πολυμορφισμός και υπερφόρτωση...45 C++ και C...46 Βάλτε τις βάσεις...47 Η Ενοποιημένη Γλώσσα Μοντελοποίησης...47 Σύνοψη...49 Ερωτήσεις...49 2 Βασικές αρχές προγραμματισμού σε C++ 53 Ξεκίνημα...54 Κατασκευή βασικού προγράμματος...54 Συναρτήσεις...55 Εντολές προγράμματος...56 Λευκά διαστήματα...57 Έξοδος με το cout...57 Αλφαριθμητικές σταθερές...58 Οδηγίες...59 Οδηγίες προεπεξεργαστή...59 Αρχεία-κεφαλίδες...59 Η οδηγία using...60 Σχόλια...60 Σύνταξη σχολίων...60 Πότε να χρησιμοποιείτε σχόλια...61 Εναλλακτική σύνταξη σχολίων...61 Ακέραιες μεταβλητές...62 Ορισμός ακέραιων μεταβλητών...62 Δηλώσεις και ορισμοί...64 Ονόματα μεταβλητών...64 Εντολές απόδοσης τιμής...64 Ακέραιες σταθερές...65 Παραλλαγές εξόδου...65 Ο χειριστής endl...65 Άλλοι ακέραιοι τύποι...66
Περιεχόμενα 5 Μεταβλητές χαρακτήρων...66 Σταθερές χαρακτήρων...67 Απόδοση αρχικών τιμών (initialization)...68 Ακολουθίες διαφυγής...68 Είσοδος με το cin...69 Μεταβλητές που ορίζονται στο σημείο χρήσης τους...71 Στοίβαξη τελεστών <<...71 Παραστάσεις...71 Προτεραιότητα...71 Τύποι κινητής υποδιαστολής...72 Ο τύπος bool...75 Ο χειριστής setw...76 Σύνοψη για τους τύπους μεταβλητών...78 Τύποι δεδομένων unsigned...79 Μετατροπή τύπου...80 Αυτόματες μετατροπές...81 Ρητές μετατροπές...82 Αριθμητικοί τελεστές...84 Ο τελεστής υπολοίπου...85 Αριθμητικοί τελεστές απόδοσης τιμής...85 Τελεστές προσαύξησης...87 Συναρτήσεις βιβλιοθήκης...89 Αρχεία-κεφαλίδες...90 Αρχεία βιβλιοθήκης...90 Αρχεία-κεφαλίδες και αρχεία βιβλιοθήκης...91 Δύο τρόποι χρήσης της οδηγίας #include...91 Σύνοψη...92 Ερωτήσεις...93 Ασκήσεις...95 3 Βρόχοι και αποφάσεις 99 Σχεσιακοί τελεστές...100 Βρόχοι...102 Ο βρόχος for...102 Βήματα κατά την αποσφαλμάτωση...108 Παραλλαγές του βρόχου for...108 Ο βρόχος while...110 Προτεραιότητα: Αριθμητικοί και σχεσιακοί τελεστές...113 Ο βρόχος do...115 Πότε να χρησιμοποιείτε τον κάθε βρόχο...117 Αποφάσεις...117 Η εντολή if...118 Η εντολή if...else...122 Η κατασκευή else...if...130 Η εντολή switch...131 Η εντολή break...133 Ο τελεστής συνθήκης...135
6 Αντικειμενοστρεφής προγραμματισμός με τη C++ Λογικοί τελεστές...138 Ο λογικός τελεστής AND...139 Ο λογικός τελεστής OR...140 Ο λογικός τελεστής NOT...141 Σύνοψη σχετικά με την προτεραιότητα...142 Άλλες εντολές ελέγχου...142 Η εντολή break...143 Η εντολή continue...145 Η εντολή goto...147 Σύνοψη...147 Ερωτήσεις...148 Ασκήσεις...150 4 Δομές 155 Δομές...156 Μία απλή δομή...156 Ορισμός της δομής...157 Ορισμός μεταβλητής δομής...158 Προσπέλαση των μελών μιας δομής...160 Άλλα χαρακτηριστικά των δομών...161 Απόδοση αρχικής τιμής σε μέλη δομής...162 Ένα παράδειγμα μέτρησης...163 Δομές μέσα σε δομές...165 Παράδειγμα με ένα παιχνίδι τράπουλας...169 Δομές και κλάσεις...172 Απαριθμήσεις...172 Ημέρες της εβδομάδας...172 Ή το ένα ή το άλλο...175 Οργάνωση των χαρτιών...177 Καθορισμός ακέραιων τιμών...179 Ο τύπος enum δεν είναι τέλειος...179 Άλλα παραδείγματα...179 Σύνοψη...180 Ερωτήσεις...180 Ασκήσεις...182 5 Συναρτήσεις 185 Απλές συναρτήσεις...186 Δήλωση της συνάρτησης...188 Η κλήση της συνάρτησης...188 Ο ορισμός της συνάρτησης...188 Σύγκριση με τις συναρτήσεις βιβλιοθήκης...190 Απαλοιφή της δήλωσης...190 Μεταβίβαση ορισμάτων σε συναρτήσεις...191 Μεταβίβαση σταθερών...191 Μεταβίβαση μεταβλητών...193 Μεταβίβαση κατ αξία...194
Περιεχόμενα 7 Οι δομές ως ορίσματα...195 Ονόματα στη δήλωση...200 Επιστροφή τιμών από συναρτήσεις...200 Η εντολή return...201 Επιστροφή μεταβλητών δομής...204 Ορίσματα αναφοράς...206 Μεταβίβαση απλών τύπων δεδομένων κατ' αναφορά...206 Μια πιο πολύπλοκη μεταβίβαση κατ' αναφορά...209 Μεταβίβαση δομών κατ' αναφορά...210 Σημειώσεις για τη μεταβίβαση κατ' αναφορά...212 Υπερφόρτωση συναρτήσεων...212 Διαφορετικό πλήθος ορισμάτων...213 Διαφορετικά είδη ορισμάτων...215 Αναδρομή...217 Εμβόλιμες συναρτήσεις...219 Προεπιλεγμένα ορίσματα...221 Εμβέλεια και κλάση αποθήκευσης...223 Τοπικές μεταβλητές...223 Κλάση αποθήκευσης...224 Καθολικές μεταβλητές...226 Στατικές τοπικές μεταβλητές...228 Αποθήκευση...229 Επιστροφή κατ' αναφορά...230 Κλήσεις συναρτήσεων στην αριστερή πλευρά του συμβόλου ίσον...231 Μην ανησυχείτε ακόμη...231 Ορίσματα const σε συναρτήσεις...232 Σύνοψη...233 Ερωτήσεις...234 Ασκήσεις...236 6 Αντικείμενα και κλάσεις 239 Μια απλή κλάση...240 Κλάσεις και αντικείμενα...241 Ορισμός της κλάσης...242 Χρήση της κλάσης...245 Κλήση συναρτήσεων-μελών...245 Αντικείμενα της C++ ως φυσικά αντικείμενα...247 Τα ανταλλακτικά συσκευών ως αντικείμενα...247 Κύκλοι ως αντικείμενα...248 Αντικείμενα της C++ ως τύποι δεδομένων...250 Συναρτήσεις δόμησης...251 Παράδειγμα μετρητή...252 Ένα παράδειγμα γραφικών...255 Συναρτήσεις αποδόμησης...256
8 Αντικειμενοστρεφής προγραμματισμός με τη C++ Αντικείμενα ως ορίσματα συναρτήσεων...257 Υπερφόρτωση συναρτήσεων δόμησης...258 Συναρτήσεις-μέλη που ορίζονται έξω από την κλάση...260 Αντικείμενα ως ορίσματα...261 Η προεπιλεγμένη συνάρτηση δόμησης αντιγράφου...262 Επιστροφή αντικειμένων από συναρτήσεις...264 Ορίσματα και αντικείμενα...265 Παράδειγμα με παιχνίδι της τράπουλας...267 Δομές και κλάσεις...271 Κλάσεις, αντικείμενα, και μνήμη...271 Στατικά δεδομένα κλάσης...273 Χρήσεις των στατικών μελών δεδομένων κλάσης...273 Ένα παράδειγμα στατικών δεδομένων κλάσης...273 Ξεχωριστή δήλωση και ορισμός...275 const και κλάσεις...276 Συναρτήσεις-μέλη const...276 Αντικείμενα const...279 Τι σημαίνουν όλα αυτά;...280 Σύνοψη...281 Ερωτήσεις...282 Ασκήσεις...284 7 Πίνακες και αλφαριθμητικά 289 Βασικά στοιχεία για τους πίνακες...290 Ορισμός πινάκων...291 Στοιχεία πίνακα...291 Προσπέλαση στοιχείων πίνακα...293 Μέσος όρος στοιχείων πίνακα...293 Απόδοση αρχικών τιμών σε πίνακες...294 Πολυδιάστατοι πίνακες...296 Μεταβίβαση πινάκων σε συναρτήσεις...300 Πίνακες δομών...303 Πίνακες ως μέλη δεδομένων σε κλάσεις...305 Πίνακες αντικειμένων...309 Πίνακες αποστάσεων στο Αγγλικό σύστημα μέτρησης...309 Πίνακας με τραπουλόχαρτα...312 Αλφαριθμητικά της C...316 Αλφαριθμητικές μεταβλητές τύπου C...316 Αποφυγή υπερχείλισης της προσωρινής αποθήκευσης...318 Αλφαριθμητικές σταθερές...318 Ανάγνωση ενσωματωμένων κενών...319 Ανάγνωση πολλών γραμμών...320 Αντιγραφή αλφαριθμητικού με το δύσκολο τρόπο...321 Αντιγραφή αλφαριθμητικού με τον εύκολο τρόπο...322 Πίνακες αλφαριθμητικών...323 Αλφαριθμητικά ως μέλη κλάσης...324 Τύπος αλφαριθμητικού ορισμένος από το χρήστη...326
Περιεχόμενα 9 Η κλάση string της Standard C++...328 Ορισμός και ανάθεση αντικειμένων string...329 Είσοδος/Έξοδος με αντικείμενα string...330 Εύρεση αντικειμένων string...331 Τροποποίηση αντικειμένων string...332 Σύγκριση αντικειμένων string...333 Προσπέλαση χαρακτήρων σε αντικείμενα string...335 Άλλες συναρτήσεις της κλάσης string...336 Σύνοψη...336 Ερωτήσεις...337 Ασκήσεις...339 8 Υπερφόρτωση τελεστών 345 Υπερφόρτωση μονομελών τελεστών...346 Η δεσμευμένη λέξη operator...348 Ορίσματα τελεστών...349 Επιστρεφόμενες τιμές τελεστών...349 Ανώνυμα προσωρινά αντικείμενα...351 Επιθεματική σημειογραφία...352 Υπερφόρτωση διμελών τελεστών...354 Αριθμητικοί τελεστές...354 Συνένωση αλφαριθμητικών...358 Πολλαπλή υπερφόρτωση...360 Τελεστές σύγκρισης...360 Αριθμητικοί τελεστές απόδοσης τιμής...363 Ο τελεστής αριθμοδείκτη([])...366 Μετατροπή δεδομένων...370 Μετατροπές μεταξύ βασικών τύπων...370 Μετατροπές μεταξύ αντικειμένων και βασικών τύπων...371 Μετατροπές μεταξύ αντικειμένων διαφορετικών κλάσεων...376 Μετατροπές: πότε να χρησιμοποιείτε τι...383 Διαγράμματα κλάσεων UML...383 Συσχετίσεις...383 Πλοηγησιμότητα...384 Παγίδες κατά την υπερφόρτωση τελεστών και τη μετατροπή δεδομένων...384 Χρησιμοποιήστε παρόμοιες έννοιες...384 Χρησιμοποιήστε παρόμοια σύνταξη...385 Δείξτε αυτοσυγκράτηση...385 Αποφύγετε τις αμφισημίες...386 Δεν μπορούν να υπερφορτωθούν όλοι οι τελεστές...386 Οι δεσμευμένες λέξεις explicit και mutable...386 Αλλαγή δεδομένων αντικειμένων const με χρήση της mutable...388 Σύνοψη...390 Ερωτήσεις...390 Ασκήσεις...393
10 Αντικειμενοστρεφής προγραμματισμός με τη C++ 9 Κληρονομικότητα 397 Παράγωγη κλάση και βασική κλάση...399 Καθορισμός της παράγωγης κλάσης...401 Η γενίκευση σε διαγράμματα κλάσεων UML...401 Προσπέλαση μελών της βασικής κλάσης...402 Το προσδιοριστικό πρόσβασης protected...403 Συναρτήσεις δόμησης παραγώγων κλάσεων...406 Υποσκέλιση συναρτήσεων-μελών...408 Ποια συνάρτηση χρησιμοποιείται;...409 Επίλυση εμβέλειας σε συναρτήσεις που έχουν υποστεί υποσκέλιση...410 Κληρονομικότητα στην κλάση Αγγλικών μονάδων μέτρησης Distance...410 Λειτουργία του προγράμματος ENGLEN...413 Συναρτήσεις δόμησης στην DistSign...413 Συναρτήσεις-μέλη της DistSign...413 Υποστήριξη της κληρονομικότητας...414 Ιεραρχίες κλάσεων...414 "Αφηρημένη" βασική κλάση...418 Συναρτήσεις δόμησης και συναρτήσεις-μέλη...419 Κληρονομικότητα και σχήματα γραφικών...419 Δημόσια και ιδιωτική κληρονομικότητα...422 Συνδυασμοί πρόσβασης...423 Προσδιοριστικά πρόσβασης: πότε να χρησιμοποιείτε τι...425 Επίπεδα κληρονομικότητας...425 Πολλαπλή κληρονομικότητα...429 Συναρτήσεις-μέλη στην πολλαπλή κληρονομικότητα...430 Ιδιωτικές παράγωγες κλάσεις στο πρόγραμμα EMPMULT...435 Συναρτήσεις δόμησης στην πολλαπλή κληρονομικότητα...435 Αμφισημία στην πολλαπλή κληρονομικότητα...439 Συσσώρευση: κλάσεις στο εσωτερικό κλάσεων...440 Η συσσώρευση στο πρόγραμμα EMPCONT...442 Σύνθεση: μια ισχυρότερη συσσώρευση...446 Κληρονομικότητα και ανάπτυξη προγραμμάτων...446 Σύνοψη...447 Ερωτήσεις...448 Ασκήσεις...450 10 Δείκτες 455 Διευθύνσεις και δείκτες...456 Ο τελεστής διεύθυνσης &...457 Μεταβλητές δείκτη...459 Προσπέλαση της μεταβλητής στην οποία δείχνει ο δείκτης...462 Δείκτης σε void...465 Δείκτες και πίνακες...466 Σταθερές δείκτη και μεταβλητές δείκτη...468
Περιεχόμενα 11 Δείκτες και συναρτήσεις...469 Μεταβίβαση απλών μεταβλητών...469 Μεταβίβαση πινάκων...472 Ταξινόμηση στοιχείων πίνακα...474 Δείκτες και αλφαριθμητικά της C...478 Δείκτες σε αλφαριθμητικές σταθερές...478 Αλφαριθμητικά ως ορίσματα συναρτήσεων...479 Αντιγραφή αλφαριθμητικού με χρήση δεικτών...480 Συναρτήσεις βιβλιοθήκης για αλφαριθμητικά...482 Δείκτες και το τροποποιητικό const...482 Πίνακες δεικτών σε αλφαριθμητικά...482 Διαχείριση μνήμης: new και delete...484 Ο τελεστής new...485 Ο τελεστής delete...487 Κλάση αλφαριθμητικών που χρησιμοποιεί τον τελεστή new...488 Δείκτες σε αντικείμενα...490 Αναφορά σε μέλη...491 Μια άλλη προσέγγιση του τελεστή new...492 Πίνακας δεικτών προς αντικείμενα...493 Ένα παράδειγμα συνδεδεμένης λίστας...495 Αλυσίδα δεικτών...495 Προσθήκη στοιχείου στη λίστα...497 Εμφάνιση των περιεχομένων της λίστας...498 Αυτοτελείς κλάσεις...499 Αναβάθμιση του προγράμματος LINKLIST...499 Δείκτες προς δείκτες...500 Ταξινόμηση δεικτών...502 Ο τύπος δεδομένων person**...502 Σύγκριση αλφαριθμητικών...504 Ένα παράδειγμα συντακτικής ανάλυσης...505 Ανάλυση αριθμητικών παραστάσεων...505 Το πρόγραμμα PARSE...507 Προσομοίωση ιπποδρομίας...510 Σχεδίαση της ιπποδρομίας...511 Η πολλαπλότητα στη UML...515 Διαγράμματα καταστάσεων UML...516 Καταστάσεις...517 Μεταβάσεις...517 Μετάβαση από κατάσταση σε κατάσταση...518 Αποσφαλμάτωση δεικτών...518 Σύνοψη...519 Ερωτήσεις...520 Ασκήσεις...523
12 Αντικειμενοστρεφής προγραμματισμός με τη C++ 11 Εικονικές συναρτήσεις 529 Εικονικές συναρτήσεις...530 Προσπέλαση κανονικών συναρτήσεων-μελών με δείκτες...531 Προσπέλαση εικονικών συναρτήσεων-μελών με δείκτες...533 Όψιμη σύνδεση...535 Αφηρημένες κλάσεις και γνήσιες εικονικές συναρτήσεις...536 Εικονικές συναρτήσεις και η κλάση person...537 Εικονικές συναρτήσεις σε ένα παράδειγμα γραφικών...540 Εικονικές συναρτήσεις αποδόμησης...543 Εικονικές βασικές κλάσεις...544 Φίλες συναρτήσεις...546 Οι φίλες συναρτήσεις ως γέφυρες...546 Παραβίαση των τειχών...548 Παράδειγμα με το Αγγλικό σύστημα μονάδων μέτρησης αποστάσεων...548 Ευκολότερη σύνταξη με φίλες συναρτήσεις...552 Φίλες κλάσεις...554 Στατικές συναρτήσεις...555 Προσπέλαση στατικών συναρτήσεων...557 Αρίθμηση των αντικειμένων...558 Εξέταση των συναρτήσεων αποδόμησης...558 Απόδοση αρχικών τιμών με ανάθεση και αντιγραφή...558 Υπερφόρτωση του τελεστή απόδοσης τιμής...559 Η συνάρτηση δόμησης αντιγράφου...562 Διαγράμματα αντικειμένων UML...565 Μια κλάση String με αποδοτική διαχείριση της μνήμης...566 Ο δείκτης this...573 Προσπέλαση μελών δεδομένων με το δείκτη this...573 Χρήση του δείκτη this για επιστροφή τιμών...574 Αναθεωρημένο πρόγραμμα STRIMEM...576 Δυναμικές πληροφορίες τύπων...579 Έλεγχος του τύπου μιας κλάσης με τον τελεστή dynamic_cast...579 Αλλαγή τύπου ενός δείκτη με τον τελεστή dynamic_cast...580 Ο τελεστής typeid...582 Σύνοψη...583 Ερωτήσεις...584 Ασκήσεις...587 12 Ρεύματα και αρχεία 593 Κλάσεις ρευμάτων...594 Πλεονεκτήματα των ρευμάτων...594 Η ιεραρχία των κλάσεων ρευμάτων...594 Η κλάση ios...596 Η κλάση istream...600 Η κλάση ostream...601 Η κλάση iostream και οι κλάσεις _withassign...602
Περιεχόμενα 13 Σφάλματα ρευμάτων...603 Bit κατάστασης σφαλμάτων...603 Είσοδος αριθμών...604 Υπερβολικά πολλοί χαρακτήρες...605 Είσοδος που δεν είναι είσοδος...605 Είσοδος αλφαριθμητικών και χαρακτήρων...606 Αποστάσεις χωρίς σφάλματα...606 Είσοδος/έξοδος σε αρχεία δίσκου με ρεύματα...609 Μορφοποιημένη είσοδος/έξοδος σε αρχεία...609 Εγγραφή δεδομένων...610 Ανάγνωση δεδομένων...611 Αλφαριθμητικά με ενσωματωμένα κενά...612 Είσοδος/έξοδος χαρακτήρων...614 Δυαδική είσοδος/έξοδος...615 Ο τελεστής reinterpret_cast...617 Κλείσιμο αρχείων...617 Είσοδος/έξοδος αντικειμένων...617 Εγγραφή αντικειμένου στο δίσκο...618 Ανάγνωση αντικειμένου από το δίσκο...619 Συμβατές δομές δεδομένων...619 Είσοδος/έξοδος με πολλά αντικείμενα...620 Δείκτες αρχείων...623 Καθορισμός της θέσης...624 Καθορισμός της σχετικής απόστασης...624 Η συνάρτηση tellg()...626 Χειρισμός σφαλμάτων εισόδου/εξόδου σε αρχεία...627 Αντίδραση σε σφάλματα...627 Ανάλυση σφαλμάτων...628 Είσοδος/έξοδος αρχείων με συναρτήσεις-μέλη...630 Αντικείμενα που γράφουν και διαβάζουν τον εαυτό τους...630 Κλάσεις που διαβάζουν και γράφουν τον εαυτό τους...633 Υπερφόρτωση των τελεστών εξαγωγής και εισαγωγής...642 Υπερφόρτωση των cout και cin...642 Υπερφόρτωση για αρχεία...644 Η μνήμη ως αντικείμενο ρεύματος...646 Ορίσματα γραμμής διαταγών...648 Έξοδος στον εκτυπωτή...650 Σύνοψη...652 Ερωτήσεις...653 Ασκήσεις...654
14 Αντικειμενοστρεφής προγραμματισμός με τη C++ 13 Προγράμματα με πολλά αρχεία 659 Λόγοι για τη χρήση προγραμμάτων με πολλά αρχεία...660 Βιβλιοθήκες κλάσεων...660 Δημιουργία προγράμματος με πολλά αρχεία...663 Αρχεία-κεφαλίδες...663 Κατάλογος...663 Έργα...663 Επικοινωνία μεταξύ αρχείων...664 Επικοινωνία μεταξύ πηγαίων αρχείων...664 Αρχεία-κεφαλίδες...669 Χώροι ονομάτων...673 Ορισμός χώρου ονομάτων...673 Προσπέλαση μελών χώρου ονομάτων...674 Χώροι ονομάτων σε αρχεία-κεφαλίδες...674 Κλάση πολύ μεγάλων αριθμών...677 Αριθμοί ως αλφαριθμητικά...678 Το προσδιοριστικό της κλάσης...678 Οι συναρτήσεις-μέλη...680 Το πρόγραμμα εφαρμογής...683 Προσομοίωση ανελκυστήρα σε πολυώροφο κτήριο...684 Εκτέλεση του προγράμματος ELEV...684 Αίτηση για μεταφορά σε όροφο...685 Σχεδιασμός του συστήματος...686 Λίστες του προγράμματος ELEV...688 Διάγραμμα καταστάσεων για το πρόγραμμα ELEV...701 Σύνοψη...702 Ερωτήσεις...703 Έργα...705 14 Πρότυπα και εξαιρέσεις 707 Πρότυπα συναρτήσεων...708 Ένα απλό πρότυπο συναρτήσεων...710 Πρότυπα συναρτήσεων με πολλά ορίσματα...712 Τα ορίσματα προτύπων πρέπει να ταιριάζουν...714 Παραλλαγή σύνταξης...714 Περισσότερα από ένα ορίσματα προτύπου...714 Γιατί όχι μακροεντολές;...715 Τι λειτουργεί;...715 Ξεκινήστε με μια κανονική συνάρτηση...716 Πρότυπα κλάσεων...716 Το όνομα της κλάσης εξαρτάται από τα συμφραζόμενα...720 Μια κλάση συνδεδεμένης λίστας που χρησιμοποιεί πρότυπα...722 Αποθήκευση τύπων δεδομένων που έχουν οριστεί από το χρήστη...724 Η UML και τα πρότυπα...728 Εξαιρέσεις...729 Γιατί χρειαζόμαστε τις εξαιρέσεις;...729 Σύνταξη εξαιρέσεων...730
Περιεχόμενα 15 Ένα απλό παράδειγμα εξαίρεσης...732 Η σειρά των συμβάντων...735 Πολλαπλές εξαιρέσεις...736 Εξαιρέσεις με την κλάση Distance...738 Εξαιρέσεις με ορίσματα...740 Η κλάση bad_alloc...743 Σημειώσεις σχετικά με τις εξαιρέσεις...744 Σύνοψη...746 Ερωτήσεις...746 Ασκήσεις...748 15 Η Καθιερωμένη Βιβλιοθήκη Προτύπων 751 Εισαγωγή στην STL...752 Αποδέκτες...753 Ακολουθιακοί αποδέκτες...754 Συνειρμικοί αποδέκτες...755 Συναρτήσεις-μέλη...756 Προσαρμογείς αποδεκτών...757 Αλγόριθμοι...758 Επαναλήπτες...759 Πιθανά προβλήματα με την STL...760 Αλγόριθμοι...761 Ο αλγόριθμος find()...761 Αρχεία-κεφαλίδες...761 Περιοχές τιμών...762 Ο αλγόριθμος count()...762 Ο αλγόριθμος sort()...763 Ο αλγόριθμος search()...763 Ο αλγόριθμος merge()...764 Αντικείμενα συναρτήσεων...765 Ο αλγόριθμος for_each()...768 Ο αλγόριθμος transform()...768 Ακολουθιακοί αποδέκτες...769 Διανύσματα...769 Συναρτήσεις-μέλη push_back(), size(), και operator[]...770 Οι συναρτήσεις-μέλη swap(), empty(), back(), και pop_back()...771 Συναρτήσεις-μέλη insert() και erase()...772 Λίστες...773 Συναρτήσεις-μέλη push_front(), front(), και pop_front()...773 Συναρτήσεις-μέλη reverse(), merge(), και unique()...774 Ουρές διπλής εισόδου...776 Επαναλήπτες...777 Οι επαναλήπτες ως έξυπνοι δείκτες...778 Οι επαναλήπτες ως διασύνδεση...779 Ταίριασμα αλγορίθμων με αποδέκτες...781 Οι επαναλήπτες σε δράση...785
16 Αντικειμενοστρεφής προγραμματισμός με τη C++ Εξειδικευμένοι επαναλήπτες...789 Προσαρμογείς επαναληπτών...789 Επαναλήπτες ρευμάτων...793 Συνειρμικοί αποδέκτες...797 Σύνολα και πολυσύνολα...797 Χάρτες και πολυχάρτες...801 Αποθήκευση αντικειμένων που έχουν οριστεί από το χρήστη...804 Ένα σύνολο αντικειμένων person...804 Λίστα αντικειμένων person...808 Αντικείμενα συναρτήσεων...812 Προκαθορισμένα αντικείμενα συναρτήσεων...812 Γραφή των δικών σας αντικειμένων συναρτήσεων...815 Αντικείμενα συναρτήσεων για την τροποποίηση της συμπεριφοράς αποδεκτών...820 Σύνοψη...820 Ερωτήσεις...821 Ασκήσεις...823 16 Αντικειμενοστρεφής ανάπτυξη λογισμικού 827 Η εξέλιξη των διαδικασιών ανάπτυξης λογισμικού...828 Η άμεση προσέγγιση...828 Η διαδικασία του "καταρράκτη"...828 Αντικειμενοστρεφής προγραμματισμός...829 Σύγχρονες διαδικασίες...829 Μοντελοποίηση περιπτώσεων χρήσης...831 Ηθοποιοί...831 Περιπτώσεις χρήσης...832 Σενάρια...832 Διαγράμματα περιπτώσεων χρήσης...832 Περιγραφές περιπτώσεων χρήσης...833 Από τις περιπτώσεις χρήσης στις κλάσεις...834 Το προγραμματιστικό πρόβλημα...835 Χειρόγραφες φόρμες...835 Παραδοχές...837 Η φάση επεξεργασίας του προγράμματος LANDLORD...838 Ηθοποιοί...838 Περιπτώσεις χρήσης...838 Περιγραφές περιπτώσεων χρήσης...839 Σενάρια...841 Διαγράμματα δραστηριοτήτων UML...841 Από τις περιπτώσεις χρήσης στις κλάσεις...842 Κατάλογος των ουσιαστικών...842 Βελτίωση του καταλόγου...843 Ανακάλυψη ιδιοτήτων...844 Από τα ρήματα στα μηνύματα...844 Διαγράμματα κλάσεων...846 Ακολουθιακά διαγράμματα...846
Περιεχόμενα 17 Γραφή κώδικα...850 Το αρχείο-κεφαλίδα...851 Τα αρχεία.cpp...857 Ακόμη περισσότερες απλουστεύσεις...867 Αλληλεπίδραση με το πρόγραμμα...867 Τελικές σκέψεις...869 Σύνοψη...870 Ερωτήσεις...870 Έργα...873 Α Πίνακας ASCII 875 Β Πίνακας προτεραιότητας και δεσμευμένες λέξεις στη C++ 885 Πίνακας προτεραιότητας...886 Δεσμευμένες λέξεις...886 Γ Microsoft Visual C++ 889 Στοιχεία της οθόνης...890 Προγράμματα ενός αρχείου...890 Δόμηση υπάρχοντος αρχείου...890 Γραφή νέου αρχείου...891 Σφάλματα...891 Πληροφορίες τύπων κατά το χρόνο εκτέλεσης (RTTI)...892 Προγράμματα με πολλά αρχεία...892 Έργα και χώροι εργασίας...892 Ανάπτυξη του έργου...893 Αποθήκευση, κλείσιμο, και άνοιγμα έργων...894 Δόμηση προγραμμάτων με συναρτήσεις Console Graphics Lite...894 Αποσφαλμάτωση...895 Βηματική εκτέλεση...895 Παρακολούθηση μεταβλητών...895 Εκτέλεση των εντολών στο εσωτερικό συναρτήσεων...895 Σημεία διακοπής...896 Δ Borland C++ Builder 897 Εκτέλεση των προγραμμάτων των παραδειγμάτων με το C++ Builder...898 Καθαρισμός της οθόνης...899 Δημιουργία νέου έργου...899 Ονομασία και αποθήκευση έργου...900 Ξεκίνημα με υπάρχοντα αρχεία...901 Μεταγλώττιση, σύνδεση, και εκτέλεση...901 Εκτέλεση από το C++ Builder...901 Εκτέλεση από το MS-DOS...901 Προμεταγλωττισμένα αρχεία-κεφαλίδες...902 Κλείσιμο και άνοιγμα έργων...902
18 Αντικειμενοστρεφής προγραμματισμός με τη C++ Προσθήκη αρχείου-κεφαλίδας στο έργο σας...902 Δημιουργία νέου αρχείου-κεφαλίδας...902 Διόρθωση υπάρχοντος αρχείου-κεφαλίδας...902 Ενημέρωση του C++ Builder για τη θέση του αρχείου-κεφαλίδας...903 Έργα με πολλά πηγαία αρχεία...903 Δημιουργία πρόσθετων πηγαίων αρχείων...903 Προσθήκη υπαρχόντων αρχείων στο έργο σας...903 Ο Διαχειριστής έργων...904 Προγράμματα με συναρτήσεις του πακέτου Console Graphics Lite...904 Αποσφαλμάτωση...905 Βηματική εκτέλεση...905 Παρακολούθηση μεταβλητών...905 Εκτέλεση εντολών στο εσωτερικό συναρτήσεων...905 Σημεία διακοπής...906 Ε Console Graphics Lite 907 Χρήση ρουτινών του πακέτου Console Graphics Lite...908 Οι συναρτήσεις του πακέτου Console Graphics Lite...909 Υλοποιήσεις των συναρτήσεων του πακέτου Console Graphics Lite...910 Μεταγλωττιστές τής Microsoft...911 Μεταγλωττιστές τής Borland...911 Λίστες πηγαίου κώδικα...911 Λίστα του MSOFTCON.H...912 Λίστα του MSOFTCON.CPP...912 Λίστα του BORLACON.H...916 Λίστα του BORLACON.CPP...917 ΣΤ Αλγόριθμοι και συναρτήσεις-μέλη της STL 921 Αλγόριθμοι...922 Συναρτήσεις-μέλη...933 Επαναλήπτες...935 Ζ Απαντήσεις ερωτήσεων και ασκήσεων 939 Κεφάλαιο 1...940 Απαντήσεις στις ερωτήσεις...940 Κεφάλαιο 2...940 Απαντήσεις στις ερωτήσεις...940 Λύσεις των ασκήσεων...942 Κεφάλαιο 3...943 Απαντήσεις στις ερωτήσεις...943 Λύσεις των ασκήσεων...944 Κεφάλαιο 4...947 Απαντήσεις στις ερωτήσεις...947 Λύσεις των ασκήσεων...948 Κεφάλαιο 5...950 Απαντήσεις στις ερωτήσεις...950 Λύσεις των ασκήσεων...951
Περιεχόμενα 19 Κεφάλαιο 6...954 Απαντήσεις στις ερωτήσεις...954 Λύσεις των ασκήσεων...955 Κεφάλαιο 7...958 Απαντήσεις στις ερωτήσεις...958 Λύσεις των ασκήσεων...959 Κεφάλαιο 8...963 Απαντήσεις στις ερωτήσεις...963 Λύσεις των ασκήσεων...964 Κεφάλαιο 9...969 Απαντήσεις στις ερωτήσεις...969 Λύσεις των ασκήσεων...970 Κεφάλαιο 10...975 Απαντήσεις στις ερωτήσεις...975 Λύσεις των ασκήσεων...976 Κεφάλαιο 11...980 Απαντήσεις στις ερωτήσεις...980 Λύσεις των ασκήσεων...982 Κεφάλαιο 12...986 Απαντήσεις στις ερωτήσεις...986 Λύσεις των ασκήσεων...987 Κεφάλαιο 13...989 Απαντήσεις στις ερωτήσεις...989 Κεφάλαιο 14...990 Απαντήσεις στις ερωτήσεις...990 Λύσεις των ασκήσεων...991 Κεφάλαιο 15...995 Απαντήσεις στις ερωτήσεις...995 Λύσεις των ασκήσεων...996 Κεφάλαιο 16...1000 Απαντήσεις στις ερωτήσεις...1000 Ζ Βιβλιογραφία 1003 C++ για προχωρημένους...1004 Έγγραφα προδιαγραφών...1004 Η Ενοποιημένη Γλώσσα Μοντελοποίησης...1004 Η Ιστορία της C++...1005 Άλλα θέματα...1005 Ευρετήριο 1007
Κληρονομικότητα ΚΕΦΑΛΑΙΟ 9 ΣΕ ΑΥΤΟ ΤΟ ΚΕΦΑΛΑΙΟ Παράγωγη κλάση και βασική κλάση 399 Συναρτήσεις δόμησης παραγώγων κλάσεων 406 Υποσκέλιση συναρτήσεων-μελών 408 Ποια συνάρτηση χρησιμοποιείται; 409 Κληρονομικότητα στην κλάση Αγγλικών μονάδων μέτρησης Distance 410 Ιεραρχίες κλάσεων 414 Κληρονομικότητα και σχήματα γραφικών 419 Δημόσια και ιδιωτική κληρονομικότητα 422 Επίπεδα κληρονομικότητας 425 Πολλαπλή κληρονομικότητα 429 Ιδιωτικές παράγωγες κλάσεις στο πρόγραμμα EMPMULT 435 Αμφισημία στην πολλαπλή κληρονομικότητα 439 Συσσώρευση: κλάσεις στο εσωτερικό κλάσεων 440 Κληρονομικότητα και ανάπτυξη προγραμμάτων 446
398 Κεφάλαιο 9 Η κληρονομικότητα είναι πιθανότατα η πιο ισχυρή λειτουργία του αντικειμενοστρεφούς προγραμματισμού, μετά βέβαια από τις κλάσεις. Η κληρονομικότητα είναι η διαδικασία της δημιουργίας νέων κλάσεων, που ονομάζονται παράγωγες κλάσεις (derived classes), από τις υπάρχουσες ή βασικές κλάσεις (base classes). Η παράγωγη κλάση κληρονομεί όλες τις δυνατότητες της βασικής κλάσης, αλλά μπορεί να τις "εκλεπτύνει" ή να τις "καλλωπίζει". Η βασική κλάση παραμένει αναλλοίωτη μετά από αυτή τη διαδικασία. Η σχέση της κληρονομικότητας φαίνεται στην Εικόνα 9.1. ΕΙΚΟΝΑ 9.1 Κληρονομικότητα. Το βέλος σε αυτή την εικόνα δείχνει σε αντίθετη κατεύθυνση από αυτή που θα περιμένατε. Αν έδειχνε προς τα κάτω, θα το λέγαμε κληρονομικότητα. Ωστόσο, η πιο συνηθισμένη προσέγγιση είναι να κατευθύνουμε το βέλος προς τα επάνω, από την παράγωγη κλάση προς τη βασική κλάση, και θεωρούμε ότι το βέλος έχει την έννοια "παράγεται από".
Κληρονομικότητα 399 Η κληρονομικότητα αποτελεί ουσιώδες τμήμα του ΑΣΠ. Το μεγάλο της πλεονέκτημα είναι ότι επιτρέπει την ανακύκλωση του κώδικα. Αφού γραφεί και αποσφαλματωθεί (debugged), μια βασική κλάση δεν χρειάζεται να την πειράξετε ξανά, αλλά μπορείτε να την προσαρμόσετε ώστε να λειτουργεί σε διαφορετικές συνθήκες. Η ανακύκλωση του υπάρχοντος κώδικα εξοικονομεί χρόνο και χρήματα και αυξάνει την αξιοπιστία του προγράμματος. Η κληρονομικότητα μπορεί επίσης να βοηθήσει στην αρχική σύλληψη ενός προβλήματος προγραμματισμού, καθώς και στο συνολικό σχεδιασμό του προγράμματος. Ένα αποτέλεσμα της ανακύκλωσης είναι η ευκολία στη διανομή βιβλιοθηκών κλάσεων. Ένας προγραμματιστής μπορεί να χρησιμοποιήσει μια κλάση που δημιουργήθηκε από κάποιο άλλο άτομο ή εταιρεία και, χωρίς να την τροποποιήσει, να παραγάγει άλλες κλάσεις που θα είναι κατάλληλες για τις συγκεκριμένες συνθήκες. Αφού δούμε στην πράξη μερικές ειδικές περιπτώσεις κληρονομικότητας, θα εξετάσουμε τα χαρακτηριστικά της κληρονομικότητας που αναφέραμε προηγουμένως. Παράγωγη κλάση και βασική κλάση Θυμάστε το παράδειγμα COUNTPP3 στο Κεφάλαιο 8, "Υπερφόρτωση τελεστών"; Αυτό το πρόγραμμα χρησιμοποιούσε την κλάση Counter ως μεταβλητή μετρητή γενικής χρήσης. Η μέτρηση μπορούσε μέσω των συναρτήσεων δόμησης να πάρει αρχική τιμή 0 ή κάποιο καθορισμένο αριθμό, ή να αυξηθεί με τον τελεστή ++, ή να διαβαστεί με τη συνάρτηση get_count(). Ας υποθέσουμε ότι εργαστήκαμε πολύ και σκληρά για να κάνουμε την κλάση Counter να λειτουργεί ακριβώς με τον τρόπο που θέλουμε, και ότι είμαστε ευχαριστημένοι με το αποτέλεσμα με εξαίρεση ένα μόνο πράγμα. Στην πραγματικότητα, χρειαζόμαστε έναν τρόπο για να μειώνουμε το μετρητή. Ί- σως μετρούμε τον κόσμο που μπαίνει σε μια τράπεζα, και θέλουμε να αυξάνουμε το μετρητή όταν κάποιος μπαίνει και να τον μειώνουμε όταν βγαίνει, ώστε κάθε στιγμή ο μετρητής να αντιπροσωπεύει το πλήθος των ατόμων που βρίσκονται μέσα στην τράπεζα. Θα μπορούσαμε να βάλουμε κατευθείαν στον πηγαίο κώδικα της κλάσης Counter μια ρουτίνα μείωσης. Ωστόσο, υπάρχουν διάφοροι λόγοι για τους οποίους μπορεί να μη θέλουμε να κάνουμε κάτι τέτοιο. Πρώτον, η κλάση Counter λειτουργεί πολύ καλά και έχει περάσει από πολλές ώρες δοκιμών και διόρθωσης σφαλμάτων. (Φυσικά, σε αυτή την περίπτωση αυτό είναι υπερβολή, αλλά αυτό θα μπορούσε να ισχύει για μια μεγαλύτερη και πιο σύνθετη κλάση.) Αν αρχίσουμε να παίζουμε με τον πηγαίο κώδικα της Counter, τότε η διαδικασία ελέγχου θα έπρεπε να επαναληφθεί, και βεβαίως μπορεί να πειράξουμε κάτι και να χρειαστούμε ώρες για να διορθώσουμε έναν κώδικα που λειτουργούσε θαυμάσια πριν τον τροποποιήσουμε. Σε μερικές περιπτώσεις μπορεί να υπάρχει και άλλος λόγος για να αποφύγουμε την τροποποίηση της κλάσης Counter: μπορεί να μην έχουμε πρόσβαση στον πηγαίο της κώδικα, ειδικά μάλιστα αν αυτή έχει διανεμηθεί μέσα σε μια βιβλιοθήκη κλάσεων. (Αυτό το θέμα θα το εξετάσουμε περισσότερο στο Κεφάλαιο 13, "Προγράμματα με πολλά αρχεία".) Για να αποφύγουμε αυτά τα προβλήματα, μπορούμε να χρησιμοποιήσουμε την κληρονομικότητα για να δημιουργήσουμε μια νέα κλάση που θα βασίζεται στην Counter, χωρίς να τροποποιήσουμε την ίδια την Counter. Θα δούμε τώρα τη λίστα του προγράμματος COUNTEN, το οποίο περιλαμβάνει μια νέα κλάση, την CountDn, που προσθέτει στην κλάση Counter έναν τελεστή μείωσης:
400 Κεφάλαιο 9 // counten.cpp // κληρονομικότητα με την κλάση Counter #include <iostream> using namespace std; //////////////////////////////////////////////////////////////// class Counter //βασική κλάση protected: //ΣΗΜΕΙΩΣΗ: η count δεν είναι unsigned int count; //ιδιωτικό μέλος public: Counter() : count(0) //συνάρτηση δόμησης χωρίς ορίσματα } Counter(int c) : count(c) //συνάρτηση ενός ορίσματος } unsigned int get_count() const //επιστροφή της count return count; } Counter operator ++ () //αύξηση της count (προθεματική) return Counter(++count); } }; //////////////////////////////////////////////////////////////// class CountDn : public Counter //παράγωγη κλάση public: Counter operator -- () //μείωση της count (προθεματική) return Counter(--count); } }; //////////////////////////////////////////////////////////////// int main() CountDn c1; //c1 της κλάσης CountDn cout << "\nc1=" << c1.get_count(); ++c1; ++c1; ++c1; cout << "\nc1=" << c1.get_count(); //εμφάνιση του c1 //αύξηση του c1 3 φορές //εμφάνιση του c1 --c1; --c1; cout << "\nc1=" << c1.get_count(); cout << endl; return 0; } //μείωση του c1 δύο φορές //εμφάνιση του c1 Η λίστα του προγράμματος ξεκινά με την κλάση Counter, η οποία (με μια μικρή εξαίρεση που θα δούμε αργότερα) δεν έχει αλλάξει από τη μορφή που είχε στο COUNTPP3. Σημειώστε ότι, για λόγους απλότητας, δεν βασίσαμε το πρόγραμμά μας στο πρόγραμμα POSTFIX το οποίο ενσωμάτωνε το δεύτερο υπερφορτωμένο τελεστή ++ ώστε να παρέχει και τη δυνατότητα επιθεματικής σημειογραφίας.
Κληρονομικότητα 401 Καθορισμός της παράγωγης κλάσης Μετά από την κλάση Counter, ακολουθεί στον κώδικα η προδιαγραφή μιας νέας κλάσης, της CountDn. Αυτή η κλάση ενσωματώνει μια νέα συνάρτηση, την operator--(), η οποία μειώνει τη μέτρηση. Ωστόσο και αυτό είναι το βασικό η νέα κλάση CountDn κληρονομεί όλα τα χαρακτηριστικά της κλάσης Counter. Η CountDn δεν χρειάζεται συνάρτηση δόμησης ούτε και τις συναρτήσεις get_count() ή operator++(), αφού αυτές υπάρχουν ήδη στην Counter. Η πρώτη γραμμή της CountDn καθορίζει ότι παράγεται από την Counter: class CountDn : public Counter Εδώ χρησιμοποιούμε μία άνω και κάτω τελεία (όχι δύο άνω και κάτω τελείες, που χρησιμοποιούνται για τον τελεστή επίλυσης εμβέλειας), ακολουθούμενη από τη δεσμευμένη λέξη public και το όνομα της βασικής κλάσης Counter. Αυτό ορίζει τη σχέση μεταξύ των κλάσεων. Η γραμμή αυτή λέει ότι η CountDn παράγεται από τη βασική κλάση Counter. (Το αποτέλεσμα της δεσμευμένης λέξης public θα την εξετάσουμε αργότερα.) Η γενίκευση σε διαγράμματα κλάσεων UML Στη UML, η κληρονομικότητα ονομάζεται γενίκευση (generalization) επειδή η γονική κλάση (parent class) είναι μια πιο γενική μορφή της θυγατρικής κλάσης (child class). Ή, για να το θέσουμε διαφορετικά, το τέκνο είναι μια πιο εξειδικευμένη εκδοχή του γονέα. (Παρουσιάσαμε τη UML στο Κεφάλαιο 1, "Η γενική εικόνα", και συναντήσαμε τα διαγράμματα κλάσεων στο Κεφάλαιο 8, "Υπερφόρτωση τελεστών".) Η γενίκευση στο πρόγραμμα COUNTEN παρουσιάζεται στην Εικόνα 9.2. Counter count counter() counter(int) get_count() operator++() CountDn operator--() ΕΙΚΟΝΑ 9.2 Διάγραμμα κλάσεων UML για τo πρόγραμμα COUNTEN.
402 Κεφάλαιο 9 Στα διαγράμματα κλάσεων της UML η γενίκευση υποδηλώνεται από μια τριγωνική (κλειστή) αιχμή βέλους στη γραμμή που συνδέει τη γονική και τη θυγατρική κλάση. Να θυμάστε ότι το βέλος αυτό σημαίνει κληρονομείται από ή παράγεται από ή είναι πιο εξειδικευμένη εκδοχή του. Η κατεύθυνση του βέλους τονίζει το γεγονός ότι η παράγωγη κλάση αναφέρεται σε συναρτήσεις και δεδομένα της βασικής κλάσης, ενώ η βασική κλάση δεν έχει πρόσβαση στην παράγωγη κλάση. Παρατηρήστε ότι έχουμε προσθέσει χαρακτηριστικά (μέλη δεδομένων) και λειτουργίες (συναρτήσειςμέλη) στις κλάσεις του διαγράμματος. Η περιοχή στην κορυφή περιέχει τον τίτλο της κλάσης, η περιοχή στο μέσο περιέχει τα χαρακτηριστικά, και η κάτω περιοχή είναι για τις λειτουργίες. Προσπέλαση μελών της βασικής κλάσης Ένα σημαντικό θέμα στην κληρονομικότητα είναι να γνωρίζετε πότε μπορεί να χρησιμοποιηθεί μια συνάρτηση-μέλος της βασικής κλάσης από αντικείμενα της παράγωγης κλάσης. Αυτό ονομάζεται προσπελασιμότητα (accessibility). Ας δούμε πώς χειρίζεται ο μεταγλωττιστής το θέμα της προσπελασιμότητας στο παράδειγμα COUNTEN. Αντικατάσταση των συναρτήσεων δόμησης της βασικής κλάσης Στο τμήμα main() του προγράμματος COUNTEN δημιουργούμε ένα αντικείμενο της κλάσης CountDn: CountDn c1; Αυτό δημιουργεί το c1 ως αντικείμενο της κλάσης CountDn και του δίνει αρχική τιμή 0. Για σταθείτε όμως πώς είναι δυνατόν να γίνεται αυτό; Δεν υπάρχει συνάρτηση δόμησης στο δηλωτικό της κλάσης CountDn, οπότε ποια οντότητα πραγματοποιεί την απόδοση αρχικής τιμής; Φαίνεται ότι τουλάχιστον κάτω από ορισμένες συνθήκες αν δεν καθορίσετε συνάρτηση δόμησης, η παράγωγη κλάση θα χρησιμοποιήσει μια κατάλληλη συνάρτηση δόμησης της βασικής κλάσης. Στο πρόγραμμα COUNTEN δεν υπάρχει συνάρτηση δόμησης στην CountDn, και έτσι ο μεταγλωττιστής χρησιμοποιεί τη χωρίς ορίσματα συνάρτηση δόμησης της Count. Η ευελιξία του μεταγλωττιστή να χρησιμοποιεί μια συνάρτηση επειδή δεν υπάρχει άλλη διαθέσιμη εμφανίζεται συχνά σε περιπτώσεις κληρονομικότητας. Συνήθως αυτή η αντικατάσταση είναι ακριβώς εκείνο που θέλετε, αλλά μερικές φορές μπορεί να είναι αποκαρδιωτική. Αντικατάσταση συναρτήσεων-μελών της βασικής κλάσης Το αντικείμενο c1 της κλάσης CountDn χρησιμοποιεί επίσης τις συναρτήσεις operator++() και get_count() από την κλάση Counter. Η πρώτη χρησιμοποιείται για την αύξηση του c1: c1++; και η δεύτερη χρησιμοποιείται για την εμφάνιση της μέτρησης του c1: cout << "\nc1=" << c1.get_count(); Και πάλι ο μεταγλωττιστής, μη βρίσκοντας αυτές τις συναρτήσεις στην κλάση της οποίας είναι μέλος το c1, χρησιμοποιεί τις συναρτήσεις-μέλη της βασικής κλάσης.
Κληρονομικότητα 403 Έξοδος του COUNTEN Στη συνάρτηση main() αυξάνουμε τρεις φορές το c1, τυπώνουμε την τιμή που προκύπτει, μειώνουμε δύο φορές το c1, και τελικά τυπώνουμε πάλι την τιμή του. Εδώ έχουμε την έξοδο: c1=0 < μετά την απόδοση αρχικής τιμής c1=3 < μετά από c1++, c1++, c1++ c1=1 < μετά από c1--, c1-- Ο τελεστής ++, οι συναρτήσεις δόμησης, η συνάρτηση get_count() της κλάσης Counter, και ο τελεστής -- της κλάσης CountDn, λειτουργούν όλα με αντικείμενα τύπου CountDn. Το προσδιοριστικό πρόσβασης protected Μέχρι τώρα, αυξήσαμε τη λειτουργικότητα της κλάσης χωρίς να την τροποποιήσουμε. Τέλος πάντων, σχεδόν χωρίς να την τροποποιήσουμε. Ας δούμε τη μοναδική αλλαγή που κάναμε στην κλάση Counter. Τα δεδομένα στις κλάσεις που έχουμε δει μέχρι τώρα, μεταξύ των οποίων και το count στην κλάση Counter του προηγούμενου προγράμματος COUNTPP3, έχουν χρησιμοποιήσει το προσδιοριστικό πρόσβασης (access specifier) private. Στην κλάση Counter του προγράμματος COUNTEN, το count παίρνει ένα νέο προσδιοριστικό, το protected (προστατευμένο). Τι κάνει αυτό; Ας επαναλάβουμε πρώτα όλα όσα ξέρουμε για τα προσδιοριστικά πρόσβασης private και public. Μια συνάρτηση-μέλος μιας κλάσης μπορεί πάντοτε να προσπελάσει τα μέλη της κλάσης, είτε είναι ιδιωτικά είτε είναι δημόσια. Όμως, ένα αντικείμενο της κλάσης που ορίζεται έξω από την κλάση μπορεί να προσπελάσει μόνο τα δημόσια μέλη της κλάσης (χρησιμοποιώντας, για παράδειγμα, τον τελεστή τελείας). Δεν επιτρέπεται να χρησιμοποιήσει ιδιωτικά μέλη. Για παράδειγμα, έστω ότι το αντικείμενο obja είναι παρουσία της κλάσης A και η συνάρτηση funca() είναι συνάρτηση-μέλος της Α. Τότε στη main() (ή σε οποιαδήποτε άλλη συνάρτηση που δεν είναι μέλος της Α), η εντολή obja.funca(); δεν θα είναι αποδεκτή εκτός αν η func() είναι δημόσια. Το αντικείμενο obja δεν μπορεί να προσπελάζει ιδιωτικά μέλη της κλάσης Α. Τα ιδιωτικά μέλη είναι, τέλος πάντων, ιδιωτικά. Αυτό φαίνεται στην Εικόνα 9.3. Αυτά είναι όλα όσα πρέπει να ξέρουμε αν δεν χρησιμοποιούμε την κληρονομικότητα. Με την κληρονομικότητα όμως, υπάρχει μια ολόκληρη γκάμα πρόσθετων δυνατοτήτων. Το ερώτημα που μας απασχολεί αυτή τη στιγμή είναι αν θα μπορούν οι συναρτήσεις-μέλη της παράγωγης κλάσης να προσπελάσουν μέλη της βασικής κλάσης. Με άλλα λόγια, μπορεί η operator--() της CountDn να προσπελάσει το μέλος δεδομένων count της Counter; Η απάντηση είναι ότι οι συναρτήσεις-μέλη μπορούν να προσπελάσουν μέλη της βασικής κλάσης μόνο αν τα μέλη αυτά είναι public (δημόσια) ή protected (προστατευμένα). Δεν μπορούν όμως να προσπελάσουν μέλη που είναι private (ιδιωτικά). Δεν θέλουμε να κάνουμε το count δημόσιο (public), αφού αυτό θα επέτρεπε την προσπέλασή του από οποιαδήποτε συνάρτηση σε οποιοδήποτε σημείο μέσα στο πρόγραμμα και θα εξαφάνιζε το πλεονέκτημα της απόκρυψης δεδομένων. Από την άλλη πλευρά, το προστατευμένο μέλος (protected) μπορεί να προσπελαστεί από συναρτήσεις-μέλη μέσα στη δική του κλάση ή και εδώ είναι το κλειδί σε οποιαδήποτε κλάση που έχει παραχθεί από την κλάση του. Δεν μπορεί να προσπελαστεί από
404 Κεφάλαιο 9 συναρτήσεις που βρίσκονται έξω από αυτές τις κλάσεις, όπως η main(). Αυτό ακριβώς είναι που θέλουμε. Η κατάσταση αυτή φαίνεται στην Εικόνα 9.4. Η συνάρτηση-μέλος της κλάσης Α μπορεί να προσπελάσει και τα ιδιωτικά και τα δημόσια μέλη. κλάση A private Δεν επιτρέπεται public ObjA ΕΙΚΟΝΑ 9.3 Προσδιοριστικά πρόσβασης χωρίς κληρονομικότητα. Το αντικείμενο της κλάσης Α μπορεί να προσπελάσει μόνο τα δημόσια μέλη της Α. ΕΙΚΟΝΑ 9.4 Προσδιοριστικά πρόσβασης με κληρονομικότητα.
Κληρονομικότητα 405 Ο Πίνακας 9.1 συνοψίζει την κατάσταση με διαφορετικό τρόπο: ΠΙΝΑΚΑΣ 9.1 Κληρονομικότητα και προσπελασιμότητα Προσδιοριστικό Προσπελάσιμο από Προσπελάσιμο από Προσπελάσιμο από πρόσβασης τη δική του κλάση παράγωγη κλάση αντικείμενα έξω από την κλάση public ναι ναι ναι protected ναι ναι όχι private ναι όχι όχι Το συμπέρασμα είναι ότι, αν γράφετε μια κλάση που υποψιάζεστε ότι μπορεί να χρησιμοποιηθεί στο μέλλον ως βασική κλάση για άλλες παράγωγες κλάσεις, τότε τα δεδομένα-μέλη που θα χρειαστεί να προσπελάσουν οι παράγωγες κλάσεις θα πρέπει να έχουν το προσδιοριστικό protected, και όχι το private. Αυτό εξασφαλίζει ότι η κλάση θα έχει τη "δυνατότητα κληροδότησης". Κίνδυνοι του προσδιοριστικού protected Θα πρέπει να γνωρίζετε ότι υπάρχει ένα μειονέκτημα όταν δίνετε την ιδιότητα protected στα μέλη της κλάσης. Ας υποθέσουμε ότι έχετε γράψει μια βιβλιοθήκη κλάσεων και την έχετε διαθέσει σε άλλους. Οποιοσδήποτε προγραμματιστής που θα αγοράσει αυτή τη βιβλιοθήκη θα μπορεί να προσπελάσει τα προστατευμένα μέλη των κλάσεών σας με απλή παραγωγή άλλων κλάσεων από αυτές. Αυτό κάνει τα προστατευμένα μέλη πολύ λιγότερο ασφαλή, σε σύγκριση με τα ιδιωτικά μέλη. Για να αποφύγετε την αλλοίωση των δεδομένων, συχνά είναι πιο ασφαλές να αναγκάζετε τις παράγωγες κλάσεις να προσπελάζουν τα δεδομένα της βασικής κλάσης με τη χρήση μόνο δημόσιων συναρτήσεων-μελών της βασικής κλάσης, όπως πρέπει να κάνουν τα κανονικά προγράμματα main(). Η χρήση του προσδιοριστικού protected οδηγεί σε απλούστερα προγράμματα, οπότε και εμείς θα το χρησιμοποιήσουμε ίσως περισσότερο από ό,τι θα έπρεπε στα παραδείγματα του βιβλίου. Για τα δικά σας προγράμματα θα πρέπει να σταθμίσετε τα πλεονεκτήματα και τα μειονεκτήματα που παρέχει το προσδιοριστικό protected. Αμετάβλητη βασική κλάση Θυμηθείτε ότι, ακόμα και αν έχουν παραχθεί άλλες κλάσεις από αυτή, η βασική κλάση παραμένει αμετάβλητη. Στο τμήμα main() του προγράμματος COUNTEN, θα μπορούσαμε να ορίσουμε αντικείμενα τύπου Counter: Counter c2; < αντικείμενο βασικής κλάσης Αυτά τα αντικείμενα θα συμπεριφέρονταν όπως και αν δεν υπήρχε η CountDn. Παρατηρήστε επίσης ότι η κληρονομικότητα δεν λειτουργεί αντίστροφα. Η βασική κλάση και τα α- ντικείμενά της δεν γνωρίζουν τίποτε για όποιες κλάσεις προκύπτουν από τη βασική κλάση. Στο συγκεκριμένο παράδειγμα, αυτό σημαίνει ότι τα αντικείμενα της κλάσης Counter, όπως το c2 που ορίσαμε εδώ, δεν μπορούν να χρησιμοποιήσουν τη συνάρτηση operator--() που υπάρχει στην CountDn. Αν θέλετε ένα μετρητή που να μπορείτε να μειώνετε, τότε αυτός θα πρέπει να ανήκει στην κλάση CountDn και όχι στην Counter.
406 Κεφάλαιο 9 Άλλοι όροι Σε ορισμένες γλώσσες, η βασική κλάση ονομάζεται υπερκλάση (superclass) και η παράγωγη κλάση υποκλάση (subclass). Επίσης, ορισμένοι συγγραφείς αναφέρουν τη βασική κλάση ως γονική (parent) και την παράγωγη κλάση ως θυγατρική (child). Συναρτήσεις δόμησης παραγώγων κλάσεων Στο πρόγραμμα COUNTEN, υπάρχει κάποιο πρόβλημα που δεν έχει κάνει ακόμα την εμφάνισή του. Τι θα συμβεί αν θελήσουμε να δώσουμε αρχική τιμή σε ένα αντικείμενο CountDn; Μπορεί να χρησιμοποιηθεί η συνάρτηση δόμησης ενός ορίσματος της Counter; Η απάντηση είναι όχι. Όπως είδαμε στο COUNTEN, ο μεταγλωττιστής θα χρησιμοποιήσει μια συνάρτηση δόμησης της βασικής κλάσης χωρίς ορίσματα, αλλά δεν θα το κάνει για τις πιο σύνθετες συναρτήσεις δόμησης. Για να μπορεί να λειτουργήσει ένας τέτοιος ορισμός, θα πρέπει να γράψουμε ένα νέο σύνολο συναρτήσεων δόμησης για την παράγωγη κλάση. Αυτό φαίνεται στο πρόγραμμα COUNTEN2: // counten2.cpp // συναρτήσεις δόμησης στην παράγωγη κλάση #include <iostream> using namespace std; //////////////////////////////////////////////////////////////// class Counter protected: //ΠΡΟΣΟΧΗ: η count δεν είναι unsigned int count; //ιδιωτικό μέλος public: Counter() : count(0) //συνάρτηση δόμησης, χωρίς ορίσματα } Counter(int c) : count(c) //συνάρτηση δόμησης, ένα όρισμα } unsigned int get_count() const //επιστροφή της count return count; } Counter operator ++ () //αύξηση της count (προθεματική) return Counter(++count); } }; //////////////////////////////////////////////////////////////// class CountDn : public Counter public: CountDn() : Counter() //συνάρτηση δόμησης, χωρίς ορίσματα } CountDn(int c) : Counter(c) //συνάρτηση δόμησης, με ένα όρισμα } CountDn operator -- () //μείωση της count (προθεματική) return CountDn(--count); } };
Κληρονομικότητα 407 //////////////////////////////////////////////////////////////// int main() CountDn c1; //κλάση CountDn CountDn c2(100); cout << "\nc1=" << c1.get_count(); cout << "\nc2=" << c2.get_count(); ++c1; ++c1; ++c1; cout << "\nc1=" << c1.get_count(); --c2; --c2; cout << "\nc2=" << c2.get_count(); //εμφάνιση //εμφάνιση //αύξηση του c1 //εμφάνιση του c1 //μείωση του c2 //εμφάνιση του c2 CountDn c3 = --c2; cout << "\nc3=" << c3.get_count(); cout << endl; return 0; } //δημιουργία του c3 από το c2 //εμφάνιση του c3 Αυτό το πρόγραμμα χρησιμοποιεί δύο νέες συναρτήσεις δόμησης στην κλάση CountDn. Εδώ έχουμε τη συνάρτηση δόμησης χωρίς ορίσματα: CountDn() : Counter() } Αυτή η συνάρτηση δόμησης έχει ένα άγνωστο χαρακτηριστικό: το όνομα της συνάρτησης που ακολουθεί την άνω και κάτω τελεία. Αυτή η κατασκευή υποχρεώνει τη συνάρτηση δόμησης CountDn() να καλέσει τη συνάρτηση δόμησης Counter()της βασικής κλάσης. Στη main(), όταν λέμε CountDn c1; ο μεταγλωττιστής δημιουργεί ένα αντικείμενο τύπου CountDn και μετά καλεί τη συνάρτηση δόμησης Counter για να του δώσει αρχική τιμή. Αυτή η συνάρτηση δόμησης καλεί με τη σειρά της τη συνάρτηση δόμησης Counter που θα φέρει σε πέρας την εργασία. Η συνάρτηση δόμησης CountDn() θα μπορούσε να περιέχει και άλλες δικές της εντολές, αλλά σε αυτή την περίπτωση δεν χρειάζεται να το κάνει και, έτσι, το σώμα της συνάρτησης μέσα στα άγκιστρα είναι κενό. Η εντολή CountDn c2(100); στη main() χρησιμοποιεί τη συνάρτηση δόμησης ενός ορίσματος της κλάσης CountDn. Και αυτή η συνάρτηση δόμησης καλεί την αντίστοιχη συνάρτηση δόμησης της βασικής κλάσης: CountDn(int c) : Counter(c) < το όρισμα c μεταβιβάζεται στην Counter }
408 Κεφάλαιο 9 Αυτή η κατασκευή αναγκάζει το όρισμα c να μεταβιβαστεί από την CountDn() στην Counter(), όπου χρησιμοποιείται για να δώσει αρχική τιμή στο αντικείμενο. Μετά την απόδοση αρχικών τιμών στα αντικείμενα c1 και c2 στη main(), αυξάνουμε το ένα και μειώνουμε το άλλο και μετά τυπώνουμε το αποτέλεσμα. Η συνάρτηση δόμησης ενός ορίσματος χρησιμοποιείται επίσης και σε μια εντολή ανάθεσης τιμής: CountDn c3 = --c2; Υποσκέλιση συναρτήσεων-μελών Μέσα σε μια παράγωγη κλάση, μπορείτε να χρησιμοποιήσετε συναρτήσεις-μέλη που υποσκελίζουν (override) αυτές που υπάρχουν στη βασική κλάση δηλαδή, έχουν το ίδιο όνομα με αυτές. Μπορεί να χρειαστεί να κάνετε κάτι τέτοιο ώστε οι κλήσεις του προγράμματός σας να λειτουργούν με τον ίδιο τρόπο για αντικείμενα τόσο της βασικής όσο και της παράγωγης κλάσης. Θα δούμε τώρα ένα παράδειγμα που βασίζεται στο πρόγραμμα STAKARAY του Κεφαλαίου 7, "Πίνακες και αλφαριθμητικά". Εκείνο το πρόγραμμα δημιουργούσε το μοντέλο μιας στοίβας ενός απλού μηχανισμού αποθήκευσης δεδομένων. Σας επέτρεπε να τοποθετείτε (push) ακεραίους μέσα στη στοίβα και να τους εξάγετε από αυτή. Ωστόσο, το πρόγραμμα STAKARAY είχε ένα ελάττωμα. Αν επιχειρούσατε να τοποθετήσετε περισσότερα στοιχεία στη στοίβα, το πρόγραμμα μπορούσε να "καταρρεύσει" επειδή τα δεδομένα θα έμπαιναν στη μνήμη σε θέσεις πέρα από το τέλος του πίνακα st[]. Επιπλέον, αν επιχειρούσατε να βγάλετε περισσότερα στοιχεία από τη στοίβα το αποτέλεσμα δεν θα είχε κανένα νόημα, αφού θα διαβάζατε δεδομένα από θέσεις της μνήμης έξω από τον πίνακα. Για να "θεραπεύσουμε" αυτά τα ελαττώματα δημιουργήσαμε μια νέα κλάση, τη Stack2, που είναι παράγωγη της Stack. Τα αντικείμενα της Stack2 συμπεριφέρονται ακριβώς με τον ίδιο τρόπο όπως και εκείνα της Stack, με τη διαφορά ότι θα ειδοποιηθείτε αν επιχειρήσετε να τοποθετήσετε περισσότερα στοιχεία στη στοίβα ή αν επιχειρήσετε να εξαγάγετε στοιχεία από μια κενή στοίβα. Η λίστα του προγράμματος STAKEN είναι η εξής: // staken.cpp // υπερφόρτωση συναρτήσεων στη βασική και τις παράγωγες κλάσεις #include <iostream> using namespace std; #include <process.h> //για την exit() //////////////////////////////////////////////////////////////// class Stack protected: //ΠΡΟΣΟΧΗ: δεν μπορεί να είναι ιδιωτικά enum MAX = 3 }; //μέγεθος του πίνακα της στοίβας int st[max]; //στοίβα: πίνακας ακεραίων int top; //αριθμοδείκτης της κορυφής της στοίβας public: Stack() //συνάρτηση δόμησης top = -1; } void push(int var) //τοποθέτηση αριθμού στη στοίβα st[++top] = var; } int pop() //εξαγωγή αριθμού από τη στοίβα
Κληρονομικότητα 409 return st[top--]; } }; //////////////////////////////////////////////////////////////// class Stack2 : public Stack public: void push(int var) //τοποθέτηση αριθμού στη στοίβα if(top >= MAX-1) //σφάλμα αν η στοίβα είναι πλήρης cout << "\nerror: stack is full"; exit(1); } Stack::push(var); //κλήση της push() της κλάσης Stack } int pop() //εξαγωγή αριθμού από τη στοίβα if(top < 0) //σφάλμα αν η στοίβα είναι κενή cout << "\nerror: stack is empty\n"; exit(1); } return Stack::pop(); //κλήση της pop() της κλάσης Stack } }; //////////////////////////////////////////////////////////////// int main() Stack2 s1; s1.push(11); s1.push(22); s1.push(33); //τοποθέτηση μερικών τιμών στη στοίβα cout << endl << s1.pop(); cout << endl << s1.pop(); cout << endl << s1.pop(); cout << endl << s1.pop(); cout << endl; return 0; } //εξαγωγή μερικών τιμών από τη στοίβα //ωπ, εξαγάγαμε πάρα πολλά Σε αυτό το πρόγραμμα, η κλάση Stack είναι ακριβώς ίδια όπως και στο πρόγραμμα STAKARAY, με τη διαφορά ότι τα μέλη δεδομένων είναι τώρα προστατευμένα (protected). Ποια συνάρτηση χρησιμοποιείται; Η κλάση Stack2 περιέχει δύο συναρτήσεις, τις push() και pop(). Αυτές οι συναρτήσεις έχουν τα ίδια ονόματα και τους ίδιους τύπους ορισμάτων και επιστρεφόμενων τιμών όπως και οι συναρτήσεις της Stack. Όταν τις καλούμε από τη main(), σε προτάσεις όπως η επόμενη s1.push(11);
410 Κεφάλαιο 9 πώς καταλαβαίνει ο μεταγλωττιστής ποια από τις δύο συναρτήσεις push() θα χρησιμοποιήσει; Ο κανόνας είναι ο εξής: όταν υπάρχει η ίδια συνάρτηση και στη βασική και στην παράγωγη κλάση, τότε εκτελείται η συνάρτηση της παράγωγης κλάσης. (Αυτό ισχύει για τα αντικείμενα της παράγωγης κλάσης. Τα αντικείμενα της βασικής κλάσης δεν γνωρίζουν τίποτε για την παράγωγη κλάση και χρησιμοποιούν πάντοτε τις συναρτήσεις της βασικής κλάσης.) Στην περίπτωση αυτή, λέμε ότι η συνάρτηση της παράγωγης κλάσης υποσκελίζει (override) τη συνάρτηση της βασικής κλάσης. Έτσι, στην προηγούμενη πρόταση, αφού το s1 είναι αντικείμενο της κλάσης Stack2, θα εκτελεστεί η συνάρτηση push() της κλάσης Stack2 και όχι αυτή της Stack. Η συνάρτηση push() της Stack2 ελέγχει αν η στοίβα είναι πλήρης. Αν είναι, εμφανίζει ένα μήνυμα σφάλματος και προκαλεί την έξοδο από το πρόγραμμα. Αν δεν είναι, καλεί τη συνάρτηση push()της κλάσης Stack. Παρόμοια, η συνάρτηση pop() της Stack2 ελέγχει αν η στοίβα είναι κενή. Αν είναι. εμφανίζει ένα μήνυμα σφάλματος και τερματίζει το πρόγραμμα, διαφορετικά καλεί τη συνάρτηση pop()της κλάσης Stack. Στη main() τοποθετούμε (push) τρία στοιχεία στη στοίβα, αλλά εξάγουμε (pop) τέσσερα. Η τελευταία εξαγωγή στοιχείου παράγει ένα μήνυμα σφάλματος 33 22 11 Error: stack is empty και τερματίζει το πρόγραμμα. Επίλυση εμβέλειας σε συναρτήσεις που έχουν υποστεί υποσκέλιση Πώς γίνεται οι push() και pop() της Stack2 να προσπελάζουν τις push() και pop() της κλάσης Stack; Χρησιμοποιούν τον τελεστή επίλυσης εμβέλειας :: στις αντίστοιχες εντολές: Stack::push(var); και return Stack::pop(); Αυτές οι εντολές ορίζουν ότι θα κληθούν οι συναρτήσεις push() και pop() της Stack. Χωρίς τον τελεστή επίλυσης εμβέλειας, ο μεταγλωττιστής θα νόμιζε ότι οι συναρτήσεις push() και pop() της Stack2 καλούν τον εαυτό τους, που σε αυτή την περίπτωση θα οδηγούσε σε αστοχία του προγράμματος. Χρησιμοποιώντας τον τελεστή επίλυσης εμβέλειας, μπορείτε να καθορίσετε επακριβώς ποιας κλάσης είναι μέλος η συνάρτηση. Κληρονομικότητα στην κλάση Αγγλικών μονάδων μέτρησης Distance Θα δούμε τώρα ένα κάπως πιο σύνθετο παράδειγμα κληρονομικότητας. Μέχρι τώρα σε αυτό το βιβλίο, τα διάφορα προγράμματα που χρησιμοποιούσαν την κλάση Αγγλικών μονάδων μέτρησης Distance θεωρούσαν ότι οι αποστάσεις που θα αναπαριστούσαν ήταν πάντοτε θετικές. Συνήθως αυτή είναι άλλωστε η κατάσταση στα αρχιτεκτονικά σχέδια.
Κληρονομικότητα 411 Ωστόσο, αν για παράδειγμα μετρούσαμε τη στάθμη των υδάτων στον Ειρηνικό Ωκεανό καθώς μεταβάλλεται με την παλίρροια, θα θέλαμε να αναπαραστήσουμε και αρνητικά μεγέθη σε πόδια και ίντσες. (Η στάθμη της παλίρροιας κάτω από τη μέση κατώτατη στάθμη των υδάτων λέγεται αρνητική παλίρροια, και επιτρέπει στους αλιείς αχιβάδων να εκμεταλλευθούν μεγαλύτερη έκταση της αποστραγγισμένης ακτής.) Ας παραγάγουμε μια νέα κλάση από την Distance. Αυτή η κλάση θα προσθέσει ένα μόνο στοιχείο δεδομένων στις μετρήσεις σε πόδια και ίντσες: ένα πρόσημο που μπορεί να είναι θετικό ή αρνητικό. Αφού προσθέσουμε το πρόσημο, θα χρειαστεί να αλλάξουμε και τις συναρτήσεις-μέλη ώστε να μπορούν να λειτουργήσουν με προσημασμένες αποστάσεις. Ακολουθεί η λίστα του προγράμματος ENGLEN: // englen.cpp // κληρονομικότητα με αποστάσεις σε Αγγλικές μονάδες μέτρησης #include <iostream> using namespace std; enum posneg pos, neg }; //για το πρόσημο στη DistSign //////////////////////////////////////////////////////////////// class Distance //κλάση Distance protected: //ΣΗΜΕΙΩΣΗ: δεν μπορεί να είναι ιδιωτικά int feet; float inches; public: //συνάρτηση δόμησης χωρίς ορίσματα Distance() : feet(0), inches(0.0) } //συνάρτηση δόμησης 2 ορισμάτων Distance(int ft, float in) : feet(ft), inches(in) } void getdist() //λήψη μήκους από το χρήστη cout << "\nenter feet: "; cin >> feet; cout << "Enter inches: "; cin >> inches; } void showdist() const //εμφάνιση απόστασης cout << feet << "\'-" << inches << '\"'; } }; //////////////////////////////////////////////////////////////// class DistSign : public Distance //προσθήκη προσήμου στη Distance private: posneg sign; //το πρόσημο, ή θετικό ή αρνητικό public: //συνάρτηση δόμησης χωρίς ορίσματα DistSign() : Distance() //κλήση συνάρτησης δόμησης της βασικής κλάσης sign = pos; } //ορισμός προσήμου σε +