Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε

Είσοδος & Έξοδος στη C++ Ι

Παρόμοιες παρουσιάσεις


Παρουσίαση με θέμα: "Είσοδος & Έξοδος στη C++ Ι"— Μεταγράφημα παρουσίασης:

1 Είσοδος & Έξοδος στη C++ Ι
#include <iostream.h> void main() { cout << “Hello, World“ << endl; } Το αρχείο iostream.h πρέπει να περιλαμβάνεται σε κάθε πηγαίο πρόγραμμα που χρησιμοποιεί λειτουργίες ροής εισόδου – εξόδου. Αυτό το αρχείο κεφαλίδας ορίζει μεταξύ άλλων το αντικείμενο cout που αναπαριστά την τυπική έξοδο. Κάθε δεδομένο που στέλνεται στο cout εμφανίζεται στο τερματικό ή στέλνεται σε αρχείο στην περίπτωση ανακατεύθυνσης της τυπικής εξόδου. Στέλνοντας στη ροή εξόδου το αντικείμενο endl τυπώνεται ο χαρακτήρας νέας γραμμής και εκκενώνεται η προσωρινή περιοχή αποθήκευσης της ροής εξόδου (stream buffer). Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

2 Είσοδος & Έξοδος στη C++ ΙΙ
Για να σταλούν δεδομένα στη ροή εξόδου χρησιμοποιείται ο τελεστής <<. Μπορούμε να έχουμε αλυσίδα λειτουργιών εξόδου στην ίδια ροή: cout << “The value of pi is approx. “ << << endl; Μπορούν τα δεδομένα αντί για την τυπική έξοδο να σταλούν σε ένα αρχείο. #include <fstream.h> ofstream os(“output.dat”); os << “The value of pi is approx. “ << << endl; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

3 Είσοδος & Έξοδος στη C++ ΙΙΙ
Το αρχείο iostream.h ορίζει επίσης την κλάση istream και ένα αντικείμενο cin για ανάγνωση από την τυπική είσοδο (είτε πληκτρολόγιο ή από ανακατευθυνόμενο αρχείο). Ο τελεστής >> χρησιμοποιείται για την ανάγνωση δεδομένων. double x; cin >> x; Για κάθε δεδομένο που διαβάζεται με τον τελεστή >> αγνοούνται τα κενά που προηγούνται. Αν θέλουμε να διαβάσουμε ένα μόνο χαρακτήρα χωρίς να αγνοήσουμε την πιθανότητα του κενού, τότε καλούμε την get. char ch = cin.get(); Μπορούμε αν θέλουμε να «ανιχνεύσουμε» τον επόμενο χαρακτήρα χωρίς να τον καταναλώσουμε από τη ροή εισόδου: Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

4 Είσοδος & Έξοδος στη C++ ΙV
double x; char aChar; char ch = cin.peek(); if (isdigit(ch)) cin >> x; else cin >> aChar; Συνάρτηση που ελέγχει αν υπάρχει αποτυχία στην ανάγνωση δεδομένου από τη ροή εισόδου (α) λόγω ασυμφωνίας τύπων ή (β) λόγω τέλους αρχείου:  Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

5 Είσοδος & Έξοδος στη C++ V
Στο παρακάτω πρόγραμμα το loop τερματίζει όταν διαβαστεί συμβολοσειρά που δεν είναι αριθμός ή στο τέλος αρχείου. double t = 0; int n = 0; while (!cin.fail()) { double x; cin >> x; if (!cin.fail()) { t += x; n++; } } if (n > 0) cout << “average: “ << t/n <<endl; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

6 Είσοδος & Έξοδος στη C++ VΙ
Ανάγνωση από αρχείο: #include <fstream.h> ifstream is(“input.dat”); is >> x; if (is.fail()) ; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

7 Η συνάρτηση main πρωτότυπο της συνάρτησης main:
void main (int argc, char* argv[]); ο αριθμός των παραμέτρων πίνακας δεικτών χαρα- κατά την κλήση του προγ/τος κτήρα που αντιστοιχούν στις συμβολοσειρές των παραμέτρων του προγ/τος Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

8 Προγραμματίζετε με assertions!!
H macro assert ορίζεται στο assert.h και επιτρέπει των ορισμό συνθηκών που ο έλεγχός τους έχει ως αποτέλεσμα τον εντοπισμό του αριθμού γραμμής όπου παρουσιάζεται κάποιο λάθος χρόνου εκτέλεσης. //#define NDEBUG #include <assert.h> #include <math.h> // . . . y = f(x); #ifndef NDEBUG assert (y>=0); #endif z = sqrt(y); Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

