User Tools

Site Tools


cpp:inheritance

Κληρονομικότητα

Οι κλάσεις στη C++ μπορούν να επεκταθούν μέσω της κληρονομικότητας, δημιουργώντας νέες κλάσεις που διατηρούν τα χαρακτηριστικά των προγόνων τους. Η κληρονομικότητα προϋποθέτει ότι υπάρχει η βασική κλάση (πρόγονος) από την οποία προκύπτουν μία ή περισσότερες κλάσεις (απόγονοι). Οι απόγονοι διατηρούν τα χαρακτηριστικά του προγόνου, αλλά έχουν την δυνατότητα να προσθέσουν και επιπλέον χαρακτηριστικά στη νέα κλάση.

Στο παρακάτω παράδειγμα, από την κλάση Shape (διδιάστατο σχήμα) προκύπτει η κλάση Rectangle (ορθογώνιο παραλληλόγραμμο). Η κλάση Shape ορίζει την private μεταβλητή color που αφορά το χρώμα του σχήματος, το οποίο αποθηκεύεται σε μορφή RGB και την protected μεταβλητή borderWidth που αφορά το πάχος του πλαισίου γύρο από το σχήμα.

Shape.hpp
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();
};
Shape.cpp
#include <iostream>
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;
}
Rectangle.hpp
#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);
};
Rectangle.cpp
#include <iostream>
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; }
RectangleUsage.cpp
#include "Rectangle.hpp"
#include <iostream>
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;
}

Από τα παραπάνω παρατηρούμε τα εξής:

  1. Η κληρονομικότητα δηλώνεται μέσω της δήλωσης class derived_class_name : public base_class_name { }; (στο παραπάνω παράδειγμα class Rectangle: public Shape { };).
  2. Η απόγονος κλάση μπορεί να ορίσει επιπλέον πεδία και επιπλέον μεθόδους. Η απόγονος κλάση Rectangle ορίζει τα επιπλέον πεδία width, height και τις επιπλέον μεθόδους getWidth, getHeight, setWidth, setHeight.
  3. Η απόγονος κλάση μπορεί να επαναορίσει μία μέθοδο η οποία είναι ήδη ορισμένη στη γονική κλάση. Η απόγονος κλάση Rectangle επαναορίζει τη μέθοδο getArea.
  4. Τα μέλη της γονικής κλάσης Shape που είναι δηλωμένα ως protected είναι προσβάσιμα από την απόγονο κλάση. Στο παραπάνω παράδειγμα η απόγονος κλάση Rectangle έχει πρόσβαση στο protected πεδίο borderWidth της γονικής κλάσης.
  5. Τα μέλη της γονικής κλάσης Shape που είναι δηλωμένα ως private δεν είναι προσβάσιμα από την απόγονο κλάση Rectangle.
  6. Όπως και στη 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;
}
cpp/inheritance.txt · Last modified: 2022/05/13 07:01 by gthanos