Ενσωματωμένες (ενσωματωμένες) λειτουργίες. Υπερφόρτωση λειτουργίας

Υπερφόρτωση λειτουργίας

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

Ορολογία

Ο όρος "υπερφόρτωση" είναι ένα χαρτί ανίχνευσης του αγγλικού "overloading", το οποίο εμφανίστηκε σε ρωσικές μεταφράσεις βιβλίων για γλώσσες προγραμματισμού το πρώτο μισό της δεκαετίας του 1990. Ίσως αυτό δεν είναι το πιο η καλύτερη επιλογήμετάφραση, καθώς η λέξη "υπερφόρτωση" στη ρωσική γλώσσα έχει μια καθιερωμένη σημασία από μόνη της, ριζικά διαφορετική από τη πρόσφατα προτεινόμενη, ωστόσο, έχει ριζώσει και είναι αρκετά διαδεδομένη. Στις εκδόσεις της σοβιετικής εποχής, παρόμοιοι μηχανισμοί ονομάζονταν στα ρωσικά "επαναπροσδιορισμός" ή "επαναπροσδιορισμός" των λειτουργιών, αλλά αυτή η επιλογή δεν αμφισβητείται: προκύπτουν αποκλίσεις και σύγχυση στις μεταφράσεις των αγγλικών "παράκαμψη", "υπερφόρτωση" και " επαναπροσδιορίσει».

Λόγοι εμφάνισης

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

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

  • Μερικές φορές υπάρχει ανάγκη να περιγραφούν και να εφαρμοστούν λειτουργίες σε τύπους δεδομένων που δημιουργήθηκαν από τον προγραμματιστή και να έχουν νόημα ισοδύναμο με αυτούς που είναι ήδη διαθέσιμοι στη γλώσσα. Ένα κλασικό παράδειγμα είναι μια βιβλιοθήκη για εργασία με μιγαδικούς αριθμούς. Αυτοί, όπως οι συνηθισμένοι αριθμητικοί τύποι, υποστηρίζουν αριθμητικές πράξεις και θα ήταν φυσικό να δημιουργηθούν για αυτού του τύπουπράξεις «συν», «πλην», «πολλαπλασιάζω», «διαίρε», δηλώνοντάς τις με τα ίδια σύμβολα λειτουργίας όπως και για άλλους αριθμητικούς τύπους. Η απαγόρευση χρήσης στοιχείων που ορίζονται στη γλώσσα αναγκάζει τη δημιουργία πολλών συναρτήσεων με ονόματα όπως ComplexPlusComplex, IntegerPlusCoplex, ComplexMinusFloat και ούτω καθεξής.
  • Όταν οι πράξεις της ίδιας σημασίας εφαρμόζονται σε τελεστές διάφοροι τύποι, πρέπει να ονομάζονται διαφορετικά. Αδυναμία υποβολής αίτησης ΔΙΑΦΟΡΕΤΙΚΟΙ ΤΥΠΟΙσυναρτήσεις με το ίδιο όνομα οδηγεί στην ανάγκη να εφευρίσκονται διαφορετικά ονόματα για το ίδιο πράγμα, γεγονός που δημιουργεί σύγχυση και μπορεί να οδηγήσει σε σφάλματα. Για παράδειγμα, στην κλασική γλώσσα C, υπάρχουν δύο εκδόσεις της τυπικής συνάρτησης βιβλιοθήκης για την εύρεση του συντελεστή μέτρησης ενός αριθμού: abs() και fabs() - η πρώτη προορίζεται για ένα όρισμα ακέραιου αριθμού, η δεύτερη για ένα πραγματικό. Αυτή η κατάσταση, σε συνδυασμό με τον αδύναμο έλεγχο τύπου C, μπορεί να οδηγήσει σε ένα δυσεύρετο σφάλμα: εάν ένας προγραμματιστής γράψει abs(x) στον υπολογισμό, όπου το x είναι μια πραγματική μεταβλητή, τότε ορισμένοι μεταγλωττιστές θα δημιουργήσουν κώδικα χωρίς προειδοποίηση που θα μετατρέψτε το x σε ακέραιο απορρίπτοντας τα κλασματικά μέρη και υπολογίστε το μέτρο από τον ακέραιο που προκύπτει!

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

Η ίδια η υπερφόρτωση χειριστή είναι απλώς "συντακτική ζάχαρη", αν και ως τέτοια μπορεί να είναι χρήσιμη επειδή επιτρέπει στον προγραμματιστή να προγραμματίζει με πιο φυσικό τρόπο και κάνει τους προσαρμοσμένους τύπους να συμπεριφέρονται περισσότερο σαν ενσωματωμένοι. Εάν προσεγγίσουμε το ζήτημα από μια πιο γενική θέση, τότε μπορούμε να δούμε ότι τα εργαλεία που σας επιτρέπουν να επεκτείνετε τη γλώσσα, τη συμπληρώνουν με νέες πράξεις και συντακτικές κατασκευές (και η υπερφόρτωση πράξεων είναι ένα από αυτά τα εργαλεία, μαζί με αντικείμενα, μακροεντολές , λειτουργικά, κλεισίματα) το μετατρέπουν ήδη στη μεταγλώσσα - ένα μέσο περιγραφής γλωσσών προσανατολισμένων σε συγκεκριμένες εργασίες. Με τη βοήθειά του, είναι δυνατό να δημιουργηθεί μια επέκταση γλώσσας για κάθε συγκεκριμένη εργασία που είναι πιο κατάλληλη για αυτήν, η οποία θα επιτρέψει την περιγραφή της λύσης της με την πιο φυσική, κατανοητή και απλή μορφή. Για παράδειγμα, σε μια εφαρμογή για πράξεις υπερφόρτωσης: η δημιουργία μιας βιβλιοθήκης πολύπλοκων μαθηματικών τύπων (διανύσματα, πίνακες) και η περιγραφή πράξεων με αυτούς σε μια φυσική, «μαθηματική» μορφή, δημιουργεί μια «γλώσσα για διανυσματικές πράξεις», στην οποία η πολυπλοκότητα των Οι υπολογισμοί είναι κρυμμένοι και είναι δυνατό να περιγραφεί η λύση των προβλημάτων με όρους διανυσμάτων και πράξεων μήτρας, εστιάζοντας στην ουσία του προβλήματος και όχι στην τεχνική. Για αυτούς τους λόγους, τέτοια μέσα συμπεριλήφθηκαν κάποτε στη γλώσσα Algol-68.

Μηχανισμός υπερφόρτωσης

