ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ ΚΑΙ ΠΟΛΥΜΟΡΦΙΣΜΟΣ

Σχετικά έγγραφα
Προγραμματισμός Υπολογιστών με C++

Προγραμματισμός Υπολογιστών με C++

ΚΑΤΑΣΚΕΥΑΣΤΕΣ ΑΝΤΙΓΡΑΦΗΣ

Κλήση Συναρτήσεων ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ. Γεώργιος Παπαϊωάννου ( )

Προγραμματισμός Υπολογιστών με C++

Προγραμματισμός Υπολογιστών με C++

Εισαγωγή σε αντικειμενοστραφή concepts. Και λίγη C#

Προγραμματισμός Υπολογιστών με C++ Φύλλο Διαγωνίσματος Ακαδημαϊκό εξάμηνο: Χειμερινό

Σύνθεση και Κληρονομικότητα

Προγραμματισμός Υπολογιστών με C++

ΚΑΛΟΥΠΩΜΑΤΑ & ΜΕΤΑΤΡΟΠΕΣ

Προγραμματισμός Ι. Κλάσεις και Αντικείμενα. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

ΔΕΙΚΤΕΣ ΚΑΙ ΔΙΕΥΘΥΝΣΕΙΣ

Σύνθεση και Κληρονομικότητα

Προγραμματισμός Υπολογιστών με C++

Προγραμματισμός Υπολογιστών με C++

Προγραμματισμός Υπολογιστών με C++

Αντικειμενοστρεφής Προγραμματισμός

Προγραμματισμός Υπολογιστών με C++

Ενδεικτικές Λύσεις σε Επιλεγμένα Θέματα της C++

Εισαγωγή στον Αντικειμενοστρεφή Προγραμματισμό Διάλεξη #2

Εισαγωγή στον Προγραµµατισµό, Αντώνιος Συµβώνης, ΣΕΜΦΕ, ΕΜΠ,, Slide 6

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα ως ορίσματα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Constructors, equals, tostring

Τελεστές ΤΕΛΕΣΤΕΣ. Γεώργιος Παπαϊωάννου ( )

Προγραμματισμός Ι. Εισαγωγή στην C++ Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

«ΕΙΔΙΚΑ ΘΕΜΑΣΑ ΣΟΝ ΠΡΟΓΡΑΜΜΑΣΙΜΟ ΤΠΟΛΟΓΙΣΩΝ» Κεφάλαιο 4: Αντικειμενοςτρεφήσ Προγραμματιςμόσ

Τελικό τεστ - απαντήσεις

Κλάσεις στη Java. Στοίβα - Stack. Δήλωση της κλάσης. ΗκλάσηVector της Java. Ηκλάση Stack

Εαρινό. Ύλη εργαστηρίου, Ασκήσεις Java

Κλάσεις και αντικείμενα #include <iostream.h<

ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ. Σχηματική παράσταση του προγράμματος. logariasmos

ΑΠΛΗ ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ

ΠΟΛΥΜΟΡΦΙΣΜΟΣ. 4.1 Κληρονομικότητα και Αρχή της Υποκατάστασης

Κλάσεις στη Java. Παύλος Εφραιμίδης. Java Κλάσεις στη Java 1

Κλάσεις και Αντικείµενα

Προγραμματισμός Υπολογιστών με C++

Wrapper Classes, Abstract Classes and Interfaces

Ερωτήσεις και απαντήσεις στα θέματα του κανονισμού κατάρτισης

Εισαγωγή στον Αντικειμενοστρέφή Προγραμματισμό Διάλεξη #13

2.1 Αντικειµενοστρεφής προγραµµατισµός

Αντικειμενοστραφείς Γλώσσες Προγραμματισμού C++ / ROOT

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Constructors

Οντοκεντρικός Προγραμματισμός

(Διαφάνειες Νίκου Βιδάκη)

12. ΑΛΦΑΡΙΘΜΗΤΙΚΑ. υο είδη αλφαριθµητικών Τα αλφαριθµητικά της C πίνακες τύπου char Ta αντικείµενα της κλάσης string

Ονοματεπώνυμο και ΑΜ: Είχα παραδώσει εργασίες τα εξής ακαδημαϊκά έτη: Διάρκεια: 2,5 ώρες, κλειστά βιβλία και σημειώσεις ΚΑΛΗ ΕΠΙΤΥΧΙΑ!

