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

Σε αυτό το άρθρο, θα συζητήσουμε το Διαμαντένιο Πρόβλημα, πώς προκύπτει από την πολλαπλή κληρονομικότητα και τι μπορείτε να κάνετε για να επιλύσετε το ζήτημα.

Πολλαπλή κληρονομικότητα σε C ++

Η πολλαπλή κληρονομικότητα είναι α χαρακτηριστικό του αντικειμενοστραφούς προγραμματισμού (OOP) όπου μια υποκατηγορία μπορεί να κληρονομήσει από περισσότερες από μία υπερκατηγορίες. Με άλλα λόγια, μια παιδική τάξη μπορεί να έχει περισσότερους από έναν γονείς.

Το παρακάτω σχήμα δείχνει μια εικονική αναπαράσταση πολλαπλών κληρονομιών.

Στο παραπάνω διάγραμμα, τάξη Γ έχει τάξη Α και κατηγορία Β ως γονείς του.

Αν εξετάσουμε ένα σενάριο πραγματικής ζωής, ένα παιδί κληρονομεί από τον πατέρα και τη μητέρα του. Έτσι, ένα Παιδί μπορεί να αναπαρασταθεί ως παράγωγη τάξη με γονείς τον «Πατέρα» και τη «Μητέρα». Ομοίως, μπορούμε να έχουμε πολλά τέτοια παραδείγματα πολλαπλής κληρονομικότητας στην πραγματική ζωή.

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

Ας απεικονίσουμε τώρα την πολλαπλή κληρονομικότητα και επαληθεύστε τη σειρά κατασκευής και καταστροφής αντικειμένων.

Εικονογράφηση κώδικα πολλαπλής κληρονομικότητας

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

#περιλαμβάνω
χρησιμοποιώντας το όνομα χώρου std.
κλάση Α // βασική κλάση Α με κατασκευαστή και καταστροφέα
{
δημόσιο:
A () {cout << "class A:: Constructor" << endl; }
A () {cout << "class A:: Destructor" << endl; }
};
κλάση Β // βασική κατηγορία Β με κατασκευαστή και καταστροφέα
{
δημόσιο:
B () {cout << "class B:: Constructor" << endl; }
B () {cout << "class B:: Destructor" << endl; }
};
κλάση C: δημόσια Β, δημόσια Α // η κλάση C που προέρχεται από την κληρονομιά της κλάσης Α και μετά την κλάση Β (σημειώστε τη σειρά)
{
δημόσιο:
C () {cout << "class C:: Constructor" << endl; }
C () {cout << "class C:: Destructor" << endl; }
};
int main () {
Γ γ;
επιστροφή 0?
}

Η έξοδος που λαμβάνουμε από το παραπάνω πρόγραμμα είναι η ακόλουθη:

κατηγορία Β:: Κατασκευαστής
κλάση Α:: Κατασκευαστής
κλάση C:: Κατασκευαστής
κατηγορία Γ:: Καταστρεπτικός
κατηγορία Α:: Καταστρεπτικός
κατηγορία Β:: Καταστροφέας

Τώρα αν ελέγξουμε την έξοδο, βλέπουμε ότι οι κατασκευαστές καλούνται με τη σειρά B, A και C ενώ οι καταστροφείς είναι στην αντίστροφη σειρά. Τώρα που γνωρίζουμε τα βασικά της πολλαπλής κληρονομικότητας, προχωράμε στη συζήτηση για το Διαμαντένιο Πρόβλημα.

Το Διαμαντένιο Πρόβλημα, Εξηγημένο

Το Διαμαντένιο Πρόβλημα εμφανίζεται όταν μια παιδική τάξη κληρονομεί από δύο τάξεις γονέων που και οι δύο μοιράζονται μια κοινή τάξη παππούδων. Αυτό απεικονίζεται στο παρακάτω διάγραμμα:

Εδώ έχουμε ένα μάθημα Παιδί κληρονομώντας από τις τάξεις Πατέρας και Μητέρα. Αυτές οι δύο κατηγορίες, με τη σειρά τους, κληρονομούν την τάξη Πρόσωπο γιατί και ο πατέρας και η μητέρα είναι Πρόσωπο.

Όπως φαίνεται στο σχήμα, η τάξη Παιδί κληρονομεί τα χαρακτηριστικά της κατηγορίας Πρόσωπο δύο φορές - μία από τον Πατέρα και ξανά από τη Μητέρα. Αυτό δημιουργεί ασάφεια αφού ο μεταγλωττιστής δεν καταλαβαίνει ποιος δρόμος πρέπει να ακολουθήσει.

Αυτό το σενάριο δημιουργεί ένα γράφημα κληρονομικότητας σε σχήμα διαμαντιού και ονομάζεται περίφημα "The Diamond Problem".

Εικονογράφηση κώδικα του προβλήματος διαμαντιών