Εκτέλεση

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

  • Για να επιτραπεί η ύπαρξη πολλών πράξεων με το ίδιο όνομα, αρκεί να εισαχθεί ένας κανόνας στη γλώσσα, σύμφωνα με τον οποίο μια πράξη (διαδικασία, συνάρτηση ή τελεστής) αναγνωρίζεται από τον μεταγλωττιστή όχι μόνο με το όνομα (σημείωση), αλλά επίσης από τους τύπους των παραμέτρων τους. Άρα το abs(i), όπου το i δηλώνεται ως ακέραιος και το abs(x), όπου το x δηλώνεται ως πραγματικό, είναι δύο διαφορετικές πράξεις. Βασικά, δεν υπάρχουν δυσκολίες στην παροχή ακριβώς μιας τέτοιας ερμηνείας.
  • Για να επιτραπεί ο ορισμός και ο επαναπροσδιορισμός των πράξεων, είναι απαραίτητο να εισαχθούν κατάλληλες συντακτικές κατασκευές στη γλώσσα. Μπορεί να υπάρχουν πολλές επιλογές, αλλά στην πραγματικότητα δεν διαφέρουν μεταξύ τους, αρκεί να θυμάστε ότι η καταχώριση της φόρμας "<операнд1> <знакОперации> <операнд2>» είναι ουσιαστικά παρόμοια με την κλήση της συνάρτησης «<знакОперации>(<операнд1>,<операнд2>)". Αρκεί να επιτρέψουμε στον προγραμματιστή να περιγράψει τη συμπεριφορά των τελεστών με τη μορφή συναρτήσεων - και το πρόβλημα της περιγραφής επιλύεται.

Επιλογές και προβλήματα

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

Πρόβλημα αναγνώρισης

Η πρώτη ερώτηση που αντιμετωπίζει ένας προγραμματιστής ενός μεταφραστή γλώσσας που επιτρέπει την υπερφόρτωση διαδικασιών και συναρτήσεων είναι πώς να επιλέξει από τις διαδικασίες με το ίδιο όνομα αυτή που θα πρέπει να εφαρμοστεί σε μια δεδομένη συγκεκριμένη περίπτωση? Όλα είναι καλά εάν υπάρχει μια παραλλαγή της διαδικασίας, οι τύποι των επίσημων παραμέτρων της οποίας ταιριάζουν ακριβώς με τους τύπους των πραγματικών παραμέτρων που χρησιμοποιούνται σε αυτήν την κλήση. Ωστόσο, σχεδόν σε όλες τις γλώσσες, υπάρχει κάποιος βαθμός ελευθερίας στη χρήση τύπων, υποθέτοντας ότι ο μεταγλωττιστής εκτελεί αυτόματα μετατροπές ασφαλούς τύπου σε ορισμένες περιπτώσεις. Για παράδειγμα, στις αριθμητικές πράξεις σε πραγματικά και ακέραια ορίσματα, ο ακέραιος συνήθως μετατρέπεται αυτόματα σε πραγματικό τύπο και το αποτέλεσμα είναι πραγματικό. Ας υποθέσουμε ότι υπάρχουν δύο παραλλαγές της συνάρτησης προσθήκης:

int add(int a1, int a2); float add(float a1, float a2);

Πώς πρέπει ο μεταγλωττιστής να χειρίζεται την παράσταση y = add(x, i) όπου x είναι float και i είναι int; Προφανώς δεν υπάρχει ακριβής αντιστοιχία. Υπάρχουν δύο επιλογές: είτε y=add_int((int)x,i) , είτε ως y=add_flt(x, (float)i) (εδώ τα ονόματα add_int και add_float υποδηλώνουν την πρώτη και τη δεύτερη έκδοση της συνάρτησης, αντίστοιχα) .

Τίθεται το ερώτημα: θα πρέπει ο μεταγλωττιστής να επιτρέπει αυτή τη χρήση υπερφορτωμένων συναρτήσεων και αν ναι, σε ποια βάση θα επιλέξει τη συγκεκριμένη παραλλαγή που θα χρησιμοποιηθεί; Συγκεκριμένα, στο παραπάνω παράδειγμα, πρέπει ο μεταφραστής να λάβει υπόψη τον τύπο της μεταβλητής y κατά την επιλογή; Θα πρέπει να σημειωθεί ότι η παραπάνω κατάσταση είναι η απλούστερη, είναι δυνατές πολύ πιο περίπλοκες περιπτώσεις, οι οποίες επιδεινώνονται από το γεγονός ότι όχι μόνο οι ενσωματωμένοι τύποι μπορούν να μετατραπούν σύμφωνα με τους κανόνες της γλώσσας, αλλά και οι κλάσεις που δηλώνονται από τον προγραμματιστή , εάν έχουν συγγενικές σχέσεις, μπορούν να πεταχτούν ο ένας στον άλλο. Υπάρχουν δύο λύσεις σε αυτό το πρόβλημα:

  • Να απαγορεύεται καθόλου η ανακριβής ταυτοποίηση. Απαιτήστε για κάθε συγκεκριμένο ζεύγος τύπων να υπάρχει μια ακριβώς κατάλληλη παραλλαγή της υπερφορτωμένης διαδικασίας ή λειτουργίας. Εάν δεν υπάρχει τέτοια επιλογή, ο μεταγλωττιστής θα πρέπει να κάνει ένα σφάλμα. Ο προγραμματιστής σε αυτήν την περίπτωση πρέπει να εφαρμόσει μια ρητή μετατροπή για να μεταφέρει τις πραγματικές παραμέτρους στο επιθυμητό σύνολο τύπων. Αυτή η προσέγγιση δεν είναι βολική σε γλώσσες όπως η C++, οι οποίες επιτρέπουν αρκετή ελευθερία στην αντιμετώπιση τύπων, καθώς οδηγεί σε σημαντική διαφορά στη συμπεριφορά των ενσωματωμένων και υπερφορτωμένων τελεστών (οι αριθμητικές πράξεις μπορούν να εφαρμοστούν σε συνηθισμένους αριθμούς χωρίς σκέψη, αλλά σε άλλους τύπους - μόνο με ρητή μετατροπή) ή στην εμφάνιση ενός τεράστιου αριθμού επιλογών για λειτουργίες.
  • Καθιερώστε ορισμένους κανόνες για την επιλογή της «πλησιέστερης εφαρμογής». Συνήθως, σε αυτήν την παραλλαγή, ο μεταγλωττιστής επιλέγει εκείνες από τις παραλλαγές των οποίων οι κλήσεις μπορούν να ληφθούν από την πηγή μόνο με ασφαλείς μετατροπές τύπου (χωρίς απώλειες πληροφοριών) και εάν υπάρχουν πολλές από αυτές, μπορεί να επιλέξει με βάση ποια παραλλαγή απαιτεί λιγότερες τέτοιες μετατροπές. Εάν το αποτέλεσμα αφήνει περισσότερες από μία δυνατότητες, ο μεταγλωττιστής κάνει ένα σφάλμα και απαιτεί από τον προγραμματιστή να καθορίσει ρητά την παραλλαγή.

