====== Κληρονομικότητα ====== Οι κλάσεις στη C++ μπορούν να επεκταθούν μέσω της κληρονομικότητας, δημιουργώντας νέες κλάσεις που διατηρούν τα χαρακτηριστικά των προγόνων τους. Η κληρονομικότητα προϋποθέτει ότι υπάρχει η βασική κλάση (πρόγονος) από την οποία προκύπτουν μία ή περισσότερες κλάσεις (απόγονοι). Οι απόγονοι διατηρούν τα χαρακτηριστικά του προγόνου, αλλά έχουν την δυνατότητα να προσθέσουν και επιπλέον χαρακτηριστικά στη νέα κλάση. Στο παρακάτω παράδειγμα, από την κλάση //Shape// (διδιάστατο σχήμα) προκύπτει η κλάση //Rectangle// (ορθογώνιο παραλληλόγραμμο). Η κλάση //Shape// ορίζει την //private// μεταβλητή //color// που αφορά το χρώμα του σχήματος, το οποίο αποθηκεύεται σε μορφή [[wp>RGB]] και την //protected// μεταβλητή //borderWidth// που αφορά το πάχος του πλαισίου γύρο από το σχήμα. 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 int getArea(); }; #include using namespace std; #include "Shape.hpp" 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 int Shape::getArea() { return 0; } #include "Shape.hpp" class Rectangle : public Shape { private: unsigned int width, height; public: Rectangle(unsigned int c, unsigned char bw, unsigned int w, unsigned int h); void setWidth(unsigned int w); void setHeight(unsigned int h); unsigned int getWidth(); unsigned int getHeight(); unsigned int getArea(); unsigned char getBorderWidth(); void setBorderWidth(unsigned char bw); }; #include using namespace std; #include "Rectangle.hpp" Rectangle::Rectangle(unsigned int c, unsigned char bw, unsigned int w, unsigned int h) : Shape(c, bw) , width(w), height(h) { } void Rectangle::setWidth(unsigned int w) { width = w; } void Rectangle::setHeight(unsigned int h) { height = h; } unsigned int Rectangle::getWidth() { return width; } unsigned int Rectangle::getHeight() { return height; } unsigned int Rectangle::getArea() { /* this is how you call a function * from the parent class. */ int area = Shape::getArea(); return Shape::getArea() + width * height; } unsigned char Rectangle::getBorderWidth() { return borderWidth; } void Rectangle::setBorderWidth(unsigned char bw) { borderWidth = bw; } #include "Rectangle.hpp" #include using namespace std; int main() { Rectangle rectangle(0xffffff, 2, 10, 20); rectangle.setBorderWidth(8); cout << "[Rectangle Properties] "; cout << " color: 0x" << std::hex << rectangle.getColor() << dec; cout << " borderWidth: " << (int)rectangle.getBorderWidth(); cout << " area: " << rectangle.getArea() << endl; return 0; } Από τα παραπάνω παρατηρούμε τα εξής: - Η κληρονομικότητα δηλώνεται μέσω της δήλωσης ''class derived_class_name : public base_class_name { /*...*/ };'' (στο παραπάνω παράδειγμα ''class Rectangle: public Shape { };''). - Η απόγονος κλάση μπορεί να ορίσει επιπλέον πεδία και επιπλέον μεθόδους. Η απόγονος κλάση //Rectangle// ορίζει τα επιπλέον πεδία //width, height// και τις επιπλέον μεθόδους //getWidth//, //getHeight//, //setWidth//, //setHeight//. - Η απόγονος κλάση μπορεί να επαναορίσει μία μέθοδο η οποία είναι ήδη ορισμένη στη γονική κλάση. Η απόγονος κλάση //Rectangle// επαναορίζει τη μέθοδο //getArea//. - Τα μέλη της γονικής κλάσης //Shape// που είναι δηλωμένα ως //protected// είναι προσβάσιμα από την απόγονο κλάση. Στο παραπάνω παράδειγμα η απόγονος κλάση //Rectangle// έχει πρόσβαση στο //protected// πεδίο //borderWidth// της γονικής κλάσης. - Τα μέλη της γονικής κλάσης //Shape// που είναι δηλωμένα ως //private// δεν είναι προσβάσιμα από την απόγονο κλάση //Rectangle//. - Όπως και στη Java, τα μέλη της γονικής κλάσης //Shape// ή της απογόνου κλάσης //Rectangle// που είναι δηλωμένα ως //public// είναι προσβάσιμα από οποιαδήποτε κλάση ή μέθοδο. Για παράδειγμα η //public// μεθόδος //getColor// της κλάσης //Shape// είναι προσβάσιμη από οποιοδήποτε κλάση ή μέθοδο (στο παράδειγμα από τη μέθοδο main). Συνοπτικά ο πίνακας προσβασιμότητας παρατίθεται παρακάτω: ^ ^ μέλη της γονικής κλάσης ^^^ ^ προσβασιμότητα ^ public ^ protected ^ private ^ | από μεθόδους της ίδιας κλάσης | ναι | ναι | ναι | | από μεθόδους μίας υποκλάσης | ναι | ναι | όχι | | από μία άλλη κλάση χωρίς σχέση κληρονομικότητας | ναι | όχι | όχι | ===== Κλήση μιας επανα-ορισμένης μεθόδου της γονικής κλάσης από την υποκλάση ===== Όταν η απόγονος κλάση επαναορίζει μία μέθοδο με το ίδιο //signature// (ίδιο όνομα, ίδιος αριθμός και τύπος ορισμάτων) τότε συχνά θέλουμε να καλέσουμε τη μέθοδο της γονικής κλάσης στην απόγονο κλάση προκειμένου να την επεκτείνουμε. Ο τρόπος κλήσης της γονικής μεθόδου είναι βάζοντας ως πρόθεμα στο όνομα της μεθόδου το όνομα της γονικής κλάσης. Από το παραπάνω παράδειγμα, δείτε την υλοποίηση της συνάρτησης //getArea// στην κλάση //Rectangle// η οποία χρησιμοποιεί την συνάρτηση //getArea// της γονικής κλάσης //Shape//. unsigned int Rectangle::getArea() { return Shape::getArea() + width * height; }