9 Ορισμός Κλάσης (Τάξης) Ι
ΚΑΛΗ ΠΡΑΚΤΙΚΗ ΟΡΙΣΜΟΥ ΚΛΑΣΕΩΝ (Η C++ ΔΙΝΕΙ ΚΑΙ ΑΛΛΕΣ ΔΥΝΑΤΟΤΗΤΕΣ) ΔΗΛΩΝΕΤΕ ΤΙΣ ΛΕΙΤΟΥΡΓΙΕΣ (ΣΥΝΑΡΤΗΣΕΙΣ) ΣΤΟ PUBLIC ΜΕΡΟΣ ΤΟΥ ΟΡΙΣΜΟΥ ΤΗΣ ΚΛΑΣΗΣ ΔΗΛΩΝΕΤΕ ΤΑ ΠΕΔΙΑ ΔΕΔΟΜΕΝΩΝ ΣΤΟ PRIVATE ΜΕΡΟΣ ΤΟΥ ΟΡΙΣΜΟΥ ΤΗΣ ΚΛΑΣΗΣ ΓΡΑΦΕΤΕ ΤΗΝ ΥΛΟΠΟΙΗΣΗ ΤΩΝ ΛΕΙΤΟΥΡΓΙΩΝ ΑΜΕΣΩΣ ΜΕΤΑ ΤΟΝ ΟΡΙΣΜΟ ΤΗΣ ΚΛΑΣΗΣ ΠΑΡΑΔΕΙΓΜΑ οι λειτουργίες αυτές δηλώνονται class Mailbox ως public γιατί καλούνται { για αντικείμενα mailbox public: οπουδήποτε μέσα στο πρόγραμμα void add(Message); Message get_current(); void delete_current(); // . . . } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

10 Ορισμός Κλάσης (Τάξης) ΙΙ
ΣΤΗ C++ TA ANTIKEIMENA EINAI ΑΠΛΕΣ ΜΕΤΑΒΛΗΤΕΣ ΠΟΥ Ο ΤΥΠΟΣ ΤΟΥΣ ΕΙΝΑΙ Η ΚΛΑΣΗ ΑΠΟ ΤΗΝ ΟΠΟΙΑ ΕΧΟΥΝ ΔΗΜΙΟΥΡΓΗΘΕΙ κώδικας που αφαιρεί ένα ΠΑΡΑΔΕΙΓΜΑ μήνυμα από ένα mailbox Mailbox mbox; και στη συνέχεια το χρη- Message msg; σιμοποιεί msg = mbox.get_current(); msg.play(); ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΜΠΟΡΕΙ ΝΑ ΚΑΛΟΥΝΤΑΙ ΜΕ ΠΑΡΑΜΕΤΡΟΥΣ ΠΑΡΑΔΕΙΓΜΑ mbox.add(msg); Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

11 Ορισμός Κλάσης (Τάξης) ΙΙI
ΚΛΗΣΗ ΛΕΙΤΟΥΡΓΙΑΣ ΓΙΑ ΕΝΑ ΑΝΤΙΚΕΙΜΕΝΟ–ΣΥΝΤΑΞΗ: αντικείμενο.λειτουργία(παράμετροι); ΔΗΛΩΣΗ ΠΕΔΙΩΝ ΔΕΔΟΜΕΝΩΝ ΣΤΗΝ ΚΛΑΣΗ: ΠΑΡΑΔΕΙΓΜΑ class Date δηλώνονται στο private μέρος του { ορισμού της κλάσης για να διασφαλι- public: στεί ότι θα είναι προσβάσιμες μόνο // από τις λειτουργίες της κλάσης Date private: και από πουθενά αλλού int _day; λέμε ότι ο ορισμός της κλάσης int _month; δίνει μια μη ολοκληρωμένη πε- int _year; ριγραφή της κατάστασης αντικει } μένων Στην πραγματικότητα το σύνολο των σωστών καταστάσεων των αντικειμένων της Date είναι ένα υποσύνολο του συνόλου των καταστάσεων που περιγράφεται στον ορισμό της κλάσης (πρέπει π.χ. _day <= 31) Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

12 Ορισμός Κλάσης (Τάξης) ΙV
Η κλάση Date είναι παρόμοια με τον ορισμό της στη C ως structure struct { int day; int month; int year; }; Παρόλα αυτά τα structures στη C δεν παρέχουν τη δυνατότητα ελέγχου πρόσβασης στα δεδομένα. Αυτά μπορούν να αλλάξουν από οπουδήποτε μέσα στο πρόγραμμα: struct date birthday = {31, 3, 1961}; // . . . birthday.day = birthday.day + 1; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

13 Ορισμός Κλάσης (Τάξης) V
Ο αντίστοιχος κώδικας στη C++ απλά δεν θα μεταφραζόταν: Date birthday; birthday._day = birthday._day + 1; //ERROR Αλλαγή ημερομηνίας μπορεί να γίνει στη C++ μόνο με κλήση της κατάλληλης λειτουργίας που έχει γράψει ο συγγραφέας της κλάσης Date. Μπορεί δηλαδή να γίνει μόνο με ελεγχόμενο τρόπο. Πως γράφουμε την υλοποίηση λειτουργιών; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

14 Ορισμός Κλάσης (Τάξης) VΙ
Έστω ότι θέλουμε να αλλάξουμε την τιμή μιας ημερομηνίας b με μία άλλη που αντιστοιχεί σε 30 ημέρες μετά. Αυτό γίνεται με την κλήση της b.advance(30); αλλά προφανώς πρέπει να έχει δηλωθεί και η κατάλληλη συνάρτηση στον ορισμό της κλάσης class Date { public: void advance(int nday); // . . . }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

15 Ορισμός Κλάσης (Τάξης) VΙI
και στη συνέχεια ακολουθεί η υλοποίηση της συνάρτησης τελεστής εμβέλειας void Date::advance(int nday) {//μετατροπή σε Ιουλιανή ημερομηνία long j = dat2jul(_day, _month, _year); //πρόσθεση n ημερών j += nday; //μετατροπή από Ιουλιανή ημερομηνία jul2dat(j, _day, _month, _year); } Ο τελεστής εμβέλειας χρησιμοποιείται για να καθορίσει σε ποια κλάση ανήκει η λειτουργία advance Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

16 Ορισμός Κλάσης (Τάξης) VΙIΙ
ΤΟ ΣΗΜΑΝΤΙΚΟ ΔΕΝ ΕΙΝΑΙ ΟΙ ΛΕΠΤΟΜΕΡΕΙΕΣ ΤΗΣ ΥΛΟΠΟΙΗΣΗΣ ΑΛΛΑ ΤΟ ΓΕΓΟΝΟΣ ΟΤΙ ΜΕ ΤΗΝ ΚΛΗΣΗ ΤΗΣ Η ΣΥΝΑΡΤΗΣΗ advance ΑΛΛΑΖΕΙ ΤΙΣ ΤΙΜΕΣ ΤΩΝ _day, _month, _year. ΠΟΙΕΣ ΤΙΜΕΣ ΟΜΩΣ ΤΕΛΙΚΑ ΑΛΛΑΖΕΙ ΑΝ ΛΑΒΟΥΜΕ ΥΠΟΨΗ ΟΤΙ ΜΠΟΡΕΙ ΝΑ ΥΠΑΡΧΟΥΝ ΠΟΛΛΑ ΑΝΤΙΚΕΙΜΕΝΑ Date; Η ΣΥΝΑΡΤΗΣΗ ΑΛΛΑΖΕΙ ΤΙΣ ΤΙΜΕΣ ΠΟΥ ΑΝΗΚΟΥΝ ΣΤΟ ΑΝΤΙΚΕΙΜΕΝΟ ΓΙΑ ΤΟ ΟΠΟΙΟ ΚΑΛΕΙΤΑΙ Η ΛΕΙΤΟΥΡΓΙΑ: b.advance(30); ΣΤΗ C++ ΠΑΝΤΑ ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΕΦΑΡΜΟΖΟΝΤΑΙ ΣΕ ΚΑΠΟΙΟ ΑΝΤΙΚΕΙΜΕΝΟ ΠΟΥ ΟΥΣΙΣΤΙΚΑ ΑΠΟΤΕΛΕΙ ΤΗΝ ΥΠΟΝΟΟΥΜΕΝΗ ΠΑΡΑΜΕΤΡΟ ΤΗΣ ΛΕΙΤΟΥΡΓΙΑΣ. ΜΙΑ ΛΕΙΤΟΥΡΓΙΑ ΟΜΩΣ ΜΠΟΡΕΙ ΝΑ ΕΧΕΙ ΚΑΙ ΦΑΝΕΡΕΣ ΠΑΡΑΜΕΤΡΟΥΣ ΚΑΙ ΣΤΟ ΠΑΡΑΔΕΙΓΜΑ ΜΑΣ ΜΙΑ ΤΕΤΟΙΑ ΕΙΝΑΙ ΤΟ 30. ΣΤΗ C++ ΟΙ ΠΡΟΓΡΑΜΜΑΤΙΣΤΕΣ ΑΠΟΚΑΛΟΥΝ ΤΙΣ ΛΕΙΤΟΥΡΓΙΕΣ ΣΥΝΑΡΤΗΣΕΙΣ ΜΕΛΗ ΚΑΙ ΤΑ ΠΕΔΙΑ ΔΕΔΟΜΕΝΩΝ ΤΑ ΑΠΟΚΑΛΟΥΝ ΔΕΔΟΜΕΝΑ ΜΕΛΗ. Ο ΤΕΛΕΣΤΗΣ . ΧΡΗΣΙΜΟΠΟΙΕΙΤΑΙ ΓΙΑ ΠΡΟΣΒΑΣΗ ΕΙΤΕ ΣΤΙΣ ΣΥΝΑΡΤΗΣΕΙΣ ΜΕΛΗ ΕΙΤΕ ΣΤΑ ΔΕΔΟΜΕΝΑ ΜΕΛΗ. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

17 Ορισμός Κλάσης (Τάξης) ΙX
CONSTANT ΛΕΙΤΟΥΡΓΙΕΣ class Date { public: void advance(int); void print() const; // . . . }; σημαίνει ότι η συγκεκριμένη λειτουργία δεν αλλάζει την κατάσταση του αντικειμένου αλλά χρησιμοποιείται μόνο για πρόσβαση στα δεδομένα του void Date::print() const { // . . . } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

18 Ορισμός Κλάσης (Τάξης) X
ΣΥΝΑΡΤΗΣEIΣ ΑΠΛΗΣ ΠΡΟΣΠΕΛΑΣΗΣ ΠΕΔΙΩΝ ΔΕΔΟΜΕΝΩΝ class Date { public: int month() const; // . . . private: int _day; int _month; είναι συναρτήσεις που απλά int _year; επιστρέφουν την τιμή μιας // private μεταβλητής }; int Date::month() const { return _month; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

19 Ορισμός Κλάσης (Τάξης) XI
Για το συγγραφέα της κλάσης είναι κάποιος επιπλέον κόπος η συγγραφή τόσο του private δεδομένου μέλους όσο και της αντίστοιχης public συνάρτησης προσπέλασης της τιμής του. Για τον προγραμματιστή που χρησιμοποιεί την κλάση αντί να γράψει d._month γράφει d.month(). Το σημαντικό είναι ότι με αυτόν τον τρόπο η τιμή του δεδομένου μέλους έχει γίνει read only για το υπόλοιπο πρόγραμμα εκτός της κλάσης. Σε περίπτωση λάθους δε χρειάζεται να ερευνηθεί όλο το πρόγραμμα, αλλά μόνο οι συναρτήσεις της κλάσης. H C++ απαιτεί ένα πεδίο δεδομένων να έχει διαφορετικό όνομα από τη συνάρτηση προσπέλασης και γι΄ αυτό το λόγο χρησιμοποιείται το _. Γράφουμε δηλαδή _month για να διακρίνεται από το month(). Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

20 Ορισμός Κλάσης (Τάξης) XIΙ
ΣΥΝΑΡΤΗΣEIΣ ΓΙΑ ΑΛΛΑΓΗ ΠΕΔΙΩΝ ΔΕΔΟΜΕΝΩΝ void Date::set_month(int m) { if (1<=m && m<=12) _month = m; } Η συνάρτηση set_month δεν δηλώνεται ως const για να μπορεί να αλλάξει τιμή δεδομένου μέλους. Ονομάζεται συνάρτηση αλλαγής δεδομένου μέλους. Όταν χρησιμοποιούμε συναρτήσεις προσπέλασης και αλλαγής πεδίων δεδομένων, αν χρειαστεί να γίνουν αλλαγές στη δομή της κλάσης τότε ο αλλαγές αυτές επηρεάζουν μόνο τις δύο αυτές κατηγορίες συναρτήσεων, που εύκολα μπορούμε να εντοπίσουμε και δεν επηρεάζουν όλο το υπόλοιπο πρόγραμμα. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

21 Ορισμός Κλάσης (Τάξης) XIΙΙ
Οι συναρτήσεις αλλαγής πεδίων δεδομένων μπορούν να κάνουν έλεγχο για λάθος τιμές. Έτσι, θα μπορούσε για παράδειγμα στην κλήση b.set_month(13) να γίνει ο σχετικός έλεγχος για λάθος τιμή κάτι που δεν είναι εφικτό αν απλά γράφαμε b._month=13. Το παράδειγμα της κλάσης Date είναι μια καλή περίπτωση κλάσης στην οποία δεν είναι καλή ιδέα να ορίσουμε συναρτήσεις αλλαγής τιμής για κάθε ένα δεδομένο μέλος. Θεωρείστε τις κλήσεις: d.set_day(31); d.set_month(3); d.set_year(1961); Αν η προηγούμενη τιμή του d ήταν 1 Φεβρουαρίου, τότε η κλήση της πρώτης συνάρτησης θέτει στο αντικείμενο d την μη επιτρεπτή τιμή 31 Φεβρουαρίου. Αυτό σημαίνει ότι θα είχαμε καλύτερο έλεγχο της ορθότητας της κατάστασης των αντικειμένων Date αν η συνάρτηση αλλαγής ήταν μία και η κλήση της είχε τη μορφή d.set_date(31,3,1961); Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

22 Ορισμός Κλάσης (Τάξης) XIV
ΣΥΝΑΡΤΗΣEIΣ INLINE Αν μας ενδιαφέρει η ταχύτητα εκτέλεσης και η συνάρτηση που θέλουμε να γράψουμε είναι απλή τότε μπορούμε να τη δηλώσουμε ως inline π.χ. inline int square(int x) {return x*x;} Αυτή η δήλωση θα έχει ως αποτέλεσμα να μεταφράζει ο μεταγλωττιστής το square(expr) απευθείας σε expr*expr υπό τον όρο ότι πρόκειται για απλή έκφραση χωρίς παράπλευρες επιπτώσεις (π.χ. αλλαγή διεύθυνσης μνήμης). Αν η τιμή της έκφρασης είναι προσωρινά αποθηκευμένη στην temp τότε γίνεται απευθείας υπολογισμός της temp*temp. Έχουμε πιο αποδοτική εκτέλεση κώδικα. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

23 Ορισμός Κλάσης (Τάξης) XV
ΣΥΝΑΡΤΗΣEIΣ INLINE inline int Date::month() const {return _month;} Στην περίπτωση της παραπάνω δήλωσης η κλήση d.month() μεταφράζεται ως d._month, δηλαδή έχουμε απευθείας προσπέλαση στο πεδίο δεδομένων και έτσι παράγεται ποιο αποδοτικός κώδικας. Οι inline συναρτήσεις προστατεύουν την κατάσταση των αντικειμένων της κλάσης και ταυτόχρονα αποφεύγουν το overhead που συνεπάγεται οι πολλές κλήσεις συναρτήσεων. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

24 Ορισμός Κλάσης (Τάξης) XVΙ
ΣΥΝΑΡΤΗΣEIΣ INLINE Εναλλακτικός τρόπος ορισμού inline συναρτήσεων: class Date { public: int day() const {return _day;} // . . . private: int _day; }; Γενικά ο παραπάνω τρόπος ορισμού δεν θεωρείται μια καλή πρακτική. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

25 Ορισμός Κλάσης (Τάξης) XVΙΙ
PRIVATE ΣΥΝΑΡΤΗΣΕΙΣ Αρκετά συχνά χρειάζεται να δηλώσουμε private συναρτήσεις, που χρησιμοποιούνται π.χ. Στην υλοποίηση άλλων συναρτήσεων της κλάσης. class Date { public: // . . . private: Bool is_leap_year() const; int _day; }; Σίγουρα η υλοποίηση τέτοιων συναρτήσεων επηρεάζεται από τη δομή της κλάσης, αν όμως η δομή αυτή αλλάξει τότε οι private συναρτήσεις μπορεί και να μη χρειάζονται με τη νέα δομή ή πρέπει να ξαναγραφυτούν. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

26 Ορισμός Κλάσης (Τάξης) XVΙΙΙ
ΔΙΚΑΙΩΜΑ ΠΡΟΣΠΕΛΑΣΗΣ ΣΕ ΔΙΑΦΟΡΕΤΙΚΑ ΣΤΙΓΜΙΟΤΥΠΑ ΚΛΆΣΗΣ Στη C++ οι συναρτήσεις μέλη έχουν το δικαίωμα προσπέλασης στα ιδιωτικά μέλη κάθε αντικειμένου της κλάσης πράγμα που δεν ισχύει με όλες τις αντικειμενοστρεφείς γλώσσες προγραμματισμού. ΠΑΡΑΔΕΙΓΜΑ int Date::compare(Date b) const { int d = _year – b._year; if (d != 0) return d; d = _month – b._month; return _day – b.d_day; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

27 Δομητές (Constructors) I
Οι ΔΟΜΗΤΕΣ είναι ειδικές συναρτήσεις που ΚΑΛΟΥΝΤΑΙ ΑΥΤΟΜΑΤΑ κατά τη δημιουργία των αντικειμένων. Οι δομητές ΈΧΟΥΝ ΤΟ ΙΔΙΟ ΟΝΟΜΑ ΜΕ ΤΟ ΌΝΟΜΑ ΤΗΣ ΚΛΑΣΗΣ. class Date { public: Date(int d, int m, int y); // . . . }; Date::Date(int d, int m, int y) : _day(d), _month(m), _year(y) { } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

28 Δομητές (Constructors) II
Οι ΔΟΜΗΤΕΣ αρχικοποιούν τα πεδία δεδομένων του αντικειμένου. Ο συμβολισμός _day(d) είναι ισοδύναμος με την ανάθεση _day = d. Επειδή δεν απαιτείται κάτι άλλο από το δομητή το σώμα της συνάρτησης δεν περιέχει κάποια επιπλέον εντολή. Επίσης, οι δομητές δεν επιστρέφουν τιμές. Ο δομητής του παραδείγματος καλείται αυτόματα με τη δήλωση ενός αντικειμένου date: Date d(31, 3, 1961); ΔΙΑΦΟΡΕΣ ΔΟΜΗΤΩΝ ΑΠΟ ΤΙΣ ΥΠΟΛΟΙΠΕΣ ΣΥΝΑΡΤΗΣΕΙΣ Ο δομητής καλείται μόνο μία φορά κατά τη δημιουργία του αντικειμένου. Δεν μπορεί να κληθεί δεύτερη φορά για να «επαναρχικοποιήσει» το αντικείμενο Ο δομητής δεν μπορεί να κληθεί για ένα ήδη υπαρκτό αντικείμενο Ο δομητής δεν μπορεί να κληθεί φανερά όπως οι άλλες συναρτήσεις. Καλείται αυτόματα από τη C++ κατά τη δήλωση του αντικειμένου. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

29 Δομητές (Constructors) III
Μεγάλο πλεονέκτημα των ΔΟΜΗΤΩΝ είναι το ότι η ύπαρξή τους απομακρύνει την πιθανότητα μη αρχικοποιημένων μεταβλητών. Αν οριστεί ΔΟΜΗΤΗΣ αποκλείεται να οριστούν αντικείμενα τα οποία δεν θα αρχικοποιούνται. έτσι αν έχει δηλωθεί η Date(int, int, int) τότε η Date d; είναι λάθος Στις κλάσεις μπορεί να ορίζονται περισσότεροι από ένας δομητές αλλά μόνο ένας από αυτούς θα είναι ο πρoκαθορισμένος (default) Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

30 Δομητές (Constructors) IV
class Date { public: Date(); Date(int d, int m, int y=0); Date(String); // . . . }; Date::Date(int d, int m, int y) : _day(d), _month(m), _year(y) { if (_year == 0) _year = current year} Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

31 Ανώνυμα αντικείμενα Οι δομητές μπορούν να χρησιμοποιηθούν στη δημιουργία αντικειμένων που χρησιμοποιούνται σε υπολογισμούς και μετά δεν μας χρειάζονται: όπως θα γράφαμε στη C int y = x + 10; μπορούμε στη C++ να γράψουμε Date d = Date(31, 3, 1961).advance(x); Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

32 Δομητές για αντικείμενα μέλη αντικειμένων
Όλα τα αντικείμενα μιας κλάσης πρέπει να δημιουργηθούν ακόμη και αυτά που περιλαμβάνονται ως μέλη σε άλλες κλάσεις. Employee::Employee(String n, int hd, int hm, int hy) : _name(n), _hiredate(hd, hm, hy) { } Ο δομητής αυτός περνάει παραμέτρους στους δομητές των υποαντικειμένων. Τα πεδία που δεν αναφέρονται ρητά στη λίστα αρχικοποίησης όπως π.χ. το _address αρχικοποιούνται από τον προκαθορισμένο δομητή της κλάσης τους. Τα πεδία που ανήκουν σε κλάση που δεν έχει προκαθορισμένο δομητή πρέπει να αρχικοποιηθούν ρητά, αλλιώς ο μεταγλωττιστής θα βγάλει λάθος. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

33 Ο καλός προγραμματιστής . . .
Κάνει πάντα τα δεδομένα μέλη private Τα πεδία δεδομένων προτιμάει να είναι κλάσεις και όχι απλοί τύποι παράδειγμα κακής σχεδίασης: class Emplyee { public: // . . . private: String _lname; String _fname; char _middle_initial; String _street; String _city; String _state; long _zip; }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

34 Ο καλός προγραμματιστής . . .
παράδειγμα καλής σχεδίασης class Name { public: // . . . private: String _lname; String _fname; char _middle_initial; }; class Address class Employee { { public: public: // // . . . private: private: String _street; Name _name; String _city; Address _address; String _state; Date _hiredate; long _zip; // . . . // }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

35 Ο καλός προγραμματιστής . . .
Δεν είναι απαραίτητες συναρτήσεις προσπέλασης και αλλαγής τιμής για όλα τα πεδία. Να δηλώνονται οι συναρτήσεις προσπέλασης ως const. Να υιοθετείται ένα standard στυλ ορισμού κλάσεων για να επιτυγχάνεται ικανοποιητική αναγνωσιμότητα. Π.χ. βάζουμε πρώτα το public μέρος, μετά το private μέρος, στο public μέρος αναφέρουμε πρώτα τους δομητές, μετά τις συναρτήσεις αλλαγής τιμής και τέλος τις συναρτήσεις προσπέλασης τιμής και στο private μέρος βάσουμε πρώτα τις συναρτήσεις και μετά τα δεδομένα μέλη Δεν παραλείπει ποτέ να δηλώσει έναν προκαθορισμένο δομητή Δημιουργεί όλα τα υποαντικείμενα πριν από το σώμα του δομητή. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

36 Κληρονομικότητα Ι Παράδειγμα class Point { public:
Point(double x=0, double y=0); void move(int dx, int dy); void scale(Point center, double scalefactor); double x() const; double y() const; void plot(GraphicsContext&) const; void print(ostream&) const; // . . . private: double _x; double _y;} Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

37 Κληρονομικότητα ΙI class Polygon { public: Polygon(int);
void set_vertex(int i, Point p); void move(double dx, double dy); void scale(Point center, double s); void plot(GraphicsContext& gc) const; Point vertex(int) const; // . . . private: Array<Point> _vertex; }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

38 Κληρονομικότητα ΙII class Triangle : public Polygon { public:
Triangle(Point, Point, Point); }; βασική κλάση από την οποία κληρονομεί παράγωγη κλάση Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

39 Κληρονομικότητα ΙII Triangle::Triangle(Point a, Point b, Point c) : Polygon(3) { set_vertex(1,a); set_vertex(2, b); set_vertex(3, c); }; Η παράγωγη κλάση μπορεί να κληρονομήσει τη συνάρτηση B::f() χωρίς αλλαγή. Η παράγωγη κλάση μπορεί να αντικαταστήσει την B::f() με τη συνάρτηση D::f() που θα κάνει μια διαφορετική λειτουργία. Η παράγωγη κλάση μπορεί να επεκτείνει την B::f() με την D::f() να καλεί την B::f() και να κάνει και κάποιες επιπλέον λειτουργίες. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

40 Κληρονομικότητα ΙV Επέκταση συνάρτησης – παράδειγμα
void ScalableText::scale(Point center, double s) { Text::scale(center, s); _size = _size * s; } Αντικατάσταση συνάρτησης - παράδειγμα class Triangle : public Polygon { public: Triangle(Point, Point, Point); void print(ostream& os) const; }; void Triangle::print(ostream& os) const { Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

41 Πολλαπλή κληρονομικότητα Ι
//Παράδειγμα πολλαπλής κληρονομικότητας #include <graphics.h> class Circle { int X_center, Y_center; int radius; public: Circle(int x, int y, int r=20){ X_center=x; Y_center=y; radius=r; } void SetCenter(int x, int y){ void SetRadius(int r){ radius=r;} int GetRadius(){ return radius; } int GetXCenter(){ return X_center; int GetYCenter(){ υπερφόρτωση return Y_center; τελεστή ++ void Paint(){ circle(X_center, Y_center, radius); void operator++(){ radius++; X_center++; Y_center++; }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

42 Πολλαπλή κληρονομικότητα ΙΙ
//Παράδειγμα (ΣΥΝΕΧΕΙΑ) enum COLORS { BLACK, BLUE, GREEN, CYAN, ..., WHITE}; class Color { COLORS color; public: Color(COLORS c=BLACK){ color=c; } COLORS GetCol(){ return color; void SetCol(COLORS c){ COLORS operator++(){ color= COLORS((color+1)%getmaxcolor()); }; class ColorCircle:public Circle, Color { public: ColorCircle(int x, int y, int r=20, COLORS c=RED): Circle(x, y, r), Color(c); {} void Paint(){ setcolor(GetCol()); Circle::Paint(); } }; Circle Color ColorCircle Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

43 Πολλαπλή κληρονομικότητα ΙΙI
A A class A; class B1: public A; class B2: public A; class C: public B1, public B2; B1 B2 Ο μεταγλωττιστής δημιουργεί δύο αντίγραφα της κλάσης A. Πρόβλημα: αν στην κλάση A υπάρχει ο ορισμός public: int data; τότε για να προσπελασθεί η data μέσα από τη C θα πρέπει να αναφερθεί ως B1::data, αλλιώς η έκφραση θα είναι ασαφής C Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

44 Πολλαπλή κληρονομικότητα ΙV
A class A; class B3: virtual public A; class B4: virtual public A; class C: public B3, public B4; B3 B4 ΠΑΡΑΔΕΙΓΜΑΤΑ: D Person Person Person Professor Author Man Woman Professor-Author Married Couple Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

45 Σχέση κληρονομικότητας
Ένα αντικείμενο κλάσης A έχει ένα αντικείμενο κλάσης Β. Το αντικείμενο κλάσης Β δηλώνεται ως μέλος της κλάσης Α. Ένα αντικείμενο κλάσης Β είναι κλάσης Α. Η κλάση Β κληρονομεί από την κλάση Α. ΠΑΡΑΔΕΙΓΜΑΤΑ: Ένα τετράγωνο είναι παραλληλόγραμμο. Η γενέθλια ημερομηνία είναι μία ημερομηνία. Ένα άτομο έχει μία διεύθυνση και μία γενέθλια ημερομηνία. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

46 Κανόνες συμβατότητας τύπων I
Δύο μεταβλητές είναι συμβατές κατά τύπο αν το όνομα των τύπων και των δύο είναι το ίδιο. Εξαίρεση: ένας δείκτης σε υπερκλάση είναι συμβατός με οποιοδήποτε δείκτη σε κλάση που προέρχεται από αυτή (υποκλάση). #include <iostream.h> void main() { typedef struct { char first; char second;} twoChars; twoChars state1; twoChars state2; state1.first=‘M’; state1.second=‘N’; state2=state1; //σωστό: οι μεταβλητές είναι του ίδιου τύπου state2.second++; cout << “Original structure: “ << state1.first << state1.second << endl << “Changed structure: ” << state2.first << state2.second << endl; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

47 Κανόνες συμβατότητας τύπων IΙ
#include <iostream.h> void main() { struct { char first; char second;} state1; char second;} state2; state1.first=‘M’; state1.second=‘N’; state2=state1; //λάθος: οι μεταβλητές δεν είναι του ίδιου τύπου state2.second++; cout << “Original structure: “ << state1.first << state1.second << endl << “Changed structure: ” << state2.first << state2.second << endl; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

48 Κανόνες συμβατότητας τύπων IΙΙ
#include <iostream.h> void main() { struct { char first; char second;} state1, state2; state1.first=‘M’; state1.second=‘N’; state2=state1; //σωστό: οι μεταβλητές είναι του ίδιου τύπου state2.second++; cout << “Original structure: “ << state1.first << state1.second << endl << “Changed structure: ” << state2.first << state2.second << endl; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

49 Κανόνες συμβατότητας τύπων IV
class TwoChars { char first; char second; public: TwoChars(char f,char s){ first=f; second=s; } }; class StateCode { StateCode(char f,char s){ void main() { StateCode* pState1; TwoChars* pState2; pState1=new StateCode(‘C’, ‘A’); pState2=new TwoChars(‘H’, ‘I’); pState2=pState1; // ΛΑΘΟΣ pState1=pState2; // ΛΑΘΟΣ } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

50 Κανόνες συμβατότητας τύπων V
class TwoChars { char first; char second; public: TwoChars(char f,char s){ first=f; second=s; } }; class StateCode:public TwoChars { StateCode(char f,char s): TwoChars(f, s) {} void main() { StateCode* pState1; TwoChars* pState2; pState1=new StateCode(‘C’, ‘A’); pState2=new TwoChars(‘H’, ‘I’); pState2=pState1; // ΣΩΣΤΟ pState1=pState2; // ΛΑΘΟΣ pState2=new StateCode(‘A’, ‘R’); } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

51 Δυναμική αντιστοίχιση (dynamic binding) - Παράδειγμα
#include <iostream.h> class Person { protected: char* name; public: Person(char* n){ name=n; } virtual void Print() { cout <<“My name is” <<name<<endl; }; class Foreigner:public Person { Foreigner(char* n):Person(n) { void Print() { cout <<“Il mio nome e” <<name<<endl; } }; void main() { Person* man; Person* woman;//ίδιος τύπος με την man man=new Person(“John”); woman=new Foreigner(“Paola”); //!!! cout <<“Prints the name of the man:” <<endl; man->Print(); //καλεί από την Person cout<<“Prints the name of the woman:” woman->Print(); //καλεί από τη Foreigner Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

52 Μετατροπή Δεδομένων Ι Τι γίνεται όταν έχουμε εντολή απόδοσης τιμής
var1=var2 και οι μεταβλητές που βρίσκονται αριστερά και δεξιά του τελεστή = είναι διαφορετικού τύπου; Αυτόματη μετατροπή: μετατροπές που γίνονται αυτόματα από το μεταγλωττιστή σύμφωνα με τους κανόνες μετατροπής δεδομένων της γλώσσας. Παραδείγματα: από int σε float, από float σε double κ.α. Κατ΄απαίτηση προσαρμογή: ο προγραμματιστής ορίζει τον τύπο στον οποίο θα γίνει η μετατροπή με τον τελεστή προσαρμογής (casting) Παράδειγμα μετατροπής μεταξύ βασικών τύπων: var1=int(var2); Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

53 Μετατροπή Δεδομένων ΙΙ
Μετατροπή μεταξύ δεδομένων βασικών τύπων και αντικειμένων (κλάσεων οριζόμενων από το χρήστη #include <iostream.h> const float MTF= ; class Distance { private: int feet; float inches; public: Distance() {feet=0; inches=0.0;} Distance(float meters) { float fltfeet=MTF*meters; feet=int(fltfeet); inches=12*(fltfeet-feet);} Distance(int ft,float in) { feet=ft; inches=in;} void getdist() { cout<<“\n Δώσε τα πόδια: ”; cin>>feet; cout<<“\n Δώσε τις ίντσες: ”; cin>>inches; } void showdist(){ cout<<feet<<“/’-”<<inches <<‘\”’;} operator float() { float fracfeet=inches/12; fracfeet+=float(feet); return fracfeet/MTF;} }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

54 Μετατροπή Δεδομένων ΙΙI
void main() { Distance dist1=2.35; //χρήση δομητή για τη μετατροπή //μέτρων σε Distance cout<<“\ndist1 =”; dist1.showdist(); dist1=1.00; //χρήση δομητή με ένα όρισμα cout<<“\ndist1=”; dist1.showdist(); Distance dist2(5,10.25); //χρήση δομητή με δύο ορίσματα float mtrs=float(dist2); //χρήση συνάρτησης μετατροπής για cout<<“\ndist2=“<<mtrs<<“ μέτρα”; //μετατροπή distance σε μέτρα mtrs=dist1; //έχουμε μετατροπή τύπου επίσης με cout<<“\ndist1=“<<mtrs<<“ μέτρα”; //χρήση της συνάρτησης μετατροπής } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

55 Μετατροπή Δεδομένων ΙV
Μετατροπή τύπου Μέλος στον Μέλος στον τύπο προέλευσης τύπο προορισμού Βασικός σε βασικό (από το μεταγλωττιστή) Βασικός σε κλάση ΔΕΝ ΥΠΑΡΧΕΙ Δομητής Κλάση σε βασικό Συνάρτηση μετατροπής ΔΕΝ ΥΠΑΡΧΕΙ Κλάση σε κλάση Συνάρτηση μετατροπής Δομητής Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

56 Έλεγχος ισοδυναμίας τύπων
Για να λειτουργεί χρειάζεται η κλάση να έχει τουλάχιστο μία virtual συνάρτηση. Shape* s; if (typeid(s) == typeid(Rectangle*)) // s points to a Rectangle object else // s points to an object of some other class Στις παλιότερες εκδόσεις της C++ χρειάζεται να δημιουργήσετε το δικό σας μηχανισμό ελέγχου ισοδυναμίας τύπων (συναρτήσεις που να επιστρέφουν κάποια ένδειξη του τύπου του αντικειμένου). Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

57 Ιεραρχίες κλάσεων Ι Στον αντικειμενοστρεφή προγραμματισμό χρησιμοποιούμε ιεραρχίες κλάσεων για: την περιγραφή μιας κατάταξης αντικειμένων σε κατηγορίες περιγράφουμε τα αντικείμενα σε σχέση με τις μεταξύ τους ομοιότητες και διαφορές αποφεύγοντας την πλήρη περιγραφή, όπου σε αρκετές περιπτώσεις θα υπήρχε επανάληψη κώδικα π.χ. κατηγορίες από τη βιολογία: κλάση ζώα, κλάση φυτά τη δημιουργία παραλλαγών μιας κλάσης, που διαφοροποιούνται σε σχέση με την υλοποίησή τους π.χ. ένας inkject printer μπορεί να κληρονομεί από την κλάση dot-matrix printer, αλλά αντί για ακίδες (αφαιρούνται από την υλοποίηση) διαθέτει μπεκ ψεκασμού την αφαίρεση (γενίκευση) των κοινών στοιχείων ενός αριθμού κλάσεων π.χ. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

58 Ιεραρχίες κλάσεων ΙΙ Αφαίρεση ή γενίκευση (abstraction)
Η περιγραφή ενός αριθμού αντικειμένων διαφορετικών κλάσεων από μία νέα κλάση που θα ενσωματώνει τα κοινά τους χαρακτηριστικά με ένα γενικό τρόπο. Όλα τα κοινά μέλη (δεδομένα, συναρτήσεις) θα περιγράφονται στη γενική κλάση όχημα. Η γενική κλάση που συνήθως αποτελείται από virtual συναρτήσεις παρέχει έτσι μία κοινή διασύνδεση και τη δυνατότητα ομοιόμορφης ονοματολογίας σε όλες τις υποκλάσεις. Φυσικά κάθε συνάρτηση της γενικής κλάσης μπορεί να επανακαθορισθεί στις υποκλάσεις όπως χρειάζεται. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

59 virtual int pure_function(int number)=0;
Ιεραρχίες κλάσεων ΙΙΙ Virtual συναρτήσεις χρησιμοποιήθηκαν και στο παράδειγμα με τη δυναμική αντιστοίχιση (dynamic binding), αλλά τώρα θα χρησιμοποιήσουμε καθαρά virtual συναρτήσεις (pure virtual functions). Η διαφορά είναι ότι οι καθαρά virtual συναρτήσεις δεν μπορούν να κληθούν για αντικείμενα της κλάσης στην οποία ορίζονται, αλλά μόνο για αντικείμενα κλάσεων όπου επανακαθορίζονται. virtual int pure_function(int number)=0; Μια κλάση που έχει τουλάχιστο μία καθαρά virtual συνάρτηση λέγεται κλάση αφαίρεσης (abstract class). Το χαρακτηριστικό των κλάσεων αφαίρεσης είναι ότι δεν μπορούμε παράγουμε αντικείμενα γι αυτές γιατί δεν το επιτρέπει η γλώσσα. Οι κλάσεις αφαίρεσης χρησιμεύουν μόνο ως βάση κληρονομικότητας για τις υποκλάσεις. Παρόλα αυτά οι κλάσεις αφαίρεσης μπορούν να χρησιμοποιούνται ως τύποι δείκτη που ενδεχομένως περνάνε ορίσματα μέσα σε συναρτήσεις. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

60 Ιεραρχίες κλάσεων ΙV class Printer { public: Printer() {} virtual void Method(char*buffer)=0; virtual int Dpi()=0; }; void function1(Printer pr); //ΛΑΘΟΣ: δεν επιτρέπεται void function2(Printer& pr); //ΣΩΣΤΟ void function3(Printer* pr); //ΣΩΣΤΟ Κάθε κλάση που κληρονομεί από μία κλάση αφαίρεσης και επανακαθορίζονται σε αυτήν όλες οι virtual συναρτήσεις λέγεται ειδική κλάση (concrete class). Τα αντικείμενα των ειδικών κλάσεων μπορούν είτε να χρησιμοποιούνται απευθείας είτε μέσω δείκτη σε κλάση αφαίρεσης, όπως στο παράδειγμα: Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

61 Ιεραρχίες κλάσεων V #include <stdio.h>
#include <iostream.h> class Printer { public: Printer(){} virtual void Method(char*buffer)=0; virtual int Dpi()=0; }; class DotMatrixPrinter:public Printer int nDots; DotMatrixPrinter(int n) { nDots=n; } void Method(char*buffer) { sprintf(buffer,”Dot matrix printer with %d pins”,nDots); int Dpi() { switch(nDots) { case 8: return 150; case 9: return 160; case 24: return 300; default: return 100; }} }; void main() { char description[40]; DotMatrixPrinter dmp(24);//Αντι dmp.Method(description); cout<<description << endl <<“DPI: ”<<dmp.Dpi()<<endl; Printer* pPri; //Δείκτ abstract pPri=new DotMatrixPrinter(9); pPri->Method(description); <<“DPI: ”<< pPri->Dpi() <<endl; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

62 Ιεραρχίες κλάσεων VΙ Οι κλάσεις αφαίρεσης είναι πολύ σημαντικές και στον πολυμορφισμό και τη δυναμική αντιστοίχιση. Γενικά είναι προτιμότερο να χρησιμοποιείται κληρονομικότητα από κλάσεις αφαίρεσης παρά από ειδικές κλάσεις. Μία ειδική κλάση ορίζει τη δικιά της αναπαράσταση δεδομένων, που βέβαια δεν πρέπει να δεσμεύει όλες τις υποκλάσεις. Αντίθετα στις κλάσεις αφαίρεσης συνήθως δεν περιλαμβάνουμε δεδομένα ως μέλη ή τουλάχιστο περιλαμβάνουμε μόνο μερικά από αυτά που συμβαίνει να υπάρχουν κοινά σε όλες τις υποκλάσεις. Έτσι, επιτρέπεται στις ειδικές κλάσεις να ορίζουν τα δικά τους ιδιωτικά (private) δεδομένα χωρίς κίνδυνο συγκρούσεων και χωρίς να κληρονομούν άχρηστα δεδομένα άλλων κλάσεων. Η δημιουργία νέων «αφαιρέσεων» δεν είναι απλή υπόθεση αλλά είναι μία τεχνική που πρέπει να καλλιεργήσει ο καλός προγραμματιστής. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

63 Ιεραρχίες κλάσεων VΙΙ ΣΥΝΤΑΓΕΣ ΓΙΑ ΤΗ ΔΗΜΙΟΥΡΓΙΑ ΕΝΟΣ «ΚΑΛΟΥ» ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΟΥΣ ΠΡΟΓΡ/ΤΟΣ - οι ιεραρχίες κλάσεων πρέπει να είναι περιορισμένες και σε κάποιο ύψος και όχι «ρηχές» και «απλωμένες» - η κλάση στη ρίζα και οι ενδιάμεσες κλάσεις (αν αυτό είναι εφικτό) πρέπει να είναι κλάσεις αφαίρεσης - οι κλάσεις μπορούν να είναι πιο γενικές αν γίνει κατορθωτό να περιορισθεί η χρήση δεδομένων ή η χρήση συγκεκριμένης αναπαράστασης δεδομένων. Αυτό γίνεται αν προσπελαύνονται τα δεδομένα με τη χρήση συναρτήσεων και όχι με απευθείας προσπέλαση. Έτσι, θα μπορεί αν χρειασθεί να αλλαχθεί η αναπαράσταση των δεδομένων με την αλλαγή των συναρτήσεων αφήνοντας ίδιο τον κώδικα όπου χρησιμοποιούνται. - οι κληρονομούσες κλάσεις πρέπει να είναι εξειδικεύσεις Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

64 Ιεραρχίες κλάσεων VΙΙΙ
ΜΙΑ «ΙΔΑΝΙΚΗ» ΙΕΡΑΡΧΙΑ ΚΛΑΣΕΩΝ ΚΛΆΣΗ ΑΦΑΙΡΕΣΗΣ ΕΙΔΙΚΗ ΚΛΑΣΗ Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

65 Ιεραρχίες κλάσεων ΙΧ ΠΑΡΑΔΕΙΓΜΑ ΕΠΕΚΤΑΣΗΣ ΜΙΑΣ ΙΕΡΑΡΧΙΑΣ ΚΛΑΣΕΩΝ
Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

66 Υπερφόρτωση Τελεστών Ι
Τελεστές στους οποίους μπορεί να γίνει υπερφόρτωση + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> <<= >>= == != <= >= && || > ->* [] () , Στους τελεστές + - * & μπορούμε να κάνουμε υπερφόρτωση τόσο στη μοναδιαία (ένας τελεσταίος) όσο και στην δυαδική τους μορφή (δύο τελεσταίοι) Παράδειγμα υπερφόρτωσης δυαδικής μορφής τελεστή Vector Vector::operator-(Vector b) const Παράδειγμα υπερφόρτωσης μοναδιαίας μορφής τελεστή Vector Vector::operator-() const { return Vector(-x(), -y()); } Χρήση δυαδικού τελεστή: a – b Xρήση μοναδιαίου τελεστή: -a Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

67 Υπερφόρτωση Τελεστών ΙI
Τελεστές στους οποίους δεν μπορεί να γίνει υπερφόρτωση . .* :: ?: Δεν μπορούμε να δημιουργήσουμε νέους τελεστές Μία συνάρτηση υπερφόρτωσης πρέπει να έχει τουλάχιστο ένα όρισμα (implicit ή explicit) τύπου κλάσης. Δεν μπορούμε να επαναπροσδιορίσουμε τελεστές που εφαρμόζονται μόνο σε αριθμητικούς τελεσταίους (int, double, . . .) ή μόνο σε δείκτες (char*, X*, . . .) Η προτεραιότητα και η προσεταιριστικότητα των υπερφορτωμένων τελεστών παραμένει η ίδια με αυτή που διαθέτουν στην προκαθορισμένη τους μορφή Παράδειγμα: Fraction Fraction::operator^(int) const; ΠΡΟΣΟΧΗ ΣΤΗΝ ΠΡΟΤΕΡΑΙΟΤΗΤΑ: το a^2+1 θα εκτελεστεί ως a^(2+1) Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

68 Υπερφόρτωση Τελεστών ΙII
Χρειάζεται ιδιαίτερη προσοχή στην υπερφόρτωση των τελεστών ++ και – και αυτό γιατί έχουμε και προθεματική μορφή (++x) και επιθεματική μορφή (x++) αυτών των τελεστών. Αν θέλουμε να καλύψουμε και τις δύο περιπτώσεις πρέπει να ορίσουμε δύο συναρτήσεις υπερφόρτωσης Fraction Fraction::operator++(); //prefix inc και Fraction Fraction::operator++(int); //postfix Στην περίπτωση της συνάρτησης υπερφόρτωσης για την επιθεματική εκδοχή το όρισμα int χρησιμοποιείται μόνο για να δείξει ότι πρόκειται για επιθεματική μορφή. Η τιμή που περνάει είναι πάντα 0. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

69 Υπερφόρτωση Τελεστών ΙV
class Fraction { public: Fraction(long n = 0, long d = 1); Fraction operator+(Fraction) const; // . . . private: long _num; long _den; }; Fraction::Fraction(long n, long d) : _num(n) _den(d) { } Fraction Fraction::operator+(Fraction b) const { return Fraction(_num * b._den + _den * b._num, _den * b._den); } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

70 Υπερφόρτωση Τελεστών V
Στο προηγούμενο παράδειγμα ο constructor Fraction f = 2; μετατρέπει έναν ακέραιο σε κλάσμα. Το ίδιο γίνεται αυτόματα στην αριθμητική έκφραση Fraction g = f + 2; // g = f.operator+(Fraction(2,1)); Χρειάζεται όμως ιδιαίτερη προσοχή γιατί δεν θα μπορούσαμε να χρησιμοποιήσουμε τη φαινομενικά ισοδύναμη έκφραση 2 + f Αυτό συμβαίνει γιατί ο compiler δεν μπορεί να εκτελέσει τη μετατροπή τύπου Fraction(2).operator+(f) Το υπονοούμενο όρισμα (implicit argument) χρησιμοποιείται αποκλειστικά για επιλογή κλάσης και δεν μπορεί να χρησιμοποιηθεί για να γίνει μετατροπή τύπου. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

71 Υπερφόρτωση Τελεστών VΙ
Όπως είδαμε στο προηγούμενο παράδειγμα οι constructors που δέχονται ένα μόνο όρισμα εξυπηρετούν και ως συναρτήσεις μετατροπής. Έτσι ο δομητής Fraction(int) μπορεί αυτόματα να μετατρέψει έναν integer σε fraction(με παρονομαστή 1) και βασικά για αυτόματη μετατροπή τύπων χρησιμοποιούνται τέτοιοι δομητές. Τι γίνεται όμως όταν: Ο τύπος στον οποίο θέλουμε να μετατρέψουμε δεν είναι κλάση; Ο τύπος στον οποίο θέλουμε να μετατρέψουμε είναι κλάση την οποία όμως δεν μπορούμε να αλλάξουμε; Ας θεωρήσουμε την μετατροπή από fractions σε αριθμούς κινητής υποδιαστολής. Επειδή ο τύπος double δεν είναι κλάση δεν μπορούμε να δηλώσουμε δομητή γι΄ αυτόν τον τύπο και έτσι δηλώνουμε έναν τελεστή μετατροπής τύπου Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

72 Υπερφόρτωση Τελεστών VΙΙ
ΠΑΡΑΔΕΙΓΜΑ class Fraction { public: // . . . operator double() const; }; Fraction::operator double() cnst { return (double) _num / (double) _den; } Fraction f(1, 4); y = sqrt(f); // μετατρέπεται σε y = sqrt(f.operator double()) Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

73 Γενίκευση (genericity) Ι
Ο λόγος που επιδιώκουμε τη δυνατότητα γενίκευσης μιας κλάσης (ή συνάρτησης) είναι το να μπορούμε να κάνουμε τον ορισμό της χωρίς να προσδιορίσουμε τον τύπο ενός ή περισσοτέρων μελών της (παράμετροι). Έτσι, θα μπορούμε να προσαρμόζουμε την κλάση (συνάρτηση) σε διαφορετικές περιπτώσεις χρήσης της χωρίς να επιβάλλεται να την ξαναγράψουμε. ΠΑΡΑΔΕΙΓΜΑ Πρέπει να γράψουμε τον κώδικα για μια ουρά ή μια στοίβα. Μπορούμε εύκολα να ορίσουμε μια ουρά ακεραίων, αλλά τι γίνεται όταν θέλουμε σε μία ουρά να χρησιμοποιούμε αντικείμενα τύπου date; Κάθε γλώσσα αντιμετωπίζει το παραπάνω πρόβλημα και με διαφορετικό τρόπο. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

74 Γενίκευση (genericity) ΙΙ
Στη Smalltalk όπου ο έλεγχος τύπων γίνεται σε χρόνο εκτέλεσης ο πολυμορφισμός δεν περιορίζεται στις ιεραρχίες των κλάσεων, αλλά κάθε κλάση μπορεί να αντικατασταθεί από μία άλλη. Έτσι, η γενίκευση γίνεται εύκολα. Στη C++ έχουμε έλεγχο τύπων σε χρόνο μεταγλώττισης. Υπάρχουν δύο τρόποι για την επίτευξη γενίκευσης: με χρήση κληρονομικότητας και πολυμορφισμού ορίζοντας κλάση, που χειρίζεται δείκτες σε αντικείμενα με χρήση παραμετροποιήσιμων τμημάτων κώδικα, των επονομαζόμενων templates Η πρώτη μέθοδος (βλ. παράδειγμα παρακάτω) συνοδεύεται από έναν κίνδυνο με την κατ΄ απαίτηση προσαρμογή (casting) που χρειάζεται στην ανάκτηση του τύπου των αντικειμένων που χειρίζεται η κλάση με τους δείκτες. Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

75 Γενίκευση (genericity) ΙΙΙ
class GenericObject {}; //stack.h class Stack { GenericObject* data[50]; int nElements; public: Stack() { nElements=0; } void Push(GenericObject* elem); GenericObject* Pop(); int Number(); int Empty(); }; #include “stack.h” //stack.cpp void Stack::Push(GenericObject* elem) { data[nElements]=elem; nElements++; GenericObject* Stack::Pop() { nElements--; return data[nElements]; } int Stack::Number() { return nElements; int Stack::Empty() { return (nElements==0); #include <iostream.h> //sta_use.cpp #include “stack.cpp” class IntObj:public GenericObject { public: int data; IntObj(int n) { data=n; }; Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

76 Γενίκευση (genericity) ΙV
void main() { Stack genericStack; //εισάγουμε νέα αντικείμενα στη στοίβα genericStack.Push(new IntObj(500)); genericStack.Push(new IntObj(1992)); genericStack.Push(new IntObj(33)); genericStack.Push(new IntObj(1024)); //τυπώνει τα στοιχεία της στοίβας καθώς την αδειάζει: //επειδή η συνάρτηση Pop επιστρέφει δείκτη σε //GenericObject, για να προσπελάσουμε μέλος του //αντικειμένου που βγάλαμε χρειάζεται type casting while(!genericStack.Empty()) cout<<((IntObj*)genericStack.Pop())->data<<endl; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

77 Γενίκευση (genericity) V
template<class T> //stack.h με template class Stack { T* data[50]; int nElements; public: Stack() { nElements=0; } void Push(T elem); T Pop(); int Number(); int Empty(); }; #include “stack.h” //stack.cpp template<class T> void Stack<T>::Push(T elem) { data[nElements]=elem; nElements++; template<class T> T Stack<T>::Pop() { nElements--; return data[nElements];} int Stack<T>::Number() { return nElements;} int Stack<T>::Empty() { return (nElements==0); } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017

78 Γενίκευση (genericity) VI
#include <iostream.h> //sta_use.cpp #include “stack.cpp” void main() { Stack <int> intStack; //εισάγουμε νέα αντικείμενα στη στοίβα intStack.Push(500); intStack.Push(1992); intStack.Push(33); intStack.Push(1024); while(!intStack.Empty()) cout<<intStack.Pop())<<endl; } Τμ. Πληροφορικής, Α.Π.Θ. Σάββατο, 8 Απριλίου 2017


Κατέβασμα ppt "Είσοδος & Έξοδος στη C++ Ι"

Παρόμοιες παρουσιάσεις


Διαφημίσεις Google