Λειτουργία Υπερφόρτωση Ειδικά Θέματα

Σε αντίθεση με τις διαδικασίες και τις συναρτήσεις, οι λειτουργίες infix των γλωσσών προγραμματισμού έχουν δύο πρόσθετες ιδιότητες που επηρεάζουν σημαντικά τη λειτουργικότητά τους: προτεραιότητα και συσχέτιση, η παρουσία των οποίων οφείλεται στη δυνατότητα "αλυσιδωτής" εγγραφής των τελεστών (πώς να κατανοήσετε a + b * γ: ως (a + b )*c ή σαν a+(b*c) Η έκφραση a-b+c είναι (a-b)+c ή a-(b+c) ?).

Οι πράξεις που είναι ενσωματωμένες στη γλώσσα έχουν πάντα προκαθορισμένη παραδοσιακή προτεραιότητα και συνειρμικότητα. Τίθεται το ερώτημα: ποιες προτεραιότητες και συνειρμότητα θα έχουν οι επανακαθορισμένες εκδόσεις αυτών των πράξεων ή, επιπλέον, οι νέες λειτουργίες που δημιουργούνται από τον προγραμματιστή; Υπάρχουν και άλλες λεπτές λεπτομέρειες που μπορεί να απαιτούν διευκρίνιση. Για παράδειγμα, στο C υπάρχουν δύο μορφές τελεστών αύξησης και μείωσης ++ και -- - πρόθεμα και μετάθεμα, οι οποίοι συμπεριφέρονται διαφορετικά. Πώς πρέπει να συμπεριφέρονται οι υπερφορτωμένες εκδόσεις τέτοιων χειριστών;

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

Έτσι το int χρησιμοποιείται για να κάνει τη διαφορά στις υπογραφές

Ανακοίνωση νέων λειτουργιών

Η κατάσταση με την ανακοίνωση νέων επιχειρήσεων είναι ακόμη πιο περίπλοκη. Η συμπερίληψη της δυνατότητας μιας τέτοιας δήλωσης στη γλώσσα δεν είναι δύσκολη, αλλά η εφαρμογή της είναι γεμάτη σημαντικές δυσκολίες. Η δήλωση μιας νέας λειτουργίας είναι, στην πραγματικότητα, η δημιουργία μιας νέας λέξη-κλειδίγλώσσα προγραμματισμού, που περιπλέκεται από το γεγονός ότι οι λειτουργίες στο κείμενο, κατά κανόνα, μπορούν να ακολουθήσουν χωρίς διαχωριστικά με άλλα διακριτικά. Όταν εμφανίζονται, προκύπτουν πρόσθετες δυσκολίες στην οργάνωση του λεξικού αναλυτή. Για παράδειγμα, εάν η γλώσσα έχει ήδη τις πράξεις "+" και το μοναδικό "-" (αλλαγή του πρόσημου), τότε η έκφραση a+-b μπορεί να ερμηνευτεί με ακρίβεια ως + (-b) , αλλά εάν μια νέα πράξη +- δηλώνεται στο πρόγραμμα, δημιουργείται αμέσως ασάφεια, επειδή η ίδια έκφραση μπορεί ήδη να αναλυθεί ως (+-) b . Ο προγραμματιστής και ο υλοποιητής της γλώσσας πρέπει να αντιμετωπίσει τέτοια προβλήματα με κάποιο τρόπο. Οι επιλογές, και πάλι, μπορεί να είναι διαφορετικές: απαιτούν όλες οι νέες πράξεις να είναι μονοχαρακτήρα, υποθέτουμε ότι σε περίπτωση οποιωνδήποτε αποκλίσεων, επιλέγεται η "μακρύτερος" έκδοση της πράξης (δηλαδή, μέχρι το επόμενο σύνολο χαρακτήρων που διαβάζει ο ο μεταφραστής ταιριάζει με οποιαδήποτε λειτουργία, συνεχίζει να διαβάζεται), προσπαθήστε να εντοπίσετε συγκρούσεις κατά τη μετάφραση και να δημιουργήσετε σφάλματα σε αμφιλεγόμενες περιπτώσεις ... Με τον ένα ή τον άλλο τρόπο, οι γλώσσες που επιτρέπουν τη δήλωση νέων λειτουργιών λύνουν αυτά τα προβλήματα.

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

Υπερφόρτωση και πολυμορφικές μεταβλητές

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

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

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

Κριτική

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

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

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

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

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

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

  • Οι υποστηρικτές της «πουριτανικής» προσέγγισης για την οικοδόμηση γλώσσας, όπως ο Wirth ή ο Hoare, αντιτίθενται στην υπερφόρτωση των χειριστών απλώς και μόνο επειδή μπορεί εύκολα να παραλειφθεί. Κατά τη γνώμη τους, τέτοια εργαλεία περιπλέκουν μόνο τη γλώσσα και τον μεταφραστή, χωρίς να παρέχουν αντίστοιχη σε αυτήν την περιπλοκή Επιπρόσθετα χαρακτηριστικά. Κατά τη γνώμη τους, η ίδια η ιδέα της δημιουργίας μιας επέκτασης της γλώσσας με γνώμονα την εργασία φαίνεται ελκυστική. Στην πραγματικότητα, η χρήση εργαλείων επέκτασης γλώσσας καθιστά το πρόγραμμα κατανοητό μόνο στον συγγραφέα του - αυτόν που ανέπτυξε αυτήν την επέκταση. Το πρόγραμμα γίνεται πολύ πιο δύσκολο για άλλους προγραμματιστές να το κατανοήσουν και να το αναλύσουν, καθιστώντας τη συντήρηση, την τροποποίηση και την ανάπτυξη της ομάδας πιο δύσκολη.
  • Σημειώνεται ότι η ίδια η πιθανότητα χρήσης υπερφόρτωσης παίζει συχνά προκλητικό ρόλο: οι προγραμματιστές αρχίζουν να το χρησιμοποιούν όπου είναι δυνατόν, με αποτέλεσμα ένα εργαλείο που έχει σχεδιαστεί για την απλοποίηση και τον εξορθολογισμό του προγράμματος γίνεται η αιτία της περιπλοκής και της σύγχυσής του.
  • Οι υπερφορτωμένοι χειριστές ενδέχεται να μην κάνουν ακριβώς αυτό που αναμένεται από αυτούς, ανάλογα με το είδος τους. Για παράδειγμα, το a + b συνήθως (αλλά όχι πάντα) σημαίνει το ίδιο πράγμα με το b + a , αλλά το "ένα" + "δύο" είναι διαφορετικό από το "δύο" + "ένα" σε γλώσσες όπου ο τελεστής + είναι υπερφορτωμένος για συνένωση χορδών.
  • Η υπερφόρτωση τελεστών κάνει τα τμήματα του προγράμματος πιο ευαίσθητα στο περιβάλλον. Χωρίς να γνωρίζουμε τους τύπους των τελεστών που εμπλέκονται σε μια έκφραση, είναι αδύνατο να κατανοήσουμε τι κάνει η έκφραση εάν χρησιμοποιεί υπερφορτωμένους τελεστές. Για παράδειγμα, σε ένα πρόγραμμα C++, η δήλωση<< может означать и побитовый сдвиг, и вывод в поток. Выражение a << 1 возвращает результат побитового сдвига значения a на один бит влево, если a - целая переменная, но если a является выходным потоком , то же выражение выведет в этот поток строку «1» .

