Προγραμματισμός Υπολογιστών με C++ ( 2012-13 ) 9η διάλεξη Ίων Ανδρουτσόπουλος http://www.aueb.gr/users/ion/ 1
Τι θα ακούσετε σήμερα Μέθοδοι με ορίσματα της ίδιας τάξης. Μέθοδοι που επιστρέφουν αντικείμενα κατά τιμή ή κατά αναφορά. Υπερφόρτωση τελεστών. Φίλιες συναρτήσεις. 2
Μέθοδοι με ορίσματα ίδιας τάξης Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; void add(const Point& p) { x += p.x; y+= p.y; ; Point p1(1, 2), p2(3, 4); p1.add(p2); p1.print(); // Τυπώνει «4,6». p2.print(); // Τυπώνει «3,4». 3
Επιστροφή αντικειμένου Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; Point plus(const Point& p) const; ; Point Point::plus(const Point& p) const { Point temp(x + p.x, y + p.y); // Δημιουργία νέου τοπικού Point. return temp; // Σε μεθόδους που επιστρέφουν αντικείμενο κατά // τιμή, σκεφτόμαστε την κλήση της μεθόδου (π.χ. p1.plus(p2)) ως // αντίγραφο του αντικειμένου που καθορίζει το return. Point p1(1, 2), p2(3, 4), p3; p3 = p1.plus(p2); // Δεν αλλάζουν τα p1 και p2. p1.print(); p2.print(); p3.print(); // Τυπώνουν «1,2», «3,4», «4,6». 4
Υπερφόρτωση του τελεστή + Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; Point operator+(const Point& p) const; ; Point Point::operator+(const Point& p) const { Point temp(x + p.x, y + p.y); // Ουσιαστικά ή ίδια μέθοδος return temp; // με την plus που είχα πριν. // Ή κατευθείαν «return Point(x + p.x, y + p.y);» χωρίς temp. Point p1(1, 2), p2(3, 4); Point p3 = p1 + p2; // Χρήση υπερφορτωμένου τελεστή +. Point p4 = p1.operator+(p2); // Ισοδύναμη μορφή. p1.print(); p2.print(); // Τυπώνουν «1,2», «3,4». p3.print(); p4.print(); // Τυπώνουν «4,6», «4,6». 5
Περισσότερα για την υπερφόρτωση Μπορούν να υπερφορτωθούν μόνο υπάρχοντες τελεστές της C++ (π.χ., +, ++, >>, <<). Δεν είναι δυνατή η αλλαγή της σύνταξης των τελεστών (π.χ. ο + απαιτεί δύο ορίσματα). Η τάξη τουλάχιστον ενός από τα ορίσματα πρέπει να είναι ορισμένη από το χρήστη (π.χ. δεν επιτρέπεται να αλλάξουμε τη σημασία του + για πρόσθεση ακεραίων). 6
Υπερφόρτωση προθηματικού ++ Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; void operator++(); ; void Point::operator++() { ++x; ++y; Point p1(1, 2), p2; ++p1; // Ισοδύναμα: p1.operator++(); p1.print(); // Τυπώνει «2,3». // p2 = ++p1; // Λάθος. Η κλήση ++p1 δεν επιστρέφει τίποτα. 7
Προσπάθεια βελτίωσης Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; Point operator++(); ; Point Point::operator++() { // Αλλάζει τα x και y του αντικειμένου return Point(++x, ++y); // του οποίου τη μέθοδο καλέσαμε και // επιστρέφει αντίγραφο του αντικειμένου. Point p1(1, 2), p2, p3; p2 = ++p1; // Το ++p1 είναι ένα αντίγραφο του p1. p1.print(); p2.print(); // OK. Τυπώνει «2,3», «2,3». p3 = ++(++p1); // Το αριστερό ++ δεν εφαρμόζεται στο p1, // αλλά στο νέο Point που επιστρέφεται. p1.print(); p3.print(); // Πρόβλημα: τυπώνει «3,4», «4,5». 8
Τελική μορφή προθηματικού ++ Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; Point& operator++(); ; Point& Point::operator++() { ++x; ++y; // Ο δείκτης this δείχνει στο αντικείμενο του οποίου // καλέσαμε τη μέθοδο. *this είναι αυτό το αντικείμενο. return *this; // Την κλήση της μεθόδου (π.χ. ++p1) τη σκέφτομαι σαν // αναφορά στο αντικείμενο που καθορίζει το return, // δηλαδή σαν αναφορά στο αντικείμενο του οποίου // καλέσαμε τη μέθοδο. Point p1(1, 2), p2, p3; p2 = ++p1; p1.print(); p2.print(); // OK. Τυπώνει «2,3», «2,3». p3 = ++(++p1); // Το ++p1 είναι αναφορά στο p1. p1.print(); p3.print(); // OK. Τυπώνει «4,5», «4,5». 9
Υπερφόρτωση επιθηματικού ++ Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << "," << y << endl; Point operator++(int); // Το όρισμα δεν χρησιμοποιείται. Απλά ; // χρησιμοποιείται ως ένδειξη ότι πρόκειται // για την επιθηματική μορφή του ++. Point Point::operator++(int) { Point temp(x, y); // Επιστρέφουμε αντίγραφο του αντικειμένου, x++; y++; // όπως ήταν πριν την αύξηση. return temp; Point p1(1, 2), p2; p2 = p1++; // Ισοδύναμα: p1.operator++(1); p1.print(); p2.print(); // Τυπώνει «2,3» και «1,2». 10
Ανεπιθύμητη χρήση επιθηματικού ++ Point Point::operator++(int) { Point temp(x, y); x++; y++; return temp; Point p1(1, 2), p2; p2 = (p1++)++; // Το δεξιό ++ αυξάνει το αντικείμενο // Point που επιστρέφει η κλήση p1++ // (αντίγραφο του p1 πριν την αύξηση) και // επιστρέφει αντίγραφο αυτού του // αντικειμένου πριν τη δεύτερη αύξηση. // Μάλλον άχρηστο... p1.print(); p2.print(); // Τυπώνει «2,3» και «1,2». int i = 10, j = (i++)++; // Δεν επιτρέπεται. Θα θέλαμε να μην // επιτρέπεται ούτε το (p1++)++. 11
Βελτιωμένη μορφή επιθηματικού ++ const Point Point::operator++(int) { Point temp(x, y); x++; y++; return temp; Point p1(1, 2), p2; // p2 = (p1++)++; // Τώρα δεν επιτρέπεται, γιατί το p2 = p1++; // αντικείμενο Point (αντίγραφο) που // επιστρέφει η κλήση p1++ είναι const. // Επιτρέπεται. p1.print(); p2.print(); // Τυπώνει «2,3» και «1,2». 12
Εναλλακτικός τρόπος υπερφόρτωσης Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << ", " << y << endl; float getx() const {return x; float gety() const {return y; ; // H operator+ τώρα δεν είναι μέθοδος της Point: Point operator+(const Point& q, const Point& p) { return Point(q.getX() + p.getx(), q.gety() + p.gety()); Point p1(1, 2), p2(3, 4); Point p3 = p1 + p2;. // Ισοδύναμο: Point p3 = operator+(p1, p2); p1.print(); p2.print(); p3.print(); // «1,2», «3,4», «4,6». 13
Ή με ορισμό φίλιας συνάρτησης Point(float xx = 0, float yy = 0) { x = xx; y = yy; void print() const { cout << x << ", " << y << endl; friend Point operator+(const Point& q, const Point& p); ; // H operator+ δεν είναι μέθοδος της Point, αλλά η δήλωση // friend τής δίνει πρόσβαση στα ιδιωτικά μέλη της Point: Point operator+(const Point& q, const Point& p) { return Point(q.x + p.x, q.y + p.y); Point p1(1, 2), p2(3, 4); Point p3 = p1 + p2;. // Ισοδύναμο: Point p3 = operator+(p1, p2); p1.print(); p2.print(); p3.print(); // «1,2», «3,4», «4,6». 14