Οντοκεντρικός Προγραμματισμός

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα ως ορίσματα Εισαγωγή στις αναφορές

Abstract classes, Interfaces ΦΡΟΝΤΙΣΤΗΡΙΟ JAVA

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Constructors Υπερφόρτωση Αντικείμενα ως παράμετροι

Αρχές Τεχνολογίας Λογισμικού Εργαστήριο

POINTERS, AGGREGATION, COMPOSITION

Διάλεξη 16-17: Πολυμορφισμός (Polymorphism) Διδάσκων: Παναγιώτης Ανδρέου

Κλάσεις ΚΛΑΣΕΙΣ. Γεώργιος Παπαϊωάννου ( )

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Υπερφόρτωση Αντικείμενα σαν ορίσματα

Ενδεικτική περιγραφή μαθήματος

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 3, 7, 8 & 9 17/1/08

Αντικειµενοστρεφής Προγραµµατισµός

Κατασκευαστές. Μέθοδοι Κατασκευής (Constructors).

Abstract classes, Interfaces ΦΡΟΝΤΙΣΤΗΡΙΟ JAVA

Αντικείμενα στη Java. Παύλος Εφραιμίδης. Java Αντικείμενα στη Java 1

Προγραμματισμός Υπολογιστών με C++

ΜΑΘΗΜΑ: Αντικειμενοστρεφής Προγραμματισμός

ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ ΠΟΛΥΜΟΡΦΙΣΜΟΣ

lab13grades 449 PASS 451 PASS PASS FAIL 1900 FAIL Page 1

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Δημιουργία Κλάσεων και Αντικειμένων Constructors

Το πρόγραμμα HelloWorld.java. HelloWorld. Κλάσεις και Αντικείμενα (2) Ορισμός μιας Κλάσης (1) Παύλος Εφραιμίδης pefraimi <at> ee.duth.

Εργαστήριο Java. Διδάσκουσα: Εργαστηριακοί Συνεργάτες:

Αντικειμενοστραφής Προγραμματισμός I (5 ο εξ) Εργαστήριο #2 ο : Ανατομία προγραμμάτων εφαρμογών, η

ΒΑΣΙΚΟΙ ΤΥΠΟΙ ΚΑΙ ΠΙΝΑΚΕΣ

Αντικειμενοστραφής Προγραμματισμός

Αντικειμενοστρέφεια. Henri Matisse, Harmony in Red, Κωστής Σαγώνας Νίκος Παπασπύρου

Αντικειμενοστρεφής Προγραμματισμός

Οντοκεντρικός Προγραμματισμός

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα με πίνακες. Constructors. Υλοποίηση Στοίβας

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πολυμορφισμός Αφηρημένες κλάσεις Interfaces (διεπαφές)

Αντικειμενοστραφείς Γλώσσες Προγραμματισμού C++ / ROOT

Υπερφόρτωση τελεστών

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Αναφορές-Παράμετροι

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Υπάρχουσες κλάσεις και αντικείμενα στην Java Strings Wrapper Classes Δομές

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Αναφορές

Αντικείμενα (Objects) στην Java. Αντικείμενα στη Java. Δημιουργία Αντικειμένων. Δηλώσεις Μεταβλητών (2) Ο τελεστής new (1)

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός Μνήμης Αντικείμενα ως ορίσματα

Αντικειμενοστραφής Προγραμματισμός I(5 ο εξ) Εργαστήριο #2 ο : Ανατομία προγραμμάτων εφαρμογών, η

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πίνακες Κλάσεις και Αντικείμενα

Κληρονομικότητα. Παύλος Εφραιμίδης pefraimi <at> ee.duth.gr. Java Κληρονομικότητα 1

Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ

Ονοματεπώνυμο και ΑΜ: Είχα παραδώσει εργασίες τα προηγούμενα ακαδημαϊκά έτη: ΚΑΛΗ ΕΠΙΤΥΧΙΑ!

Εργαστήριο Java. Διδάσκουσα: Εργαστηριακοί Συνεργάτες:

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΗΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Προγραμματισμός Υπολογιστών με C++

Οντοκεντρικός Προγραμματισμός ΦΡΟΝΤΙΣΤΗΡΙΟ JAVA