Ταξινόμηση

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

Λειτουργίες Χωρίς υπερφόρτωση Υπάρχει υπερφόρτωση
Περιορισμένο σύνολο λειτουργιών
  • Στόχος-Γ
  • Πύθων
Είναι δυνατός ο ορισμός νέων λειτουργιών
  • PostgreSQL
  • δείτε επίσης

    Ίδρυμα Wikimedia. 2010 .

    Δείτε τι είναι το "Function Overloading" σε άλλα λεξικά:

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

Η υπερφόρτωση συναρτήσεων είναι ο ορισμός πολλών συναρτήσεων (δύο ή περισσότερων) με το ίδιο όνομα αλλά διαφορετικές παραμέτρους. Τα σύνολα παραμέτρων των υπερφορτωμένων συναρτήσεων ενδέχεται να διαφέρουν ως προς τη σειρά, τον αριθμό και τον τύπο. Επομένως, απαιτείται υπερφόρτωση συναρτήσεων προκειμένου να αποφευχθεί η αντιγραφή των ονομάτων των συναρτήσεων που εκτελούν παρόμοιες ενέργειες, αλλά με διαφορετική λογική προγράμματος. Για παράδειγμα, θεωρήστε τη συνάρτηση areaRectangle(), η οποία υπολογίζει το εμβαδόν ενός ορθογωνίου.

Float areaRectangle(float, float) //συνάρτηση που υπολογίζει το εμβαδόν ενός ορθογωνίου με δύο παραμέτρους a(cm) και b(cm) (επιστρέφει a * b; // πολλαπλασιάζουμε τα μήκη των πλευρών του ορθογωνίου και επιστρέφουμε το προϊόν που προκύπτει)

Άρα, αυτή είναι μια συνάρτηση με δύο παραμέτρους τύπου float και τα ορίσματα που μεταβιβάζονται στη συνάρτηση πρέπει να είναι σε εκατοστά, ενώ η τιμή επιστροφής του τύπου float είναι επίσης σε εκατοστά.

Ας υποθέσουμε ότι τα αρχικά μας δεδομένα (πλευρές ορθογωνίου) δίνονται σε μέτρα και εκατοστά, για παράδειγμα: a = 2m 35 cm; b = 1m 86 εκ. Σε αυτήν την περίπτωση, θα ήταν βολικό να χρησιμοποιήσετε μια συνάρτηση με τέσσερις παραμέτρους. Δηλαδή, κάθε μήκος των πλευρών του ορθογωνίου περνά στη συνάρτηση σε δύο παραμέτρους: μέτρα και εκατοστά.

Float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // συνάρτηση που υπολογίζει το εμβαδόν ενός ορθογωνίου με 4 παραμέτρους a(m) a(cm); b(m) b(cm) (επιστροφή (a_m * 100 + a_sm) * (b_m * 100 + b_sm); )

Στο σώμα της συνάρτησης, οι τιμές που μεταβιβάστηκαν σε μέτρα (a_m και b_m) μετατρέπονται σε εκατοστά και προστίθενται στις τιμές a_sm b_sm, μετά από τις οποίες πολλαπλασιάζουμε τα αθροίσματα και παίρνουμε το εμβαδόν του ορθογωνίου σε εκ. Φυσικά, ήταν δυνατή η μετατροπή των αρχικών δεδομένων σε εκατοστά και η χρήση της πρώτης συνάρτησης, αλλά τώρα δεν πρόκειται για αυτό.

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

AreaRectangle(32, 43); // θα κληθεί μια συνάρτηση που υπολογίζει το εμβαδόν ενός ορθογωνίου με δύο παραμέτρους a(cm) και b(cm) areaRectangle(4, 43, 2, 12); // θα κληθεί μια συνάρτηση που υπολογίζει το εμβαδόν ενός ορθογωνίου με 4 παραμέτρους a(m) a(cm). b(m) b(cm)

Όπως μπορείτε να δείτε, ο μεταγλωττιστής θα επιλέξει ανεξάρτητα την επιθυμητή συνάρτηση, αναλύοντας μόνο τις υπογραφές των υπερφορτωμένων συναρτήσεων. Παρακάμπτοντας την υπερφόρτωση συναρτήσεων, θα μπορούσε κανείς απλώς να δηλώσει μια συνάρτηση με διαφορετικό όνομα και θα έκανε καλά τη δουλειά της. Φανταστείτε όμως τι θα συμβεί αν χρειάζεστε περισσότερες από δύο τέτοιες συναρτήσεις, για παράδειγμα 10. Και για καθεμία πρέπει να βρείτε ένα ουσιαστικό όνομα, και το πιο δύσκολο πράγμα να θυμάστε είναι αυτές. Γι' αυτό ακριβώς είναι ευκολότερο και καλύτερο να υπερφορτώνετε τις λειτουργίες, εκτός φυσικά και αν υπάρχει ανάγκη για κάτι τέτοιο. Ο πηγαίος κώδικας του προγράμματος φαίνεται παρακάτω.

#include "stdafx.h" #include << "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

// code Code::Blocks

// Κώδικας Dev-C++

#περιλαμβάνω χρησιμοποιώντας namespace std? // πρωτότυπα υπερφορτωμένων συναρτήσεων float areaRectangle(float a, float b); float areaRectangle(float a_m, float a_sm, float b_m, float b_sm); int main() ( cout<< "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

