runtime polymorphism c
Λεπτομερής μελέτη του πολυμορφισμού χρόνου εκτέλεσης στο C ++.
Ο πολυμορφισμός χρόνου εκτέλεσης είναι επίσης γνωστός ως δυναμικός πολυμορφισμός ή καθυστερημένη σύνδεση. Στον πολυμορφισμό του χρόνου εκτέλεσης, η κλήση συνάρτησης επιλύεται στο χρόνο εκτέλεσης.
Αντίθετα, για τη μεταγλώττιση του χρόνου ή του στατικού πολυμορφισμού, ο μεταγλωττιστής συνάγει το αντικείμενο κατά το χρόνο εκτέλεσης και στη συνέχεια αποφασίζει ποια συνάρτηση καλεί να συνδεθεί με το αντικείμενο. Στο C ++, ο πολυμορφισμός χρόνου εκτέλεσης εφαρμόζεται με χρήση μεθόδου παράκαμψης.
Σε αυτό το σεμινάριο, θα διερευνήσουμε λεπτομερώς όλα σχετικά με τον πολυμορφισμό χρόνου εκτέλεσης.
=> Δείτε ΟΛΟΥΣ τα μαθήματα C ++ εδώ.
Τι θα μάθετε:
- Παράκαμψη λειτουργίας
- Εικονική συνάρτηση
- Εργασία εικονικού πίνακα και _vptr
- Καθαρές εικονικές λειτουργίες και αφηρημένη κλάση
- Εικονικοί καταστροφείς
- συμπέρασμα
- Συνιστώμενη ανάγνωση
Παράκαμψη λειτουργίας
Η παράκαμψη της συνάρτησης είναι ο μηχανισμός με τον οποίο μια συνάρτηση που ορίζεται στην κατηγορία βάσης ορίζεται και πάλι στην παράγωγη κλάση. Σε αυτήν την περίπτωση, λέμε ότι η συνάρτηση παρακάμπτεται στην παράγωγη κλάση.
Πρέπει να θυμόμαστε ότι η παράκαμψη λειτουργίας δεν μπορεί να γίνει μέσα σε μια τάξη. Η συνάρτηση παρακάμπτεται μόνο στην παράγωγη κλάση. Ως εκ τούτου, η κληρονομιά πρέπει να είναι παρούσα για αντικατάσταση της λειτουργίας.
Το δεύτερο πράγμα είναι ότι η συνάρτηση από μια βασική κλάση που παρακάμπτουμε θα πρέπει να έχει την ίδια υπογραφή ή πρωτότυπο, δηλαδή θα πρέπει να έχει το ίδιο όνομα, τον ίδιο τύπο επιστροφής και την ίδια λίστα ορισμάτων.
Ας δούμε ένα παράδειγμα που δείχνει την παράκαμψη μεθόδου.
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< Παραγωγή:
Κατηγορία :: Βάση
Κατηγορία :: Προέρχεται
Στο παραπάνω πρόγραμμα, έχουμε μια βασική τάξη και μια παράγωγη τάξη. Στην κλάση βάσης, έχουμε μια συνάρτηση show_val που παρακάμπτεται στην παράγωγη κλάση. Στην κύρια συνάρτηση, δημιουργούμε ένα αντικείμενο κάθε κατηγορίας Base και Derived και καλούμε τη συνάρτηση show_val με κάθε αντικείμενο. Παράγει την επιθυμητή έξοδο.
Η παραπάνω δέσμευση συναρτήσεων χρησιμοποιώντας αντικείμενα κάθε κλάσης είναι ένα παράδειγμα στατικής σύνδεσης.
Τώρα ας δούμε τι συμβαίνει όταν χρησιμοποιούμε το δείκτη κλάσης βάσης και αντιστοιχίζουμε τα παράγωγα αντικείμενα κλάσης ως το περιεχόμενό του.
Το παράδειγμα προγράμματος φαίνεται παρακάτω:
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() //overridden function { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //Early Binding }
Παραγωγή:
Κατηγορία :: Βάση
Τώρα βλέπουμε, ότι η έξοδος είναι 'Class :: Base'. Έτσι, ανεξάρτητα από το είδος του αντικειμένου που κρατά ο δείκτης βάσης, το πρόγραμμα εξάγει τα περιεχόμενα της συνάρτησης της κλάσης της οποίας ο βασικός δείκτης είναι ο τύπος. Σε αυτήν την περίπτωση, πραγματοποιείται επίσης στατική σύνδεση.
Προκειμένου να κάνουμε την έξοδο του δείκτη βάσης, το σωστό περιεχόμενο και τη σωστή σύνδεση, αναζητούμε δυναμική δέσμευση συναρτήσεων. Αυτό επιτυγχάνεται με τη χρήση μηχανισμού εικονικών λειτουργιών που εξηγείται στην επόμενη ενότητα.
η δοκιμή συστημάτων ενός νέου συστήματος πληροφοριών θα πρέπει να εκτελείται από
Εικονική συνάρτηση
Για την παράκαμψη της συνάρτησης πρέπει να συνδέεται δυναμικά με το σώμα της συνάρτησης, κάνουμε τη συνάρτηση βασικής κλάσης εικονική χρησιμοποιώντας την «εικονική» λέξη-κλειδί. Αυτή η εικονική συνάρτηση είναι μια συνάρτηση που παρακάμπτεται στην παράγωγη κλάση και ο μεταγλωττιστής εκτελεί καθυστέρηση ή δυναμική δέσμευση για αυτήν τη συνάρτηση.
Τώρα ας τροποποιήσουμε το παραπάνω πρόγραμμα για να συμπεριλάβουμε την εικονική λέξη-κλειδί ως εξής:
#include using namespace std;. class Base { public: virtual void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //late Binding }
Παραγωγή:
Κατηγορία :: Προέρχεται
Έτσι, στον παραπάνω ορισμό κλάσης της βάσης, κάναμε τη λειτουργία show_val ως «εικονική». Καθώς η συνάρτηση βασικής κλάσης γίνεται εικονική, όταν αντιστοιχίζουμε το παράγωγο αντικείμενο κλάσης σε δείκτη κλάσης βάσης και συνάρτηση show_val κλήσης, η δέσμευση συμβαίνει κατά το χρόνο εκτέλεσης.
Έτσι, καθώς ο δείκτης κατηγορίας βάσης περιέχει παράγωγο αντικείμενο κλάσης, το σώμα συνάρτησης show_val στην κλάση που παράγεται δεσμεύεται να λειτουργεί show_val και ως εκ τούτου η έξοδος.
Στο C ++, η παράκαμψη της παραγόμενης κλάσης μπορεί επίσης να είναι ιδιωτική. Ο μεταγλωττιστής ελέγχει μόνο τον τύπο του αντικειμένου κατά το χρόνο μεταγλώττισης και δεσμεύει τη συνάρτηση στο χρόνο εκτέλεσης, επομένως δεν κάνει καμία διαφορά ακόμη και αν η συνάρτηση είναι δημόσια ή ιδιωτική.
Σημειώστε ότι εάν μια συνάρτηση δηλωθεί εικονική στην βασική κλάση, τότε θα είναι εικονική σε όλες τις παραγόμενες κλάσεις.
Αλλά μέχρι τώρα, δεν έχουμε συζητήσει πώς ακριβώς οι εικονικές συναρτήσεις διαδραματίζουν ρόλο στον προσδιορισμό της σωστής λειτουργίας που θα δεσμευτεί ή με άλλα λόγια, πόσο πραγματικά συμβαίνει η καθυστέρηση δέσμευσης.
Η εικονική συνάρτηση συνδέεται με το σώμα της λειτουργίας με ακρίβεια κατά το χρόνο εκτέλεσης, χρησιμοποιώντας την έννοια του εικονικός πίνακας (VTABLE) και ένα κρυφό δείκτη που ονομάζεται _vptr.
Και οι δύο αυτές έννοιες είναι εσωτερική εφαρμογή και δεν μπορούν να χρησιμοποιηθούν απευθείας από το πρόγραμμα.
καλύτερο λογισμικό για απόκρυψη διεύθυνσης IP
Εργασία εικονικού πίνακα και _vptr
Αρχικά, ας καταλάβουμε τι είναι ένας εικονικός πίνακας (VTABLE).
Ο μεταγλωττιστής κατά το χρόνο μεταγλώττισης δημιουργεί ένα VTABLE το καθένα για μια τάξη που έχει εικονικές συναρτήσεις καθώς και τις κλάσεις που προέρχονται από τάξεις με εικονικές συναρτήσεις.
Ένα VTABLE περιέχει καταχωρήσεις που είναι δείκτες συνάρτησης στις εικονικές συναρτήσεις που μπορούν να κληθούν από τα αντικείμενα της κλάσης. Υπάρχει μια καταχώριση δείκτη λειτουργίας για κάθε εικονική λειτουργία.
Στην περίπτωση καθαρών εικονικών λειτουργιών, αυτή η καταχώριση είναι NULL. (Αυτός είναι ο λόγος για τον οποίο δεν μπορούμε να δείξουμε την αφηρημένη τάξη).
Επόμενη οντότητα, το _vptr που ονομάζεται δείκτης vtable είναι ένας κρυμμένος δείκτης που ο μεταγλωττιστής προσθέτει στην βασική κλάση. Αυτό το _vptr δείχνει το vtable της τάξης. Όλες οι τάξεις που προέρχονται από αυτήν τη βασική κλάση κληρονομούν το _vptr.
Κάθε αντικείμενο μιας κλάσης που περιέχει τις εικονικές συναρτήσεις αποθηκεύει εσωτερικά αυτό το _vptr και είναι διαφανές για τον χρήστη. Κάθε κλήση σε εικονική λειτουργία που χρησιμοποιεί ένα αντικείμενο επιλύεται στη συνέχεια χρησιμοποιώντας αυτό το _vptr.
Ας πάρουμε ένα παράδειγμα για να δείξουμε τη λειτουργία του vtable και του _vtr.
#include using namespace std; class Base_virtual { public: virtual void function1_virtual() {cout<<'Base :: function1_virtual()
';}; virtual void function2_virtual() {cout<<'Base :: function2_virtual()
';}; virtual ~Base_virtual(){}; }; class Derived1_virtual: public Base_virtual { public: ~Derived1_virtual(){}; virtual void function1_virtual() { coutfunction2_virtual(); delete (b); return (0); }
Παραγωγή:
Derived1_virtual :: function1_virtual ()
Βάση :: function2_virtual ()
Στο παραπάνω πρόγραμμα, έχουμε μια βασική τάξη με δύο εικονικές λειτουργίες και έναν εικονικό καταστροφέα. Έχουμε επίσης παράγει μια τάξη από την βασική τάξη και σε αυτήν. έχουμε παρακάμψει μόνο μία εικονική συνάρτηση. Στην κύρια συνάρτηση, ο παραγόμενος δείκτης κατηγορίας αντιστοιχεί στον βασικό δείκτη.
Στη συνέχεια καλούμε και τις δύο εικονικές συναρτήσεις χρησιμοποιώντας ένα δείκτη κατηγορίας βάσης. Βλέπουμε ότι η παράκαμψη συνάρτησης καλείται όταν καλείται και όχι η βασική συνάρτηση. Ενώ στη δεύτερη περίπτωση, καθώς η συνάρτηση δεν παρακάμπτεται, καλείται η συνάρτηση βασικής κλάσης.
Τώρα ας δούμε πώς το παραπάνω πρόγραμμα αντιπροσωπεύεται εσωτερικά χρησιμοποιώντας vtable και _vptr.
Σύμφωνα με την προηγούμενη εξήγηση, καθώς υπάρχουν δύο τάξεις με εικονικές συναρτήσεις, θα έχουμε δύο vtables - μία για κάθε τάξη. Επίσης, το _vptr θα είναι παρόν για τη βασική τάξη.