Αντικειμενοστρεφής Προγραμματισμός Διάλεξη 5 : ΠΕΡΙΣΣΟΤΕΡΑ ΓΙΑ ΤΙΣ CLASSES

Απάντηση. // We write in a header file named my_header.h #ifndef my_header_h #define my_header_h #define divides(x,y) (((y)%(x)==0)?

Αναφορές, είκτες και Αλφαριθμητικά

02 Αντικειμενοστρεφής Προγραμματισμός

Ενδεικτικές Λύσεις σε Επιλεγμένα Θέματα της C++

ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΗΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Transcript:

ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ ΚΑΙ ΠΟΛΥΜΟΡΦΙΣΜΟΣ Γεώργιος Παπαϊωάννου (2013-14) gepap@aueb.gr

Περιγραφή: Επέκταση κλάσεων στη C++ Τροποποίηση μεθόδων (method overriding) Κληρονομικότητα και κατασκευαστές, καταστροφείς, τελεστές Virtual Μέθοδοι Αφηρημένες κλάσεις (abstract classes) Πολλαπλή κληρονομικότητα (multiple inheritance) Τελευταία ενημέρωση: Αύγουστος 2013 Εισαγωγή - 2

class Point { private: int x, y; void setx(int newx) { x = newx; void sety(int newy) { y = newy; int getx() const { return x; int gety() const { return y; void display() const { cout << "x: " << x << ", y: " << y << endl; Point(int newx = 0, int newy = 0): x(newx), y(newy) { Point(const Point& original): x(original.x), y(original.y) { int main() { Point p1(1, 2); p1.display(); 3

class LabeledPoint : public Point { string label; // Πρόσθετη μεταβλητή-μέλος. // Νέες μέθοδοι: void setlabel(const string& newlabel) { label = newlabel; string getlabel() const { return label; LabeledPoint(int newx = 0, int newy = 0, // Κατασκευαστής. const string& newlabel = "label"); int main() { LabeledPoint p2(1, 2, "test"); p2.setx(3); p2.sety(4); p2.setlabel("other"); cout << p2.getlabel() << endl; p2.display(); // Κληρονομημένες μέθοδοι. // Πρόσθετη μέθοδος. // Τυπώνει «other». // Κληρονομημένη. Δεν τυπώνει το label. 4

class LabeledPoint : public Point { string label; void setlabel(const string& newlabel) { label = newlabel; string getlabel() const { return label; LabeledPoint(int newx = 0, int newy = 0, const string& newlabel = "label"); void display() const; // Τροποποίηση μεθόδου. // (Υπάρχει και στην Point.) // Τώρα τυπώνεται και το label: void LabeledPoint::display() const { cout << "label: " << label << ", "; // cout << x << y; // Λάθος. Ιδιωτικά μέλη της Point. // cout << getx() << gety(); // ΟΚ, αλλά καλύτερα το ακόλουθο: Point::display(); // Κλήση της αρχικής μορφής της μεθόδου. 5

LabeledPoint::LabeledPoint(int newx, int newy, const string& newlabel) : Point(newX, newy), // Κλήση του κατασκευαστή της Point. label(newlabel) // Αρχικοποίηση μεταβλητής. { // x = NewX; // Λάθος. Δεν έχουμε πρόσβαση στα ιδιωτικά μέλη της Point. // y = NewY; Κατά τη δημιουργία ενός αντικειμένου της παράγωγης κλάσης, καλείται πρώτα ο κατασκευαστής του προγόνου. Εάν ο κατασκευαστής της παράγωγης κλάσης δεν περιέχει ρητή κλήση κατασκευαστή του προγόνου, καλείται ο προεπιλεγμένος κατασκευαστής του. Στο συγκεκριμένο παράδειγμα, χωρίς ρητή κλήση κατασκευαστή της Point, όλα τα αντικείμενα LabeledPoint θα είχαν x = 0, y = 0. 6

Η κληρονομικότητα δεν είναι ο μόνος τρόπος να επαναχρησιμοποιήσουμε μια υπάρχουσα τάξη. Η κληρονομικότητα αντιστοιχεί σε σχέσεις is-a. Ένα αντικείμενο LabeledPoint είναι ένα είδος Point. Ένα αυτοκίνητο είναι ένα είδος οχήματος. Συχνά μας χρειάζονται σχέσεις has-a (ή part-of). Ένα αντικείμενο Line περιέχει δύο αντικείμενα Point. Ένα αυτοκίνητο έχει τέσσερις ρόδες. 7

class Line { Point start, end; string label; Line(int x1, int y1, int x2, int y2, const string& s) : start(x1, y1), end(x2, y2), label(s) { void display() const { cout << label << endl; start.display(); end.display(); int main() { Line myline(10, 20, 40, 50, "test"); myline.display(); 8

Η κλήση των κατασκευαστών γίνεται φωλιασμένα (base class derived class) Η κλήση των καταστροφέων γίνεται κι αυτή φωλιασμένα, αλλά με την αντίστροφη σειρά (derived class base class) Αν δε δηλώνεται διαφορετικά, καλείται ο default constructor της κλάσης βάσης Αν δεν έχει οριστεί default constructor και έχουμε δηλώσει κάποιον κατασκευαστή με ορίσματα, πρέπει να καλέσουμε αυτών (από τη λίστα αρχικοποίησης) Ο καταστροφέας μιας βασικής κλάσης καλείται αυτόματα 9

class A { A() {cout<<"a+"; ~A() {cout<<"a-"; class B : public A { B() {cout<<"b+"; ~B() {cout<<"b-"; void main( int argc, char* argv[]) { B * b = new B(); delete b; Έξοδος: Α+Β+Β-Α- Δέσμευση Μνήμης: (Β * b = new B()) Κλήση Κατασκευαστή Προγόνου: A() Κλήση Κατασκευαστή: B() Κλήση Καταστροφέα: ~B() Κλήση Καταστροφέα Προγόνου: ~A() Αποδέσμευση Μνήμης 10

Είναι καλή πρακτική η κάθε κλάση να δεσμεύει τη μνήμη για τα δικά της πεδία (όπου απαιτείται) και να τα αποδεσμεύει η ίδια Δηλαδή να μην περιμένει ότι κάποια κλάση απόγονος θα αναλάβει αυτή τη δουλειά 11

class A { char * buffer; A(int size) { buffer= new char[size]; ~A() { delete [] buffer; class B : public A { B() : A(10) { ~B() { class A { protected: A() { ~A() { char * buffer; class B : public A { B(int size) : A() { buffer = new char[size]; ~B() { delete [] buffer; class A { char * buffer; A(int size) {buffer = new char[size]; ~A() {delete [] buffer; class B : public A { B() : A(10) { ~B() {delete [] buffer; 12

Αν έχουμε κατασκευαστή αντιγραφής σε μια παράγωγη κλάση, τότε καλείται ο αντίστοιχος κατασκευαστής αντιγραφής της βασικής κλάσης Ο κατασκευαστής αντιγραφής της βασικής κλάσης χρησιμοποιεί μόνο το τμήμα της παράγωγης κλάσης που αντιστοιχεί στα πεδία της βασικής κλάσης για την αρχικοποίησή του 13

class A { char * buffer; int size; A(const int size) : size(size) { buffer = new char[size]; A(const A & src) : size(src.size) { buffer = new char[size]; ~A() {delete [] buffer; class B : public A { char fill_char; B(const int size, const char fill) : A(size), fill_char(fill) { B(const B & src) : fill_char(src.fill_char), ~B() { A(src){ char * buffer int size char * buffer int size char fill_char Πεδία της Β Πεδία της Α 14

Όπως είδαμε, μπορούμε να τροποποιήσουμε μια μέθοδο μιας παράγωγης κλάσης. Όμως: Η καινούρια μέθοδος ανήκει στην παράγωγη κλάση και είναι στατικά συσχετισμένη μαζί της. Κοινώς: Συνυπάρχει η μέθοδος της βασικής κλάσης με την μέθοδο της παράγωγης κλάσης! Ανάλογα με το πώς είναι δηλωμένο ένα στιγμιότυπο μια δεδομένη στιγμή, θα κληθεί και η αντίστοιχη μέθοδος 15

class Person { string name; Person(const string& namein) : name(namein) { void print() const { cout << name ; class Student : public Person { string code; Student(const string& namein, const string& codein) : Person(nameIn), code(codein) { void print() const { Person::print(); cout << code; 16

int main() { Person p("νίκος"); p.print(); // Τυπώνει «Νίκος». Student s("γιώργος", "p30100"); s.print(); // Τυπώνει «Γιώργοςp30100». Student* p_student = &s; Person* p_person = &s; // Δείκτης Student* // Δείκτης Person* που δείχνει // σε ένα Student (το s). // Επιτρέπεται γιατί η Student // είναι παράγωγη της Person. p_person->print(); // Τυπώνει «Γιώργος». Χρησιμοποιεί την // print της Person, παρ' όλο που ο // δείκτης δείχνει σε αντικείμενο // Student. 17

Ένας δείκτης βασικής κλάσης (π.χ. Person*) επιτρέπεται να δείχνει σε αντικείμενο παράγωγης κλάσης (π.χ. Student). Αν όμως κληθεί χρησιμοποιώντας το δείκτη της βασικής κλάσης μια μέθοδος (π.χ. print) που τροποποιήθηκε στην παράγωγη κλάση, χρησιμοποιείται η μορφή που είχε η μέθοδος στη βασική τάξη. Δηλαδή το ποια μορφή της μεθόδου θα χρησιμοποιηθεί καθορίζεται από τον τύπο του δείκτη. Συχνά θέλουμε, όμως, να καθορίζεται από τον τύπο (κλάση) του αντικειμένου στο οποίο δείχνει ο δείκτης. Για να συμβεί αυτό πρέπει να δηλώσουμε τη μέθοδο ως εικονική (virtual). 18

class Person { string name; Person(const string& namein) : name(namein) { virtual void print() const { cout << name ; class Student : public Person { string code; Student(const string& namein, const string& codein) : Person(nameIn), code(codein) { // Δε χρειάζεται να ξαναγράψουμε virtual: void print() const { Person::print(); cout << code; 19

int main() { // Η print είναι τώρα εικονική. Person p("νίκος"); Student s("γιώργος", "p30100"); Person* p_person = &s; // Δείκτης Person* που δείχνει στο s. p_person->print(); // Τώρα τυπώνει «Γιώργοςp30100». // Δηλαδή χρησιμοποιεί την // print της Student. // Το ποια μέθοδος θα // χρησιμοποιηθεί το καθορίζει τώρα ο // τύπος του αντικειμένου, όχι ο τύπος // του δείκτη. 20

Στην απλή τροποποίηση μεθόδου, το ποια μέθοδος θα κληθεί καθορίζεται κατά τη μεταγλώττιση του κώδικα με βάση τον τύπο της μεταβλητής Όταν μια μέθοδος είναι virtual, η πραγματική μέθοδος που θα κληθεί καθορίζεται κατά την εκτέλεση Για να μπορέσει να γίνει αυτό, όταν μεταγλωττίζεται ο κώδικας αντί να προσδιοριστεί η θέση στη μνήμη (Offset) που βρίσκεται η μέθοδος προς κλήση, δηλώνεται μια μεταβλητή «δείκτη προς μέθοδο», το περιεχόμενο της οποίας καθορίζεται με τη δέσμευση του αντικειμένου 21

Non-virtual print() method class Person Κώδικας της print() main () Στιγμιότυπο s (Student) string name string code class Student pointer to base class Κώδικας της print() Κλήση συνάρτησης ( Person::print() ) int main() { Student s("γιώργος", "p30100"); Person* p_person = &s; p_person->print(); 22

Virtual print() method class Person Κώδικας της virtual print() class Student pointer to base class Κώδικας της virtual print() main () Κλήση συνάρτησης (p_print) int main() { Στιγμιότυπο s (Student) (void*)(void) p_print string name string code Student s("γιώργος", "p30100"); Person* p_person = &s; p_person->print(); 23

H τροποποιημένη μέθοδος δεν επισκιάζει πάντα την μέθοδος της βασικής κλάσης. Αυτό εξαρτάται από το αν μια μέθοδος είναι εικονική ή όχι Όλες οι τροποποιημένες μέθοδοι είναι εικονικές Εμείς καθορίζουμε πότε μια μέθοδος είναι εικονική Το ποια μέθοδος θα κληθεί καθορίζεται κατά την εκτέλεση του κώδικα μόνο για τις εικονικές μεθόδους Το ποια μέθοδος θα κληθεί καθορίζεται πάντα κατά την εκτέλεση του κώδικα Για τις υπόλοιπες, καθορίζεται κατά τη μεταγλώττιση 24

Είδαμε παραπάνω πως ο compiler προκειμένου να υλοποιήσει το μηχανισμό των virtual μεθόδων, δημιουργεί δεδομένα μέσα στα στιγμιότυπα τύπου «δείκτη σε συνάρτηση» (μέθοδο) Αυτό γενικά επιτρέπεται στη C και στη C++: Μπορούμε να δηλώνουμε δείκτες σε συναρτήσεις ως μεταβλητές Μπορούμε να περνάμε ως ορίσματα και να επιστρέφουμε δείκτες σε συναρτήσεις Μπορούμε να χειριζόμαστε κανονικά τους δείκτες σε συναρτήσεις ως δεδομένα 25

Δήλωση δείκτη σε συνάρτηση: RETURN_VAL (*POINTER_NAME) (ARG_LIST) Παράδειγμα: void printdecorated(string name) {cout << \ <<name<< \ ; void printundecorated(string name) {cout <<name; bool use_decoration = false; cin >> use_decoration; void (*print_func)(string) = nullptr; printf_func = use_decoration? printdecorated : printundecorated; // Έχουμε ορίσει μια κλάση StilizedOutput με κατασκευαστή που // παίρνει ως όρισμα void (*)(string) StilizedOutput * output = new StilizedOutput(printf_func); output->print( Nikolas ); output->print( Maria ); 26

27 void display(const Person& pers) { pers.print(); // Η print είναι εικονική. Το ποια print θα // χρησιμοποιηθεί το καθορίζει ο τύπος του // αντικειμένου, όχι ο τύπος της αναφοράς. int main() { Person p("νίκος"); Student s("γιώργος", "p30100"); display(p); // Τυπώνει «Νίκος». display(s); // Τυπώνει «Γιώργοςp30100».

void display(person pers) { pers.print(); // Η print είναι εικονική, αλλά κατά τη // μεταβίβαση δημιουργείται ένα τοπικό // αντίγραφο του pers που είναι Person, // ακόμα κι αν το αρχικό ήταν Student. // Το τοπικό αντίγραφο δεν έχει τα // επιπλέον μέλη της Student). // Το ποια μορφή της print() θα // χρησιμοποιηθεί το καθορίζει ο τύπος του // τοπικού αντιγράφου (Person). int main() { Person p("νίκος"); Student s("γιώργος", "p30100"); display(p); // Τυπώνει «Νίκος». display(s); // Τυπώνει «Γιώργος», σαν να ήταν το s Person. 28

Όπως αναφέρθηκε, οι καταστροφείς των κληρονομημένων κλάσεων καλούν αυτόματα αναδρομικά και τους καταστροφείς των προγόνων Τι γίνεται αν δε θέλουμε να συμβεί αυτό; Σε ποιες περιπτώσεις μπορεί να μη θέλουμε; Το πρόβλημα προκύπτει συνήθως όταν: Καταστρέφουμε αντικείμενο που έχει δηλωθεί ως πρόγονος της πραγματικής κλάσης που είναι 29

class Employee { string name; Employee (string e_name) {name=e_name; class Manager : public Employee { Employee ** employees; unsigned int num_employees; Manager (string m_name) : Employee(m_name), employees(nullptr) { void addsubordinate(employee * employee) {... ~Manager() {delete [] employees; // πρέπει να διαγράψω τη λίστα // των υπαλλήλων όταν καταστρέψω // το αντικείμενο τύπου Manager 30

int main() { Employee * staff[3]; staff[0] = new Employee( Μαρία ); staff[1] = new Employee( Γιάννης ); staff[2] = new Manager( Δήμητρα ); staff[2]->addsubordinate(staff[0]); staff[2]->addsubordinate(staff[1]);... for (int i=0; i<3; i++) { delete staff[i]; Δε διαχωρίζεται από την αρχή ο ακριβής τύπος του «υπαλλήλου». Έχω μια κοινή λίστα με όλους Δημιουργώ ένα στιγμιότυπο derived class του οποίου όμως ο δηλωμένος τύπος παραμένει Employee Για όλα θα κληθεί ο καταστροφέας του δηλωμένου τύπου: ~Employee() Δε θα κληθεί ο καταστροφέας της derived class Manager για τη μεταβλητή staff[2] Δε θα αποδεσμευθεί ποτέ η μνήμη του πεδίου Manager::employees 31

class Employee { string name; Employee (string e_name) {name=e_name; virtual ~Employee() { // μπορώ να το δηλώσω είτε εδώ είτε // στον απόγονο που πραγματικά // χρειάζεται το virtual καταστροφέα class Manager : public Employee { Employee ** employees; unsigned int num_employees; Manager (string m_name) : Employee(m_name), employees(nullptr) { void addsubordinate(employee * employee) {... ~Manager() {delete [] employees; // Ο καταστροφέας έχει δηλωθεί // virtual στον πρόγονο οπότε // συνεχίζει να ισχύει η δήλωση 32

int main() { Employee * staff[3]; staff[0] = new Employee( Μαρία ); staff[1] = new Employee( Γιάννης ); staff[2] = new Manager( Δήμητρα ); staff[2]->addsubordinate(staff[0]); staff[2]->addsubordinate(staff[1]);... for (int i=0; i<3; i++) { delete staff[i]; Τώρα πλέον θα κληθεί ο καταστροφέας της κλάσης που πραγματικά είναι το staff[2] 33

Αν θέλετε μια τάξη να μπορεί να χρησιμοποιηθεί ως βασική για παράγωγες τάξεις, κάντε τον καταστροφέα της εικονικό. Έστω κι αν το σώμα του είναι κενό. Προσοχή: Οι κατασκευαστές απαγορεύεται να είναι εικονικοί. Αν θέλετε μια μέθοδος να μπορεί να τροποποιηθεί σε παραγόμενες τάξεις, κάντε την εικονική. Δεν είναι προεπιλογή της C++, επειδή η χρήση εικονικών μεθόδων κάνει τον εκτελέσιμο κώδικα πιο αργό και απαιτεί περισσότερη μνήμη. 34

Οι καθαρά εικονικές μέθοδοι δεν έχουν κυρίως μέρος. Υπόσχονται ότι θα οριστούν σε παράγωγες κλάσεις Μια κλάση που περιέχει τουλάχιστον μια καθαρά εικονική μέθοδο λέγεται αφηρημένη Δεν επιτρέπεται η δημιουργία αντικειμένων αφηρημένων κλάσεων Οι αφηρημένες κλάσεις χρησιμοποιούνται συχνά ως προδιαγραφές (Interfaces) ειδικότερων κλάσεων: Μπορούμε να προδιαγράψουμε στις αφηρημένες κλάσεις μεθόδους που είναι κοινές στις παράγωγες κλάσεις τους. 35

// Αφηρημένη κλάση: προδιαγράφει τη μορφή των μεθόδων και ενδεχομένως, // των πεδίων, αλλά δε δίνει συγκεκριμένη υλοποίηση, ούτε καν κενή: // Υποχρεώνει τις παράγωγες κλάσεις να προσδιορίσουν την υλοποίηση class Vehicle { protected: float horsepower; virtual void calculatehorsepower() = 0; Class Car : public Vehicle { calculatehorsepower() { // Συγκεκριμένη υλοποίηση μεθόδου... // του (interface) Vehicle Class Truck : public Vehicle { calculatehorsepower() { // Συγκεκριμένη υλοποίηση μεθόδου... // του (interface) Vehicle 36

Στη c++ επιτρέπεται η πολλαπλή κληρονομικότητα κλάσεων: Μια κλάση μπορεί να προέρχεται από πάνω από μία ισότιμες κλάσεις Η παράγωγη κλάση κληρονομεί τα πεδία και τις μεθόδους όλων των βασικών κλάσεων Μία παράγωγη κλάση μπορεί να προέρχεται από δύο ή παραπάνω κλάσεις με κοινό πρόγονο Σε περίπτωση ταυτόσημων μεθόδων και πεδίων, ορίζεται σαφής μηχανισμός για το ποιάς βασικής κλάσης τις μεθόδους καλούμε ανά πάσα στιγμή 37

class A { /*... */ class B { /*... */ class C { /*... */ class X : public A, private B, public C { /*... */ class L { /*... */ // indirect base class class B2 : public L { /*... */ class B3 : public L { /*... */ class D : public B2, public B3 { /*... */ class L { /*... */ // indirect base class class B2 : virtual public L { /*... */ class B3 : virtual public L { /*... */ class D : public B2, public B3 { /*... */ 38

Αν κατά την πολλαπλή κληρονομικότητα προκύπτει κοινή κλάση από 2+ κληρονομικά μονοπάτια, τότε το κάθε μονοπάτι θεωρεί δική του έκδοση της κοινής κλάσης, Εκτός αν ορίσουμε τον κοινό πρόγονο ως virtual class, οπότε όλες οι κλάσεις αναφέρονται στην ίδια κοινή βασική κλάση virtual virtual 39

Οι παράγωγη κλάση κληρονομεί την ένωση των πεδίων και μεθόδων των προγόνων Αν υπάρχει κοινή ονομασία πεδίων και υπογραφή συναρτήσεων, εξειδικεύουμε την κλάση στην οποία αναφερόμαστε με:.classname::method() για μεθόδους και.classname::field για πεδία (παράδειγμα στην επόμενη διαφάνεια) Συνεχίζουμε να μπορούμε να τροποποιήσουμε (override) κληρονομημένες μεθόδους 40

class USBDevice { private: long m_lid; USBDevice(long lid) : m_lid(lid) { long GetID() { return m_lid; class NetworkDevice { private: long m_lid; NetworkDevice(long lid) : m_lid(lid){ long GetID() { return m_lid; class WirelessAdapter: public USBDevice, public NetworkDevice { WirelessAdapter(long lusbid, long lnetworkid) : USBDevice(lUSBID), NetworkDevice(lNetworkID) { int main() { WirelessAdapter c54g(5442, 181742); cout << c54g.getid(); // ποια GetID() θα κληθεί; Compilation Error return 0; 41

class USBDevice { private: long m_lid; USBDevice(long lid) : m_lid(lid) { long GetID() { return m_lid; class NetworkDevice { private: long m_lid; NetworkDevice(long lid) : m_lid(lid){ long GetID() { return m_lid; class WirelessAdaptor: public USBDevice, public NetworkDevice { WirelessAdaptor(long lusbid, long lnetworkid) : USBDevice(lUSBID), NetworkDevice(lNetworkID) { int main() { WirelessAdaptor c54g(5442, 181742); cout << c54g.usbdevice::getid(); // OK! return 0; 42

Η πολλαπλή κληρονομικότητα γενικά βασίζεται στο επιχείρημα ότι οι βασικές κλάσεις είναι όσο το δυνατό αποσυσχετισμένες: Δεν έχει νόημα να κάνουμε πολλαπλή κληρονομικότητα σε κλάσεις της ίδιας κληρονομικής αλυσίδας Για να έχουμε 2 ανεξάρτητες βασικές κλάσεις, αυτές θα πρέπει να διαφέρουν και εννοιολογικά, αλλιώς θα είχαν σχέση προγόνου - απογόνου Από κακό σχεδιασμό ή κοινούς προγόνους, μπορούν να προκύψουν σοβαρά προβλήματα (είδαμε ένα στις 2 προηγούμενες διαφάνειες) 43

Στην πολλαπλή κληρονομικότητα σκεφτόμαστε τις βασικές κλάσεις ως πρότυπα κλάσεων περισσότερο παρά ως πλήρως υλοποιημένες κλάσεις: Οι βασικές κλάσεις στην πολλαπλή κληρονομικότητα πρέπει να: προσδιορίζουν αφηρημένες ιδιότητες Ισοδυναμούν με τα Interfaces της Java 44

Geometry Drawable vector<vec3> points virtual void draw() = 0 Surface float area Color paint virtual void calculatearea()=0 void setcolor(color c) PhysicsObject vector<force*> forces void addforce(force *) virtual void calculatespeed(float dt) Solid float mass virtual void calculatemass()=0 Box virtual void calculatearea() virtual void draw() virtual void calculatemass() Sphere virtual void calculatearea() virtual void draw() virtual void calculatemass()

Pure virtual μέθοδοι: virtual ret_type methodname(params)=0; Abstract κλάσεις μόνο με pure virtual methods: Συνεχίζουν να λέγονται class, είναι στην ουσία interfaces Επιτρέπεται η πολλαπλή κληρονομικότητα Abstract μέθοδοι: abstract ret_type methodname(params); Abstract κλάσεις μόνο με pure virtual methods: Λέγονται interfaces Δεν επιτρέπεται η πολλαπλή κληρονομικότητα αλλά γίνεται μέσω της υλοποίησης interfaces 46