Το αποτέλεσμα του προγράμματος φαίνεται στο σχήμα 1.



Πώς να επιτύχετε υπερφόρτωση συναρτήσεων στο C; (10)

Υπάρχει τρόπος να επιτευχθεί υπερφόρτωση συναρτήσεων στο C; Εξετάζω απλές λειτουργίες που μπορεί να υπερφορτωθούν όπως

foo (int a) foo (char b) foo (float c , int d)

Νομίζω ότι δεν υπάρχει άμεσος τρόπος. Ψάχνω για λύσεις, αν υπάρχουν.

Ελπίζω ότι ο παρακάτω κώδικας θα σας βοηθήσει να κατανοήσετε την υπερφόρτωση συναρτήσεων

#περιλαμβάνω #περιλαμβάνω int fun(int a, ...); int main(int argc, char *argv)( fun(1,10); fun(2"questionbank"); return 0; ) int fun(int a, ...)( va_list vl; va_start(vl,a ); if(a==1) printf("%d",va_arg(vl,int)); αλλιώς printf("\n%s",va_arg(vl,char *)); )

Δηλαδή, εννοείς - όχι, δεν μπορείς.

Μπορείτε να δηλώσετε τη συνάρτηση va_arg ως

void my_func (μορφή char*, ...);

Αλλά θα χρειαστεί να περάσετε κάποιες πληροφορίες σχετικά με τον αριθμό των μεταβλητών και τους τύπους τους στο πρώτο όρισμα - όπως printf() .

Ναι, όπως.

Εδώ δίνετε ένα παράδειγμα:

void printA(int a)( printf("Hello world from printA: %d\n",a); ) void printB(const char *buff)( printf("Hello world from printB: %s\n",buff) ; ) #define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 #define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define _Num_ARGS_(... ) __VA_ARG_N(__VA_ARGS__) #define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) #define CHECK_ARGS_MAX_LIMIT(t) if(mingsHE_t) if(MINGS_ARGe) if(NUM_ARGS(args) #define print(x, args...) \ CHECK_ARGS_MIN_LIMIT(1) printf("σφάλμα");fflush(stdout); \ CHECK_ARGS_MAX_LIMIT(4) printf("σφάλμα");fflush(stdout) ; \ ( \ if (__builtin_types_compatible_p (typeof (x), int)) \ printA(x, ##args); \ other \ printB (x,##args); \ )) int main(int argc, char* * argv) ( int a=0; print(a); print ("hello"); επιστροφή (EXIT_SUCCESS); )

Θα βγάζει 0 και hello από το printA και το printB.

Εάν ο μεταγλωττιστής σας είναι gcc και δεν σας πειράζει να κάνετε μη αυτόματες ενημερώσεις κάθε φορά που προσθέτετε μια νέα υπερφόρτωση, μπορείτε να κάνετε μια μάζα μακροεντολών και να έχετε το αποτέλεσμα που θέλετε από την άποψη των καλούντων, όχι τόσο ωραίο να γράφετε... αλλά είναι δυνατό

κοιτάξτε το __builtin_types_compatible_p και, στη συνέχεια, χρησιμοποιήστε το για να ορίσετε μια μακροεντολή που κάνει κάτι όπως

#define foo(a) \ ((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

αλλά ναι, άσχημο, απλά όχι

ΕΠΕΞΕΡΓΑΣΙΑ:Το C1X θα λάβει υποστήριξη για εκφράσεις τύπου, οι οποίες μοιάζουν με αυτό:

#define cbrt(X) _Generic((X), long double: cbrtl, \ default: cbrt, \ float: cbrtf)(X)

Όπως αναφέρθηκε ήδη, η υπερφόρτωση με την έννοια που εννοείτε δεν υποστηρίζεται από το C. Το συνηθισμένο ιδίωμα για την επίλυση του προβλήματος είναι η συνάρτηση να παίρνει μια ένωση με ετικέτα . Αυτό υλοποιείται χρησιμοποιώντας την παράμετρο struct, όπου η ίδια η δομή αποτελείται από κάποιο τύπο δείκτη τύπου, όπως ένα enum , και μια ένωση διαφορετικών τύπων τιμών. Παράδειγμα:

#περιλαμβάνω typedef enum ( T_INT, T_FLOAT, T_CHAR, ) my_type; typedef struct ( my_type type; union ( int a; float b; char c; ) my_union; ) my_struct; void set_overload (my_struct *whatever) ( switch (whatever->type) ( case T_INT: whatever->my_union.a = 1; break; case T_FLOAT: whatever->my_union.b = 2.0; break; case T_CHAR: whatever-> my_union.c = "3"; ) ) void printf_overload (my_struct *whatever) ( switch (whatever->type) ( case T_INT: printf("%d\n", whatever->my_union.a); break; case T_FLOAT : printf("%f\n", whatever->my_union.b); break; case T_CHAR: printf("%c\n", whatever->my_union.c); break; ) ) int main (int argc, char* argv) ( my_struct s; s.type=T_INT; set_overload(&s); printf_overload(&s); s.type=T_FLOAT; set_overload(&s); printf_overload(&s); s.type=T_CHAR; set_overload(&s) ; printf_overload(&s); )

Δεν μπορείτε να χρησιμοποιήσετε απλώς τη C++ και να μην χρησιμοποιήσετε όλες τις άλλες δυνατότητες της C++ εκτός από αυτήν;

Εάν δεν έχει υπάρξει αυστηρά αυστηρή C μέχρι στιγμής, θα συνιστούσα αντ 'αυτού τις variadic συναρτήσεις.

Η ακόλουθη προσέγγιση είναι παρόμοια με a2800276, αλλά με μερικές μακροεντολές C99:

// χρειαζόμαστε `size_t` #include // τύπους ορίσματος για αποδοχή enum sum_arg_types ( SUM_LONG, SUM_ULONG, SUM_DOUBLE ); // μια δομή για να κρατήσει ένα όρισμα struct sum_arg ( enum sum_arg_types type; union ( long as_long; unsigned long as_ulong; double as_double; ) value; ); // προσδιορίστε το μέγεθος ενός πίνακα #define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY))) // έτσι θα ονομαστεί η συνάρτησή μας #define sum(...) _sum( count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__)) // δημιουργήστε έναν πίνακα "struct sum_arg" #define sum_args(...) ((struct sum_arg )( __VA_ARGS__ )) // δημιουργία αρχικοποιητών για τα ορίσματα #longde (VALUE) ( SUM_LONG, ( .as_long = (VALUE) ) ) #define sum_ulong(VALUE) ( SUM_ULONG, ( .as_ulong = (VALUE) ) ) #define sum_double(VALUE) ( SUM_DOUBLE, ( .VAL_dou) ) // η πολυμορφική μας συνάρτηση long double _sum(size_t count, struct sum_arg * args) ( long double value = 0; for(size_t i = 0; i< count; ++i) { switch(args[i].type) { case SUM_LONG: value += args[i].value.as_long; break; case SUM_ULONG: value += args[i].value.as_ulong; break; case SUM_DOUBLE: value += args[i].value.as_double; break; } } return value; } // let"s see if it works #include int main() ( ανυπόγραφο long foo = -1; long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10)); printf("%Le\n", τιμή); return 0; )