Παρακάτω φαίνεται η εικονική αναπαράσταση του πώς θα είναι η διάταξη του vtable για το παραπάνω πρόγραμμα. Το vtable για την κατηγορία βάσης είναι απλό. Στην περίπτωση της παραγόμενης κλάσης, μόνο το function1_virtual παρακάμπτεται.
Επομένως βλέπουμε ότι στην παράγωγη κλάση vtable, ο δείκτης συνάρτησης για το function1_virtual δείχνει την παράκαμψη της συνάρτησης στην παράγωγη κλάση. Από την άλλη πλευρά, ο δείκτης λειτουργίας για το function2_virtual δείχνει μια συνάρτηση στην κατηγορία βάσης.
Έτσι, στο παραπάνω πρόγραμμα, όταν ο βασικός δείκτης έχει αντιστοιχιστεί ένα αντικείμενο παράγωγης κλάσης, ο δείκτης βάσης δείχνει το _vptr της παραγόμενης κλάσης.
Έτσι, όταν πραγματοποιείται η κλήση b-> function1_virtual (), η function1_virtual από την κλάση που προέρχεται και όταν πραγματοποιείται η κλήση συνάρτησης b-> function2_virtual (), καθώς αυτός ο δείκτης συνάρτησης δείχνει τη συνάρτηση βασικής κλάσης, η συνάρτηση βασικής κλάσης λέγεται.
Καθαρές εικονικές λειτουργίες και αφηρημένη κλάση
Έχουμε δει λεπτομέρειες σχετικά με τις εικονικές λειτουργίες στο C ++ στην προηγούμενη ενότητα μας. Στο C ++, μπορούμε επίσης να ορίσουμε ένα « καθαρή εικονική λειτουργία 'Που συνήθως ισούται με μηδέν.
Η καθαρή εικονική συνάρτηση δηλώνεται όπως φαίνεται παρακάτω.
virtual return_type function_name(arg list) = 0;
Η τάξη που έχει τουλάχιστον μία καθαρή εικονική συνάρτηση που ονομάζεται ' αφηρημένη τάξη '. Δεν μπορούμε ποτέ να δημιουργήσουμε instantiate την αφηρημένη τάξη, δηλαδή δεν μπορούμε να δημιουργήσουμε ένα αντικείμενο της αφηρημένης τάξης.
Αυτό συμβαίνει επειδή γνωρίζουμε ότι γίνεται μια καταχώριση για κάθε εικονική λειτουργία στο VTABLE (εικονικός πίνακας). Αλλά σε περίπτωση καθαρής εικονικής λειτουργίας, αυτή η καταχώρηση δεν έχει καμία διεύθυνση καθιστώντας την ελλιπή. Επομένως, ο μεταγλωττιστής δεν επιτρέπει τη δημιουργία αντικειμένου για την τάξη με ελλιπή καταχώριση VTABLE.
Αυτός είναι ο λόγος για τον οποίο δεν μπορούμε να δημιουργήσουμε μια αφηρημένη τάξη.
Το παρακάτω παράδειγμα θα δείξει καθαρή εικονική λειτουργία, καθώς και κλάση Abstract.
#include using namespace std; class Base_abstract { public: virtual void print() = 0; // Pure Virtual Function }; class Derived_class:public Base_abstract { public: void print() { cout <<'Overriding pure virtual function in derived class
'; } }; int main() { // Base obj; //Compile Time Error Base_abstract *b; Derived_class d; b = &d; b->print(); }
Παραγωγή:
Παράκαμψη καθαρής εικονικής λειτουργίας στην παράγωγη κλάση
Στο παραπάνω πρόγραμμα, έχουμε μια κλάση που ορίζεται ως Base_abstract που περιέχει μια καθαρή εικονική συνάρτηση που την καθιστά μια αφηρημένη τάξη. Στη συνέχεια, αντλούμε μια κλάση 'Derived_class' από το Base_ab Abstract και παρακάμπτουμε την καθαρή εικονική λειτουργία εκτύπωσης σε αυτήν.
Στην κύρια συνάρτηση, δεν σχολιάζεται η πρώτη γραμμή. Αυτό συμβαίνει επειδή, εάν το απογοητεύσουμε, ο μεταγλωττιστής θα δώσει ένα σφάλμα, καθώς δεν μπορούμε να δημιουργήσουμε ένα αντικείμενο για μια αφηρημένη κλάση.
Αλλά η δεύτερη γραμμή και μετά ο κώδικας λειτουργεί. Μπορούμε να δημιουργήσουμε με επιτυχία ένα δείκτη κλάσης βάσης και μετά να του αντιστοιχίσουμε αντικείμενο κλάσης. Στη συνέχεια, καλούμε μια συνάρτηση εκτύπωσης που εξάγει τα περιεχόμενα της συνάρτησης εκτύπωσης που παρακάμπτονται στην παράγωγη κλάση.
Ας απαριθμήσουμε εν συντομία ορισμένα χαρακτηριστικά της αφηρημένης τάξης:
- Δεν μπορούμε να δημιουργήσουμε μια αφηρημένη τάξη.
- Μια αφηρημένη κλάση περιέχει τουλάχιστον μία καθαρή εικονική συνάρτηση.
- Αν και δεν μπορούμε να δημιουργήσουμε την αφηρημένη τάξη, μπορούμε πάντα να δημιουργήσουμε δείκτες ή αναφορές σε αυτήν την τάξη.
- Μια αφηρημένη τάξη μπορεί να έχει κάποια εφαρμογή όπως ιδιότητες και μεθόδους μαζί με καθαρές εικονικές λειτουργίες.
- Όταν αντλούμε μια κλάση από την αφηρημένη τάξη, η παράγωγη κλάση θα πρέπει να παρακάμψει όλες τις καθαρές εικονικές συναρτήσεις στην αφηρημένη τάξη. Εάν δεν το έκανε, τότε η παράγωγη τάξη θα είναι επίσης μια αφηρημένη τάξη.
Εικονικοί καταστροφείς
Οι καταστροφείς της τάξης μπορούν να δηλωθούν ως εικονικοί. Κάθε φορά που κάνουμε αναβάθμιση, δηλαδή εκχώρηση του παραγόμενου αντικειμένου κλάσης σε δείκτη κατηγορίας βάσης, οι συνηθισμένοι καταστροφείς μπορούν να παράγουν μη αποδεκτά αποτελέσματα.
Για παράδειγμα,εξετάστε την ακόλουθη αναβάθμιση του συνηθισμένου καταστροφέα.
#include using namespace std; class Base { public: ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Παραγωγή:
Βασική κατηγορία :: Καταστροφέας
Στο παραπάνω πρόγραμμα, έχουμε μια κληρονομική παράγωγη κλάση από την βασική τάξη. Κατά κύριο λόγο, εκχωρούμε ένα αντικείμενο της παραγόμενης κλάσης σε έναν δείκτη κατηγορίας βάσης.
Στην ιδανική περίπτωση, ο καταστροφέας που καλείται όταν καλείται 'διαγραφή b' θα έπρεπε να ήταν αυτός της παραγόμενης κλάσης, αλλά μπορούμε να δούμε από την έξοδο ότι ο καταστροφέας της βασικής κλάσης καλείται ως δείκτης κλάσης βάσης δείχνει σε αυτό.
Λόγω αυτού, ο παράγοντας καταστροφής κλάσης δεν καλείται και το παράγωγο αντικείμενο κλάσης παραμένει άθικτο με αποτέλεσμα τη διαρροή μνήμης. Η λύση σε αυτό είναι να κάνετε τον κατασκευαστή βασικής κλάσης εικονικό έτσι ώστε ο δείκτης αντικειμένου να δείχνει τη σωστή καταστροφή και την σωστή καταστροφή αντικειμένων.
Η χρήση εικονικού καταστροφέα φαίνεται στο παρακάτω παράδειγμα.
#include using namespace std; class Base { public: virtual ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Παραγωγή:
Παράγωγη τάξη :: Καταστροφέας
Βασική κατηγορία :: Καταστροφέας
Αυτό είναι το ίδιο πρόγραμμα με το προηγούμενο πρόγραμμα, εκτός από το ότι έχουμε προσθέσει μια εικονική λέξη-κλειδί μπροστά από τον καταστροφέα βασικής κλάσης. Κάνοντας τον καταστροφέα βασικής κατηγορίας εικονικό, έχουμε επιτύχει την επιθυμητή έξοδο.
Μπορούμε να δούμε ότι όταν εκχωρούμε το αντικείμενο κλάσης σε δείκτη κατηγορίας βάσης και μετά διαγράφουμε το δείκτη κλάσης βάσης, οι καταστροφείς καλούνται με την αντίστροφη σειρά της δημιουργίας αντικειμένων. Αυτό σημαίνει ότι πρώτα ο παραγόμενος καταστροφέας κλάσης καλείται και το αντικείμενο καταστρέφεται και έπειτα το αντικείμενο βασικής κατηγορίας καταστρέφεται.
το στάδιο του κύκλου ανάπτυξης λογισμικού στον οποίο διεξάγεται ο προγραμματισμός είναι:
Σημείωση: Στο C ++, οι κατασκευαστές δεν μπορούν ποτέ να είναι εικονικοί, καθώς οι κατασκευαστές συμμετέχουν στην κατασκευή και την αρχικοποίηση των αντικειμένων. Ως εκ τούτου, όλοι οι κατασκευαστές πρέπει να εκτελεστούν πλήρως.
συμπέρασμα
Ο πολυμορφισμός χρόνου εκτέλεσης πραγματοποιείται με χρήση μεθόδου παράκαμψης. Αυτό λειτουργεί καλά όταν καλούμε τις μεθόδους με τα αντίστοιχα αντικείμενα. Αλλά όταν έχουμε δείκτη κατηγορίας βάσης και καλούμε μεθόδους παράκαμψης χρησιμοποιώντας το δείκτη κλάσης βάσης που δείχνει τα παραγόμενα αντικείμενα κλάσης, προκύπτουν απροσδόκητα αποτελέσματα λόγω στατικής σύνδεσης.
Για να το ξεπεράσουμε, χρησιμοποιούμε την έννοια των εικονικών συναρτήσεων. Με την εσωτερική αναπαράσταση των vtables και _vptr, οι εικονικές συναρτήσεις μας βοηθούν να καλέσουμε με ακρίβεια τις επιθυμητές συναρτήσεις. Σε αυτό το σεμινάριο, έχουμε δει λεπτομερώς σχετικά με τον πολυμορφισμό χρόνου εκτέλεσης που χρησιμοποιείται στο C ++.
Με αυτό, ολοκληρώνουμε τα σεμινάρια μας σχετικά με τον αντικειμενοστρεφή προγραμματισμό στο C ++. Ελπίζουμε ότι αυτό το σεμινάριο θα είναι χρήσιμο για να αποκτήσετε μια καλύτερη και διεξοδική κατανόηση των αντικειμενοστρεφών προγραμματιστικών εννοιών στο C ++.
=> Επισκεφθείτε εδώ για να μάθετε C ++ από το μηδέν.
Συνιστώμενη ανάγνωση
- Πολυμορφισμός σε C ++
- Κληρονομικότητα σε C ++
- Λειτουργίες φίλων στο C ++
- Μαθήματα και αντικείμενα σε C ++
- Χρήση του Selenium Select Class για το χειρισμό των αναπτυσσόμενων στοιχείων σε μια ιστοσελίδα - Selenium Tutorial # 13
- Εκπαιδευτικό πρόγραμμα Python Main Function με πρακτικά παραδείγματα
- Java Virtual Machine: Πώς βοηθά το JVM στην εκτέλεση της εφαρμογής Java
- Τρόπος εγκατάστασης αρχείων δέσμης ενεργειών LoadRunner VuGen και ρυθμίσεων χρόνου εκτέλεσης