This is an old revision of the document!
Κληρονομικότητα
Σε αναλογία με τη Java, οι κλάσεις στη C++ μπορούν να επεκταθούν μέσω της κληρονομικότητας, δημιουργώντας νέες κλάσεις που διατηρούν τα χαρακτηριστικά των προγόνων τους. Η κληρονομικότητα προϋποθέτει ότι υπάρχει η βασική κλάση (πρόγονος) από την οποία προκύπτουν μία ή περισσότερες κλάσεις (απόγονοι). Οι απόγονοι διατηρούν τα χαρακτηριστικά του προγόνου, αλλά έχουν την δυνατότητα να προσθέσουν και επιπλέον χαρακτηριστικά στη νέα κλάση.
Στο παρακάτω παράδειγμα, από την κλάση Shape2D (διδιάστατο σχήμα) προκύπτει η κλάση Rectangle (ορθογώνιο παραλληλόγραμμο). Η κλάση Shape2D ορίζει την private μεταβλητή color που αφορά το χρώμα του τετραγώνου και το οποίο αποθηκεύεται σε μορφή RGB και την protected μεταβλητή borderWidth που αφορά το πάχος του πλαισίου γύρο από το σχήμα.
- Shape2D.cpp
#include <iostream> using namespace std; #ifndef __SHAPE2D__ #define __SHAPE2D__ class Shape2D { unsigned int color; protected: unsigned char borderWidth; public: Shape2D(unsigned int c, unsigned char bw); Shape2D(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); unsigned int getArea(); }; void Shape2D::setColor(unsigned int c) { color = c; } void Shape2D::setColor(unsigned char red, unsigned char blue, unsigned char green) { color = red; color <<= 8; color |= blue; color <<= 8; color |= green; } unsigned int Shape2D::getColor() { return color; } Shape2D::Shape2D(unsigned int c, unsigned char bw) : color(c), borderWidth(bw) { cout << "Color: 0x" << hex << color << dec; cout << " BorderWidth: " << borderWidth << endl; } Shape2D::Shape2D(unsigned char red, unsigned char blue, unsigned char green, unsigned char bw) : borderWidth(bw) { setColor(red, blue, green); } unsigned int Shape2D::getArea() { return 0; } unsigned char Shape2D::getBorderWidth() { return borderWidth; } void Shape2D::setBorderWidth(unsigned char bw) { borderWidth = bw; } #endif
- Rectangle.cpp
#include <iostream> using namespace std; #include "Shape2D.cpp" class Rectangle : public Shape2D { 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 char getBorderWidth(); void setBorderWidth(unsigned char bw); unsigned int getArea(); }; Rectangle::Rectangle(unsigned int c, unsigned char bw, unsigned int w, unsigned int h) : Shape2D(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 char Rectangle::getBorderWidth() { return borderWidth; } void Rectangle::setBorderWidth(unsigned char bw) { borderWidth = bw; } unsigned int Rectangle::getArea() { return width * height; }
- ShapeUsage.cpp
#include "Rectangle.cpp" int main() { Rectangle rectangle(0xffffff, 2, 10, 20); cout << "[Rectangle Properties] "; cout << " color: 0x" << 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 Shape2D { };
). - Η απόγονος κλάση μπορεί να ορίσει επιπλέον πεδία και επιπλέον μεθόδους. Η απόγονος κλάση Rectangle ορίζει τα επιπλέον πεδία width, height και τις επιπλέον μεθόδους getWidth, getHeight, setWidth, setHeight.
- Η απόγονος κλάση μπορεί να επαναορίσει μία μέθοδο η οποία είναι ήδη ορισμένη στη γονική κλάση. Η απόγονος κλάση Rectangle επαναορίζει τις μεθόδους setBorderWidth και getBorderWidth.
- Τα μέλη της γονικής κλάσης Shape2D που είναι δηλωμένα ως protected είναι προσβάσιμα από την απόγονο κλάση. Στο παραπάνω παράδειγμα η απόγονος κλάση Rectangle έχει πρόσβαση στο protected πεδίο borderWidth της γονικής κλάης.
- Τα μέλη της γονικής κλάσης Shape2D που είναι δηλωμένα ως private δεν είναι προσβάσιμα από την απόγονο κλάση Rectangle.
- Όπως και στη Java, τα μέλη της γονικής κλάσης Rectangle ή της απογόνου κλάσης Square που είναι δηλωμένα ως public είναι προσβάσιμα από οποιαδήποτε κλάση ή μέθοδο. Για παράδειγμα η public μεθόδος getColor της κλάσης Shape2D είναι προσβάσιμη από οποιοδήποτε κλάση ή μέθοδο (στο παράδειγμα από τη μέθοδο main).
Συνοπτικά ο πίνακας προσβασιμότητας παρατίθεται παρακάτω:
μέλη της γονικής κλάσης | |||
---|---|---|---|
προσβασιμότητα | public | protected | private |
μέλη της ίδιας κλάσης | ναι | ναι | ναι |
μέλη υποκλάσης | ναι | ναι | όχι |
μη μέλη της κλάσης | ναι | όχι | όχι |
Όταν μία κλάση έχει πρόσβαση σε ένα πεδίο, το πεδίο αυτό δεν είναι προσβάσιμο μόνο για το τρέχον αντικείμενο, αλλά και για όλα αντικείμενα του τύπου της κλάσης. Στο παραπάνω παράδειγμα, η μέθοδος equals στις κλάσεις Rectangle και Square έχει πρόσβαση στα πεδία του αντικειμένου της μεθόδου που λαμβάνεται ως όρισμα.
Όταν η απόγονος κλάση επαναορίζει μία μέθοδο με το ίδιο signature (ίδιο όνομα, ίδιος αριθμός και τύπος ορισμάτων) τότε συχνά θέλουμε να χρησιμοποιήσουμε τη γονική μέθοδο προκειμένου να την επεκτείνουμε. Ο τρόπος χρήσης της γονικής μεθόδου είναι βάζοντας ως πρόθεμα στη μέθοδο το όνομα της γονικής κλάσης. Ο λόγος που δεν υπάρχει κάποια δεσμευμένη λέξη (όπως ο τελεστής super στη Java) που να αναφέρεται στη γονική κλάση είναι η πολλαπλή κληρονομικότητα που επιτρέπει η C++. Από το παραπάνω παράδειγμα, δείτε την υλοποίηση της equals στην κλάση Square.
bool Square::equals(Square &s) { if( Rectangle::equals(s) && s.color == color) return true; return false; }