Προς το παρόν, _Generic αφού το _Generic της ερώτησης, το πρότυπο C (χωρίς επεκτάσεις) είναι αποτελεσματικά έλαβευποστήριξη για λειτουργίες υπερφόρτωσης (και όχι τελεστών) χάρη στην προσθήκη της λέξης _Generic _Generic στο C11. (υποστηρίζεται στο GCC από την έκδοση 4.9)

(Η υπερφόρτωση δεν είναι πραγματικά "ενσωματωμένη" με τον τρόπο που φαίνεται στην ερώτηση, αλλά είναι εύκολο να καταστραφεί κάτι που λειτουργεί έτσι.)

Το Generic είναι ένας τελεστής χρόνου μεταγλώττισης στην ίδια οικογένεια με το sizeof και το _Alignof . Περιγράφεται στην τυπική ενότητα 6.5.1.1. Χρειάζεται δύο κύριες παραμέτρους: μια έκφραση (η οποία δεν θα αξιολογηθεί κατά το χρόνο εκτέλεσης) και μια λίστα συσχετίσεων τύπου/έκφρασης, που μοιάζει λίγο με μπλοκ διακόπτη. Το _Generic λαμβάνει τον γενικό τύπο της έκφρασης και, στη συνέχεια, "μεταβαίνει" σε αυτόν για να επιλέξει την έκφραση τελικού αποτελέσματος στη λίστα για τον τύπο της:

Generic(1, float: 2.0, char *: "2", int: 2, default: get_two_object());

Η παραπάνω έκφραση υπολογίζεται σε 2 - ο τύπος της έκφρασης ελέγχου είναι int , επομένως επιλέγει την παράσταση που σχετίζεται με το int ως τιμή. Τίποτα από αυτά δεν αφήνεται κατά το χρόνο εκτέλεσης. (Η προεπιλεγμένη ρήτρα είναι υποχρεωτική: εάν δεν την καθορίσετε και ο τύπος δεν ταιριάζει, θα προκαλέσει σφάλμα μεταγλώττισης.)

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

#define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \)(X)

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

Έτσι, για να εφαρμόσουμε το αρχικό σας παράδειγμα, θα μπορούσαμε να κάνουμε αυτό:

Foo_int (int a) foo_char (char b) foo_float_int (float c , int d) #define foo(_1, ...) _Generic((_1), \ int: foo_int, \ char: foo_char, \ float: _Generic(( FIRST(__VA_ARGS__,)), \int: foo_float_int))(_1, __VA_ARGS__) #define FIRST(A, ...) A

Σε αυτήν την περίπτωση, θα μπορούσαμε να χρησιμοποιήσουμε την προεπιλογή: binding για την τρίτη περίπτωση, αλλά αυτό δεν δείχνει πώς να επεκτείνουμε την αρχή σε πολλαπλά ορίσματα. Το τελικό αποτέλεσμα είναι ότι μπορείτε να χρησιμοποιήσετε το foo(...) στον κώδικά σας χωρίς να ανησυχείτε (πολύ) για τον τύπο των ορισμάτων σας.

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

void print_ii(int a, int b) ( printf("int, int\n"); ) void print_di(double a, int b) ( printf("double, int\n"); ) void print_iii(int a, int b, int c) ( printf("int, int, int\n"); ) void print_default(void) ( printf("άγνωστα ορίσματα\n"); ) #define print(...) OVERLOAD(print, (__VA_ARGS__), \ (print_ii, (int, int)), \ (print_di, (double, int)), \ (print_iii, (int, int, int)) \) #define OVERLOAD_ARG_TYPES (int, double) #define OVERLOAD_FUNCTIONS (print) #include "activate-overloads.h" int main(void) ( print(44, 47); // prints "int, int" print(4.4, 47); // prints "double, int" print (1, 2, 3); // εκτυπώνει "int, int, int" print(""); // εκτυπώνει "άγνωστα ορίσματα" )

(εφαρμογή εδώ). Με λίγη προσπάθεια, μπορείτε να μειώσετε το boilerplate ώστε να μοιάζει αρκετά με μια γλώσσα με ενσωματωμένη υποστήριξη υπερφόρτωσης.

Στην άκρη, ήταν ήδη δυνατή η υπερφόρτωση ποσότηταορίσματα (αντί τύπου) στο C99.

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

Η απάντηση του Leushenko είναι πολύ ωραία: μόνο το παράδειγμα foo δεν μεταγλωττίζεται με το GCC, το οποίο αποτυγχάνει στο foo(7) , έρχεται σε επαφή με την ΠΡΩΤΗ μακροεντολή και την πραγματική κλήση συνάρτησης ((_1, __VA_ARGS__) , παραμένοντας με ένα επιπλέον κόμμα. Επίσης, αντιμετωπίζουμε προβλήματα, αν θέλουμε να παρέχουμε επιπλέον υπερφορτώσεις όπως foo(double) .

Έτσι αποφάσισα να απαντήσω σε αυτήν την ερώτηση με περισσότερες λεπτομέρειες, συμπεριλαμβανομένης της άδειας της άδειας υπερφόρτωσης (foo(void) - που προκάλεσε κάποιο πρόβλημα...).

Η ιδέα τώρα είναι: ορίστε περισσότερα από ένα γενικά σε διαφορετικές μακροεντολές και αφήστε το σωστό να επιλεγεί ανάλογα με τον αριθμό των ορισμάτων!

Ο αριθμός των επιχειρημάτων είναι αρκετά απλός, με βάση αυτήν την απάντηση:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) #define CONCAT(X, Y) CONCAT_(X, Y #) ορίστε CONCAT_(X, Y) X ## Y

Αυτό είναι καλό, αποφασίζουμε είτε SELECT_1 είτε SELECT_2 (ή περισσότερα επιχειρήματα αν τα θέλετε/χρειάζεστε), οπότε χρειαζόμαστε απλώς τους κατάλληλους ορισμούς:

