User Tools

Site Tools


cpp:polymorphism

This is an old revision of the document!


Δυναμικός Πολυμορφισμός

Ας επανέλθουμε στο αρχικό παράδειγμα της κληρονομικότητας και ας ορίσουμε δύο νέες μεταβλητές α) μία μεταβλητή τύπου δείκτη και μία μεταβλητή τύπου αναφορά σε ένα αντικείμενο τύπου Shape2D ως εξής:

ShapeUsage.cpp
#include "Rectangle.cpp"
 
int main() {
  Shape2D shape(0x333333, 5);
  Shape2D &shape_ref = shape, *shape_ptr = &shape;
  Rectangle rectangle(0xffffff, 2, 10, 20);
  Shape2D &rect_ref = rectangle, *rect_ptr = &rectangle;
 
  cout << "Shape2D area: " << shape.getArea() << endl;
  cout << "Shape2D reference area: " << shape_ref.getArea() << endl;
  cout << "Shape2D 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;
} 

Μεταγλωττίζοντας και εκτελώντας τον παραπάνω κώδικα λαμβάνουμε τα εξής:

Shape2D area: 0
Shape2D reference area: 0
Shape2D pointer area: 0

Rectangle area: 200
Rectangle reference area: 0
Rectangle pointer area: 0

Από τα παραπάνω συμπεραίνουμε ότι η επιλογή κλήσης της μεθόδου getArea δεν γίνεται δυναμικά με βάση τον τύπο του αντικειμένου στον οποίο δείχνει ο δείκτης ή η αναφορά, αλλά στατικά με βάση τον τύπο δεδομένων για τον οποίο δηλώνεται ο δείκτης ή η αναφορά. Σε αυτή την περίπτωση η επιλογή της μεθόδου γίνεται από τον compiler κατά τη μεταγλώττιση του προγράμματος.

Εάν θέλουμε η επιλογή της μεθόδου να γίνεται δυναμικά με βάση τον τύπο του αντικειμένου που δείχνει ο δείκτης ή η αναφορά θα πρέπει να δηλώσουμε τη μέθοδο getArea στη γονική κλάση Shape2D ως virtual όπως παρακάτω:

class Shape2D {
  public:
    virtual unsigned int getArea();
}
unsigned int Shape2D::getArea() { return 0; }

Με αυτό τον τρόπο δηλώνουμε προς τον compiler ότι η απόφαση για τον ποια μέθοδος θα κληθεί δεν θα ληφθεί κατά τη μεταγλώττιση, αλλά κατά την εκτέλεση του προγράμματος. Δηλώνοντας τη μέθοδο getArea ως virtual στη γονική κλάση το αποτέλεσμα της εκτέλεσης είναι το εξής:

Shape2D area: 0
Shape2D reference area: 0
Shape2D 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 <return type> <function_name>(<function parameters) =0;

Η κλάση που περιέχει μία ή περισσότερες pure virtual συναρτήσεις είναι abstract και δεν μπορεί να παράγει αντικείμενα, ακόμη και εάν διαθέτει κατασκευαστή. Μόνο οι κλάσεις που θα κληρονομήσουν τη συγκεκριμένη κλάση και θα παρέχουν υλοποιήσεις όλων των pure virtual μεθόδων θα μπορέσουν να παράγουν αντικείμενα.

Για παράδειγμα, στην κλάση Shape2D είναι λογικό να δηλώσουμε την μέθοδο getArea ως pure virtual (αντί να επιστρέφει μηδέν) μιας και η συγκεκριμένη συνάρτηση δεν έχει νόημα για το κλάση Shape2D, αλλά μόνο για τις υποκλάσεις αυτής. Η κλάση Shape2D διαμορφώνεται ως εξής:

Shape.cpp
#include <iostream>
#include <string>
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);
    virtual unsigned int getArea() = 0;
};
 
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) {}
Shape2D::Shape2D(unsigned char red, unsigned char blue, unsigned char green, unsigned char bw) : borderWidth(bw) { setColor(red, blue, green); }
unsigned char Shape2D::getBorderWidth() { return borderWidth; }
void Shape2D::setBorderWidth(unsigned char bw) { borderWidth = bw; }
#endif

Αντίστοιχα, η μέθοδος getArea() της κλάσης Rectangle διαμορφώνεται ως εξής:

unsigned int Rectangle::getArea() {
  return width * height;
}

Επειδή η κλάση Shape2D είναι πλέον abstract δεν μπορεί να δώσει αντικείμενα. Έτσι η συνάρτηση main διαμορφώνεται ως εξής.

ShapeUsage.cpp
#include "Rectangle.cpp"
 
int main() {
  Rectangle rectangle(0xffffff, 2, 10, 20);
  Shape2D &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;
} 
cpp/polymorphism.1493124912.txt.gz · Last modified: 2017/04/25 11:55 (external edit)