====== Δυναμικός Πολυμορφισμός ====== Ας επανέλθουμε στο αρχικό παράδειγμα της κληρονομικότητας και ας ορίσουμε δύο νέες μεταβλητές α) μία μεταβλητή τύπου δείκτη και μία μεταβλητή τύπου αναφορά σε ένα αντικείμενο τύπου //Shape// ως εξής: #include "Rectangle.cpp" int main() { Shape shape(0x333333, 5); Shape &shape_ref = shape, *shape_ptr = &shape; Rectangle rectangle(0xffffff, 2, 10, 20); Shape &rect_ref = rectangle, *rect_ptr = &rectangle; cout << "Shape area: " << shape.getArea() << endl; cout << "Shape reference area: " << shape_ref.getArea() << endl; cout << "Shape pointer area: " << shape_ptr->getArea() << endl; cout << endl; cout << "Rectangle area: " << rectangle.getArea() << endl; cout << "Rectangle reference area: " << rect_ref.getArea() << endl; cout << "Rectangle pointer area: " << rect_ptr->getArea() << endl; } Μεταγλωττίζοντας και εκτελώντας τον παραπάνω κώδικα λαμβάνουμε τα εξής: Shape area: 0 Shape reference area: 0 Shape pointer area: 0 Rectangle area: 200 Rectangle reference area: 0 Rectangle pointer area: 0 Από τα παραπάνω συμπεραίνουμε ότι η επιλογή κλήσης της μεθόδου //getArea// δεν γίνεται δυναμικά με βάση τον τύπο του αντικειμένου στον οποίο δείχνει ο δείκτης ή η αναφορά, αλλά στατικά με βάση τον τύπο δεδομένων για τον οποίο δηλώνεται ο δείκτης ή η αναφορά. Σε αυτή την περίπτωση η επιλογή της μεθόδου γίνεται από τον //compiler// κατά τη μεταγλώττιση του προγράμματος. Εάν θέλουμε η επιλογή της μεθόδου να γίνεται δυναμικά με βάση τον τύπο του αντικειμένου που δείχνει ο δείκτης ή η αναφορά θα πρέπει να δηλώσουμε τη μέθοδο //getArea// στη γονική κλάση //Shape// ως //**virtual**// όπως παρακάτω: class Shape { public: virtual unsigned int getArea(); }; unsigned int Shape::getArea() { return 0; } Με αυτό τον τρόπο δηλώνουμε προς τον //compiler// ότι η απόφαση για τον ποια μέθοδος θα κληθεί δεν θα ληφθεί κατά τη μεταγλώττιση, αλλά κατά την εκτέλεση του προγράμματος. Δηλώνοντας τη μέθοδο //getArea// ως //**virtual**// στη γονική κλάση το αποτέλεσμα της εκτέλεσης είναι το εξής: Shape area: 0 Shape reference area: 0 Shape pointer area: 0 Rectangle area: 200 Rectangle reference area: 200 Rectangle pointer area: 200 ====== Pure virtual συναρτήσεις και abstract κλάσεις ====== Εκτός από τις //virtual// μεθόδους που είδαμε προηγούμενα μπορούμε να έχουμε και //pure virtual// μεθόδους. Οι μέθοδοι που χαρακτηρίζονται //pure virtual// όταν ορίζονται σε μία κλάση, δηλώνεται μόνο το //prototype// τους, χωρίς να δηλώνεται σώμα και ακολουθεί η δήλωση ''=0''. virtual ( Η κλάση που περιέχει μία ή περισσότερες //pure virtual// συναρτήσεις είναι //abstract// και δεν μπορεί να παράγει αντικείμενα, **ακόμη και εάν διαθέτει κατασκευαστή**. Μόνο οι κλάσεις που θα κληρονομήσουν τη συγκεκριμένη κλάση και θα παρέχουν υλοποιήσεις όλων των //pure virtual// μεθόδων θα μπορέσουν να παράγουν αντικείμενα. Για παράδειγμα, στην κλάση //Shape// είναι λογικό να δηλώσουμε την μέθοδο //getArea// ως //pure virtual// (αντί να επιστρέφει μηδέν) μιας και η συγκεκριμένη συνάρτηση δεν έχει νόημα για το κλάση //Shape//, αλλά μόνο για τις υποκλάσεις αυτής. Η κλάση //Shape// διαμορφώνεται ως εξής: #include #include using namespace std; #ifndef __SHAPE2D__ #define __SHAPE2D__ class Shape { unsigned int color; protected: unsigned char borderWidth; public: Shape(unsigned int c, unsigned char bw); Shape(unsigned char red, unsigned char blue, unsigned char green, unsigned char bw); void setColor(unsigned int c); void setColor(unsigned char red, unsigned char blue, unsigned char green); unsigned int getColor(); unsigned char getBorderWidth(); void setBorderWidth(unsigned char bw); virtual unsigned int getArea() = 0; }; void Shape::setColor(unsigned int c) { color = c; } void Shape::setColor(unsigned char red, unsigned char blue, unsigned char green) { color = red; color <<= 8; color |= blue; color <<= 8; color |= green; } unsigned int Shape::getColor() { return color; } Shape::Shape(unsigned int c, unsigned char bw) : color(c), borderWidth(bw) {} Shape::Shape(unsigned char red, unsigned char blue, unsigned char green, unsigned char bw) : borderWidth(bw) { setColor(red, blue, green); } unsigned char Shape::getBorderWidth() { return borderWidth; } void Shape::setBorderWidth(unsigned char bw) { borderWidth = bw; } #endif Αντίστοιχα, η μέθοδος getArea() της κλάσης //Rectangle// διαμορφώνεται ως εξής: unsigned int Rectangle::getArea() { return width * height; } Επειδή η κλάση //Shape// είναι πλέον //abstract// δεν μπορεί να δώσει αντικείμενα. Έτσι η συνάρτηση //main// διαμορφώνεται ως εξής. #include "Rectangle.cpp" int main() { Rectangle rectangle(0xffffff, 2, 10, 20); Shape &rect_ref = rectangle, *rect_ptr = &rectangle; cout << "Rectangle area: " << rectangle.getArea() << endl; cout << "Rectangle reference area: " << rect_ref.getArea() << endl; cout << "Rectangle pointer area: " << rect_ptr->getArea() << endl; }