Παρακάτω έχουμε αναπαραστήσει το παραπάνω παράδειγμα κληρονομικότητας σε σχήμα διαμαντιού μέσω προγραμματισμού. Ο κωδικός δίνεται παρακάτω:

#περιλαμβάνω
χρησιμοποιώντας το όνομα χώρου std.
class Person {// class Person
δημόσιο:
Πρόσωπο (int x) {cout << "Πρόσωπο:: Πρόσωπο (int) που ονομάζεται" << endl; }
};
τάξη Πατέρας: δημόσιο Πρόσωπο {// τάξη Ο πατέρας κληρονομεί το Πρόσωπο
δημόσιο:
Πατέρας (int x): Πρόσωπο (x) {
cout << "Πατέρας:: Πατέρας (int) που ονομάζεται" << endl;
}
};
τάξη Μητέρα: δημόσιο Πρόσωπο {// τάξη Η μητέρα κληρονομεί το Πρόσωπο
δημόσιο:
Μητέρα (int x): Πρόσωπο (x) {
cout << "Μητέρα:: Μητέρα (int) που ονομάζεται" << endl;
}
};
τάξη Παιδί: δημόσιος πατέρας, δημόσια μητέρα {// Το παιδί κληρονομεί τον πατέρα και τη μητέρα
δημόσιο:
Παιδί (int x): Μητέρα (x), πατέρας (x) {
cout << "Παιδί:: Παιδί (int) που ονομάζεται" << endl;
}
};
int main () {
Παιδί παιδί (30).
}

Ακολουθεί η έξοδος αυτού του προγράμματος:

Πρόσωπο:: Πρόσωπο (int) που καλείται
Πατέρας:: Πατέρας (int) καλείται
Πρόσωπο:: Πρόσωπο (int) που καλείται
Μητέρα:: Μητέρα (int) καλείται
Παιδί:: Παιδί (int) καλείται

Τώρα μπορείτε να δείτε την ασάφεια εδώ. Ο κατασκευαστής κλάσης Person καλείται δύο φορές: μία όταν δημιουργείται το αντικείμενο της τάξης Πατέρα και την επόμενη όταν δημιουργείται το αντικείμενο της κλάσης Μητέρα. Οι ιδιότητες της κλάσης Person κληρονομούνται δύο φορές, δημιουργώντας ασάφεια.

Δεδομένου ότι ο κατασκευαστής κλάσης Person καλείται δύο φορές, ο destructor θα κληθεί επίσης δύο φορές όταν καταστραφεί το αντικείμενο της κλάσης Child.

Τώρα αν έχετε καταλάβει σωστά το πρόβλημα, ας συζητήσουμε τη λύση του Διαμαντένιου Προβλήματος.

Πώς να διορθώσετε το πρόβλημα διαμαντιών στο C ++

Η λύση στο πρόβλημα των διαμαντιών είναι η χρήση του εικονικός λέξη -κλειδί. Κάνουμε τις δύο τάξεις γονέων (οι οποίοι κληρονομούν από την ίδια τάξη παππού και γιαγιά) σε εικονικές τάξεις, προκειμένου να αποφύγουμε δύο αντίγραφα της τάξης των παππούδων στην τάξη παιδιών.

Ας αλλάξουμε την παραπάνω εικόνα και ελέγξουμε την έξοδο:

Code Illustration για να διορθώσετε το πρόβλημα Diamond

#περιλαμβάνω
χρησιμοποιώντας το όνομα χώρου std.
class Person {// class Person
δημόσιο:
Person () {cout << "Person:: Person () ονομάζεται" << endl; } // Κατασκευαστής βάσης
Πρόσωπο (int x) {cout << "Πρόσωπο:: Πρόσωπο (int) που ονομάζεται" << endl; }
};
class Πατέρας: εικονικό δημόσιο Πρόσωπο {// class Ο πατέρας κληρονομεί το Person
δημόσιο:
Πατέρας (int x): Πρόσωπο (x) {
cout << "Πατέρας:: Πατέρας (int) που ονομάζεται" << endl;
}
};
class Mother: virtual public Person {// class Η μητέρα κληρονομεί το Person
δημόσιο:
Μητέρα (int x): Πρόσωπο (x) {
cout << "Μητέρα:: Μητέρα (int) που ονομάζεται" << endl;
}
};
τάξη Παιδί: δημόσιος πατέρας, δημόσια μητέρα {// τάξη Το παιδί κληρονομεί τον πατέρα και τη μητέρα
δημόσιο:
Παιδί (int x): Μητέρα (x), πατέρας (x) {
cout << "Παιδί:: Παιδί (int) που ονομάζεται" << endl;
}
};
int main () {
Παιδί παιδί (30).
}

Εδώ έχουμε χρησιμοποιήσει το εικονικός λέξη -κλειδί όταν οι τάξεις Πατέρας και Μητέρα κληρονομούν την τάξη Person. Αυτό συνήθως ονομάζεται "εικονική κληρονομικότητα", η οποία εγγυάται ότι μεταδίδεται μόνο μία μόνο περίπτωση της κληρονομούμενης κλάσης (στην περίπτωση αυτή, της κλάσης Person).