#define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char: foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double : _Generic((_2), \ int: foo_double_int \) \)

Πρώτον, μια κενή κλήση μακροεντολής (foo()) εξακολουθεί να δημιουργεί ένα διακριτικό, αλλά είναι κενό. Έτσι, η μακροεντολή καταμέτρησης επιστρέφει στην πραγματικότητα 1 αντί για 0, ακόμα κι αν η μακροεντολή ονομάζεται άδεια. Μπορούμε "εύκολα" να διορθώσουμε αυτό το πρόβλημα εάν __VA_ARGS__ με κόμμα μετά __VA_ARGS__ υπό όρους, ανάλογα με το αν η λίστα είναι κενή ή όχι:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

Αυτό κοίταξεεύκολη, αλλά η μακροεντολή COMMA είναι αρκετά βαριά. ευτυχώς, αυτό το θέμα καλύπτεται ήδη στο ιστολόγιο του Jens Gustedt (ευχαριστώ, Jens). Το κύριο κόλπο είναι ότι οι μακροεντολές συναρτήσεων δεν επεκτείνονται εκτός εάν ακολουθούνται από παρενθέσεις, ανατρέξτε στο ιστολόγιο Jens για περαιτέρω επεξήγηση... Πρέπει απλώς να τροποποιήσουμε τις μακροεντολές λίγο για τις ανάγκες μας (θα χρησιμοποιήσω μικρότερα ονόματα και λιγότερα ορίσματα για συντομία) .

#define ARGN(...) ARGN_(__VA_ARGS__) #define ARGN_(_0, _1, _2, _3, N, ...) N #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0 ) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_COMMA(SET_COMMA __VA_ARGS__), \ HASVA_COM_MA \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3 # ορίστε COMMA_0000, #define COMMA_0001 #define COMMA_0010, // ... (όλα τα άλλα με κόμμα) #define COMMA_1111,

Και τώρα είμαστε καλά...

Πλήρης κώδικας σε ένα μπλοκ:

/* * demo.c * * Δημιουργήθηκε στις: 14-09-2017 * Συγγραφέας: sboehler */ #include void foo_void(void) ( puts("void"); ) void foo_int(int c) ( printf("int: %d\n", c); ) void foo_char(char c) ( printf("char: %c \n", c); ) void foo_double(double c) ( printf("double: %.2f\n", c); ) void foo_double_int(double c, int d) ( printf("double: %.2f, int: %d\n", c, d); ) #define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) # define CONCAT(X, Y) CONCAT_(X, Y) #define CONCAT_(X, Y) X ## Y #define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char : foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double: _Generic((_2), \ int: foo_double_int \) \) #define ARGN(...) ARGN_( __VA_ARGS__) #define ARGN_(_0, _1, _2, N, ...) N #define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0) #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_C OMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define _2, _3_COMMA, COMMA_ ## _0 ## _1 ## _2 ## _3 COMMA_1001 , #define COMMA_1010 , #define COMMA_1011 , #define COMMA_1100 , #define COMMA_1101 , #define COMMA_1101 , #define COMMA_1001, #define COMMA_1de, char #1define ( foo(); foo(7); foo(10.12); foo(12.10, 7); foo((char)"s"); επιστροφή 0; )

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

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

Ενσωματωμένες λειτουργίες

Κλήση μιας συνάρτησης, μετάδοση τιμών σε αυτήν, επιστροφή μιας τιμής - αυτές οι λειτουργίες απαιτούν πολύ χρόνο CPU. Συνήθως, κατά τον ορισμό μιας συνάρτησης, ο μεταγλωττιστής διατηρεί μόνο ένα μπλοκ κελιών στη μνήμη για να αποθηκεύσει τις δηλώσεις του. Μετά την κλήση της συνάρτησης, ο έλεγχος προγράμματος μεταφέρεται σε αυτούς τους τελεστές και μετά την επιστροφή από τη συνάρτηση, η εκτέλεση του προγράμματος συνεχίζεται από τη γραμμή που ακολουθεί την κλήση συνάρτησης.

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

Κάθε άλμα στην περιοχή μνήμης που περιέχει τις δηλώσεις συνάρτησης επιβραδύνει την εκτέλεση του προγράμματος. Εάν η συνάρτηση είναι μικρή, μπορείτε να εξοικονομήσετε χρόνο σε πολλαπλές κλήσεις δίνοντας εντολή στον μεταγλωττιστή να ενσωματώσει τον κωδικό λειτουργίας απευθείας στο πρόγραμμα στην τοποθεσία κλήσης. Τέτοιες συναρτήσεις ονομάζονται αντικατασταθεί. Σε αυτή την περίπτωση, μιλώντας για αποτελεσματικότητα, πρώτα απ 'όλα, υπονοείται η ταχύτητα εκτέλεσης του προγράμματος.

Inline ή Inline Λειτουργίεςείναι συναρτήσεις των οποίων ο κώδικας εισάγεται από τον μεταγλωττιστή απευθείας στην τοποθεσία κλήσης, αντί να μεταφέρεται ο έλεγχος σε μία μόνο παρουσία της συνάρτησης.

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

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

Για παράδειγμα:

/*συνάρτηση επιστρέφει απόσταση από σημείο με συντεταγμένες(x1,y1) σε σημείο με συντεταγμένες (x2,y2)*/ inline float Line(float x1,float y1,float x2, float y2) ( return sqrt(pow(x1-x2 ,2)+pow(y1-y2,2)); )

Ωστόσο, πρέπει να σημειωθεί ότι η χρήση ενσωματωμένων συναρτήσεων δεν οδηγεί πάντα σε θετικό αποτέλεσμα. Εάν μια τέτοια συνάρτηση καλείται πολλές φορές στον κώδικα του προγράμματος, τότε στο χρόνος μεταγλώττισηςΌσα αντίγραφα αυτής της συνάρτησης υπάρχουν κλήσεις προς αυτήν θα εισαχθούν στο πρόγραμμα. Θα υπάρξει σημαντική αύξηση στο μέγεθος του κώδικα προγράμματος, με αποτέλεσμα να μην προκύψει η αναμενόμενη αύξηση της αποτελεσματικότητας της εκτέλεσης του προγράμματος σε χρόνο.

Παράδειγμα 1.

