====== Κληρονομικότητα ======
Οι κλάσεις στη 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;
}