Με άλλα λόγια, η τάξη Παιδί θα έχει μια μοναδική περίπτωση της τάξης Προσωπικό, την οποία μοιράζονται τόσο οι τάξεις του Πατέρα όσο και της Μητέρας. Έχοντας ένα μόνο παράδειγμα της κλάσης Person, επιλύεται η ασάφεια.

Η έξοδος του παραπάνω κώδικα δίνεται παρακάτω:

Person:: Person () καλείται
Πατέρας:: Πατέρας (int) καλείται
Μητέρα:: Μητέρα (int) καλείται
Παιδί:: Παιδί (int) καλείται

Εδώ μπορείτε να δείτε ότι ο κατασκευαστής κλάσης Person καλείται μόνο μία φορά.

Ένα πράγμα που πρέπει να σημειωθεί για την εικονική κληρονομικότητα είναι ότι ακόμη και αν ο παραμετροποιημένος κατασκευαστής του Η κατηγορία ατόμων καλείται ρητά από τους κατασκευαστές τάξης Πατέρα και Μητέρας μέσω της προετοιμασίας τόπος αγώνων, θα κληθεί μόνο ο κατασκευαστής βάσης της κλάσης Person.

Αυτό συμβαίνει επειδή υπάρχει μόνο μία περίπτωση μιας εικονικής βασικής κλάσης που μοιράζεται με πολλαπλές κλάσεις που κληρονομούνται από αυτήν.

Για να αποφευχθεί η εκτέλεση του κατασκευαστή βάσης πολλές φορές, ο κατασκευαστής μιας εικονικής κλάσης βάσης δεν καλείται από την κλάση που κληρονομεί από αυτήν. Αντ 'αυτού, ο κατασκευαστής καλείται από τον κατασκευαστή της κλάσης σκυροδέματος.

Στο παραπάνω παράδειγμα, η κλάση Child καλεί απευθείας τον κατασκευαστή βάσης για την κλάση Person.

Σχετίζεται με: Οδηγός για αρχάριους στη Βιβλιοθήκη τυπικού προτύπου σε C ++

Τι γίνεται αν πρέπει να εκτελέσετε τον παραμετροποιημένο κατασκευαστή της βασικής κλάσης; Μπορείτε να το κάνετε αυτό καλώντας το ρητά στην τάξη Παιδί και όχι στις τάξεις Πατέρα ή Μητέρα.

Το Διαμαντένιο Πρόβλημα στην C ++, λύθηκε

Το Διαμαντένιο Πρόβλημα είναι μια ασάφεια που προκύπτει σε πολλαπλή κληρονομικότητα όταν δύο γονικές τάξεις κληρονομούν από την ίδια τάξη παππού και γιαγιά και οι δύο τάξεις γονέων κληρονομούνται από μια τάξη ενός παιδιού. Χωρίς χρήση εικονικής κληρονομικότητας, η παιδική τάξη θα κληρονομούσε τις ιδιότητες της τάξης του παππού και της γιαγιάς δύο φορές, οδηγώντας σε ασάφεια.

Αυτό μπορεί να εμφανίζεται συχνά σε πραγματικό κώδικα, οπότε είναι σημαντικό να αντιμετωπίζετε αυτήν την ασάφεια κάθε φορά που εντοπίζεται.

Το Πρόβλημα Διαμαντιού διορθώνεται χρησιμοποιώντας εικονική κληρονομικότητα, στην οποία το εικονικός Η λέξη -κλειδί χρησιμοποιείται όταν οι γονικές τάξεις κληρονομούν από μια κοινόχρηστη τάξη παππού και γιαγιά. Με αυτόν τον τρόπο, γίνεται μόνο ένα αντίγραφο της τάξης των παππούδων και η κατασκευή αντικειμένων της τάξης των παππούδων γίνεται από την τάξη παιδιών.

ΜερίδιοΤιτίβισμαΗΛΕΚΤΡΟΝΙΚΗ ΔΙΕΥΘΥΝΣΗ
Τα 10 καλύτερα έργα για αρχάριους για νέους προγραμματιστές

Θέλετε να μάθετε προγραμματισμό αλλά δεν ξέρετε από πού να ξεκινήσετε; Αυτά τα αρχικά προγράμματα προγραμματισμού και τα σεμινάρια θα σας ξεκινήσουν.

Διαβάστε Επόμενο

Σχετικά θέματα
  • Προγραμματισμός
  • Γ Προγραμματισμός
Σχετικά με τον Συγγραφέα
Προσωπικό MUO

Εγγραφείτε στο newsletter μας

Εγγραφείτε στο ενημερωτικό μας δελτίο για τεχνικές συμβουλές, κριτικές, δωρεάν ebooks και αποκλειστικές προσφορές!

Κάντε κλικ εδώ για εγγραφή