====== Δυναμικός Πολυμορφισμός ======
Ας επανέλθουμε στο αρχικό παράδειγμα της κληρονομικότητας και ας ορίσουμε δύο νέες μεταβλητές α) μία μεταβλητή τύπου δείκτη και μία μεταβλητή τύπου αναφορά σε ένα αντικείμενο τύπου //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;
}