#include "stdafx.h" #include χρησιμοποιώντας namespace std? inline int Cube(int x); int _tmain(int argc, _TCHAR* argv)( int x=2; float y=3; double z=4; cout<

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

  • η ενσωματωμένη συνάρτηση είναι αναδρομική.
  • συναρτήσεις των οποίων η κλήση τοποθετείται πριν από τον ορισμό της·
  • συναρτήσεις που καλούνται περισσότερες από μία φορές σε μια έκφραση.
  • λειτουργίες που περιέχουν βρόχους, διακόπτες και τελεστές άλματος;
  • συναρτήσεις που είναι πολύ μεγάλες για να επιτρέψουν την αντικατάσταση.

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

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

Υπερφόρτωση λειτουργίας

Κατά τον ορισμό συναρτήσεων σε προγράμματα, είναι απαραίτητο να προσδιορίσετε τον τύπο της τιμής που επιστρέφεται από τη συνάρτηση, καθώς και τον αριθμό των παραμέτρων και τον τύπο καθεμιάς από αυτές. Εάν υπήρχε μια συνάρτηση στη C++ που ονομάζεται add_values ​​· που λειτουργούσε με δύο ακέραιες τιμές και το πρόγραμμα έπρεπε να χρησιμοποιήσει μια παρόμοια συνάρτηση για να περάσει τρεις ακέραιες τιμές, τότε θα έπρεπε να δημιουργηθεί μια συνάρτηση με διαφορετικό όνομα. Για παράδειγμα, add_two_values ​​και add_three_values ​​. Ομοίως, εάν θέλετε να χρησιμοποιήσετε μια παρόμοια συνάρτηση για να εργαστείτε με τιμές float, τότε χρειάζεστε μια άλλη συνάρτηση με άλλο όνομα. Για να αποφύγετε την αντιγραφή συναρτήσεων, η C++ σας επιτρέπει να ορίσετε πολλές συναρτήσεις με το ίδιο όνομα. Κατά τη μεταγλώττιση, η C++ λαμβάνει υπόψη τον αριθμό των ορισμάτων που χρησιμοποιούνται από κάθε συνάρτηση και στη συνέχεια καλεί ακριβώς την απαιτούμενη συνάρτηση. Το να δώσουμε στον μεταγλωττιστή μια επιλογή μεταξύ πολλαπλών συναρτήσεων ονομάζεται υπερφόρτωση.

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

Ονομάζεται επίσης υπερφόρτωση συναρτήσεων πολυμορφισμός συνάρτησης. "Poly" σημαίνει πολλά, "morph" - μια μορφή, δηλαδή μια πολυμορφική συνάρτηση είναι μια συνάρτηση που διακρίνεται από μια ποικιλία μορφών.

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

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

Για παράδειγμα, το ακόλουθο πρόγραμμα υπερφορτώνει μια συνάρτηση με το όνομα add_values ​​. Ο πρώτος ορισμός συνάρτησης προσθέτει δύο τιμές τύπου int. Ο δεύτερος ορισμός συνάρτησης προσθέτει τρεις τιμές τύπου int. Κατά τη μεταγλώττιση, η C++ καθορίζει σωστά τη συνάρτηση που θα χρησιμοποιηθεί:

#include "stdafx.h" #include χρησιμοποιώντας namespace std? int add_values(int a,int b); int add_values ​​· (int a, int b, int c); int _tmain(int argc, _TCHAR* argv)( cout<< "200+801=" << add_values(200,801) << "\n"; cout << "100+201+700=" << add_values(100,201,700) << "\n"; system("pause"); return 0; } int add_values(int a,int b) { return(a + b); } int add_values (int a, int b, int c) { return(a + b + c); }

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

Χρήση υπερφόρτωσης λειτουργίας

Μία από τις πιο συνηθισμένες περιπτώσεις χρήσης για υπερφόρτωση είναι η χρήση μιας συνάρτησης για την παραγωγή ενός συγκεκριμένου αποτελέσματος με βάση διάφορες παραμέτρους. Για παράδειγμα, ας υποθέσουμε ότι το πρόγραμμά σας έχει μια συνάρτηση με το όνομα day_of_week που επιστρέφει την τρέχουσα ημέρα της εβδομάδας (0 για την Κυριακή, 1 για τη Δευτέρα, ... , 6 για το Σάββατο). Ένα πρόγραμμα θα μπορούσε να υπερφορτώσει αυτήν τη συνάρτηση, ώστε να επιστρέφει σωστά την ημέρα της εβδομάδας, εάν του δοθεί μια Ιουλιανή ημέρα ως παράμετρος ή εάν του δοθεί μια ημέρα, μήνας και έτος.

int day_of_week(int julian_day) ( // operators ) int day_of_week(int month, int day, int year) ( // τελεστές )

Χρησιμοποιώντας υπερφορτωμένες λειτουργίεςσυχνά γίνονται πολλά λάθη. Για παράδειγμα, εάν οι συναρτήσεις διαφέρουν μόνο στον τύπο επιστροφής, αλλά όχι στους τύπους ορίσματος, τέτοιες συναρτήσεις δεν μπορούν να έχουν το ίδιο όνομα. Η ακόλουθη επιλογή υπερφόρτωσης δεν είναι επίσης έγκυρη:

int function_name(int argument_name); int function_name(int argument_name); /* μη έγκυρη υπερφόρτωση ονόματος: τα ορίσματα έχουν τον ίδιο αριθμό και τον ίδιο τύπο */

Οφέλη από υπερφόρτωση λειτουργιών:

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

Παράδειγμα 2.

/*Οι υπερφορτωμένες συναρτήσεις έχουν το ίδιο όνομα αλλά διαφορετικές λίστες παραμέτρων και επιστρέφουν τιμές*/ #include "stdafx.h" #include χρησιμοποιώντας namespace std? int μέσος όρος (int first_number, int second_number, int three_number); int μέσος όρος(int first_number, int second_number); int _tmain(int argc, _TCHAR* argv)(// κύρια συνάρτηση int number_A = 5, number_B = 3, number_C = 10; cout<< "Целочисленное среднее чисел " << number_A << " и "; cout << number_B << " равно "; cout << average(number_A, number_B) << ".\n\n"; cout << "Целочисленное среднее чисел " << number_A << ", "; cout << number_B << " и " << number_C << " равно "; cout << average(number_A, number_B, number_C) << ".\n"; system("PAUSE"); return 0; }// конец главной функции /*функция для вычисления целочисленного среднего значения 3-х целых чисел*/ int average(int first_number, int second_number, int third_number) { return((first_number + second_number + third_number)/3); } // конец функции /*функция для вычисления целочисленного среднего значения 2-х целых чисел*/ int average(int first_number, int second_number) { return((first_number + second_number)/2); } // конец функции



Φόρτωση...
Μπλουζα