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

9.1. Υπερφορτωμένες δηλώσεις συναρτήσεων

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

ονομάζεται η πράξη της πρόσθεσης ακεραίων, ενώ η αξιολόγηση της έκφρασης

1.0 + 3.0

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

9.1.1. Γιατί πρέπει να υπερφορτώσετε το όνομα της συνάρτησης

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

int i_max(int, int); int vi_max(διάνυσμα const &); int matrix_max(const matrix &);

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

Int ix = max(j, k); διάνυσμα vec; //... int iy = max(vec);

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

9.1.2. Πώς να υπερφορτώνετε ένα όνομα συνάρτησης

Στη C++, δύο ή περισσότερες συναρτήσεις μπορούν να λάβουν το ίδιο όνομα, με την προϋπόθεση ότι οι λίστες παραμέτρων τους διαφέρουν είτε στον αριθμό των παραμέτρων είτε στον τύπο τους. ΣΤΟ αυτό το παράδειγμαδηλώνουμε μια υπερφορτωμένη συνάρτηση max():

intmax(int, int); int max(διάνυσμα Const &); int max(const matrix &);

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

  • εάν οι λίστες παραμέτρων δύο συναρτήσεων διαφέρουν ως προς τον αριθμό ή τους τύπους των παραμέτρων, τότε οι συναρτήσεις θεωρούνται υπερφορτωμένες: // overloaded functions void print(const string &); κενή εκτύπωση (διάνυσμα &);
  • εάν οι λίστες τύπων και παραμέτρων επιστροφής στις δηλώσεις δύο συναρτήσεων είναι ίδιες, τότε η δεύτερη δήλωση θεωρείται επαναλαμβανόμενη: // δηλώσεις της ίδιας συνάρτησης void print(const string &str); void print(const ststr &); Τα ονόματα παραμέτρων δεν λαμβάνονται υπόψη κατά τη σύγκριση των δηλώσεων.
    εάν οι λίστες παραμέτρων δύο συναρτήσεων είναι ίδιες, αλλά οι τύποι επιστροφής είναι διαφορετικοί, τότε η δεύτερη δήλωση θεωρείται άκυρη (σε αντίθεση με την πρώτη) και επισημαίνεται ως σφάλμα από τον μεταγλωττιστή: unsigned int max(int ​​· i1, int i2); int max(int ​​· i1, int i2);
    // σφάλμα: μόνο οι τύποι διαφέρουν
    // επιστρέφει τιμές

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

// δηλώσεις της ίδιας συνάρτησης int max (int *ia, int sz); int max (int *ia, int = 10);

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

// typedef δεν εισάγει νέο τύπο typedef διπλό ΔΟΛΑΡΙΟ. // σφάλμα: ίδιες λίστες παραμέτρων, αλλά διαφορετικοί // τύποι επιστροφής extern DOLLAR calc(DOLLAR); extern int calc(διπλό);

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

// δηλώνει την ίδια συνάρτηση void f(int); void f(const int);

Ο προσδιοριστής const είναι σημαντικός μόνο μέσα στον ορισμό της συνάρτησης: υποδεικνύει ότι απαγορεύεται η αλλαγή της τιμής της παραμέτρου στο σώμα της συνάρτησης. Ωστόσο, ένα όρισμα που μεταβιβάζεται με τιμή μπορεί να χρησιμοποιηθεί στο σώμα μιας συνάρτησης όπως μια κανονική μεταβλητή ενεργοποίησης: εκτός της συνάρτησης, οι αλλαγές δεν είναι ορατές. (Οι μέθοδοι μεταβίβασης ορισμάτων, ιδιαίτερα η μετάδοση με τιμή, εξετάζονται στην Ενότητα 7.3.) Η προσθήκη ενός προσδιοριστή const σε μια παράμετρο που μεταβιβάζεται με τιμή δεν επηρεάζει την ερμηνεία της. Οποιαδήποτε τιμή τύπου int μπορεί να μεταβιβαστεί σε μια συνάρτηση που δηλώνεται ως f(int), όπως και η συνάρτηση f(const int). Επειδή και οι δύο λαμβάνουν το ίδιο σύνολο τιμών ορισμάτων, οι παραπάνω δηλώσεις δεν θεωρούνται υπερφορτωμένες. Η f() μπορεί να οριστεί ως

Κενό f(int i) ( )

Κενό f(const int i) ( )

Η παρουσία αυτών των δύο ορισμών σε ένα πρόγραμμα είναι σφάλμα, αφού η ίδια συνάρτηση ορίζεται δύο φορές.
Ωστόσο, εάν ένας προσδιοριστής const ή volatile εφαρμόζεται σε μια παράμετρο ενός δείκτη ή ενός τύπου αναφοράς, τότε λαμβάνεται υπόψη κατά τη σύγκριση των δηλώσεων.

// διαφορετικές συναρτήσεις δηλώνονται void f(int*); void f(const int*); // και εδώ δηλώνονται διάφορες συναρτήσεις
void f(int&);
void f(const int&);

9.1.3. Πότε να μην υπερφορτώνετε ένα όνομα συνάρτησης

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

void setDate(Date&, int, int, int); Ημερομηνία &convertDate(συμβολοσειρά const &); void printDate(const Date&);

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

#περιλαμβάνω class Date ( public: set(int, int, int); Date& convert (const string &); void print(); // ...
};

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

Screen& moveHome(); Screen& moveAbs (int, int); Screen& moveRel(int, int, char *direction); Screen& moveX(int); Screen& moveY(int);

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

// συνάρτηση που συνδυάζει moveX() και moveY() Screen& move(int, char xy);

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

// ποια κλήση είναι πιο ξεκάθαρη; myScreen.home(); // νομίζουμε αυτό! myScreen.move();

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

MoveAbs(int, int); moveAbs(int, int, char*);

διαφέρουν παρουσία τρίτης παραμέτρου τύπου char*. Εάν οι υλοποιήσεις τους είναι παρόμοιες και μπορεί να βρεθεί μια λογική προεπιλεγμένη τιμή για το τρίτο όρισμα, τότε και οι δύο συναρτήσεις μπορούν να αντικατασταθούν από μία. Σε αυτήν την περίπτωση, ένας δείκτης με τιμή 0 είναι κατάλληλος για το ρόλο της προεπιλεγμένης τιμής:

Move(int, int, char* = 0);

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

9.1.4. Υπερφόρτωση και εμβέλεια Α

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

#περιλαμβάνω void print(const ststr &); voidprint (διπλό); // υπερφορτώνει την print() void fooBar(int ival)
{
// ξεχωριστό εύρος: κρύβει και τις δύο υλοποιήσεις της print()
extern void print(int); // σφάλμα: το print(const string &) δεν είναι ορατό σε αυτήν την περιοχή
print("Τιμή: ");
print(ival); // correct: η εκτύπωση(int) είναι ορατή
}

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

#περιλαμβάνω namespace IBM ( extern void print(const string &); extern void print(double); // overloads print() ) namespace Disney ( // ξεχωριστό εύρος: // δεν υπερφορτώνει τη συνάρτηση print() από το εξωτερικό κενό χώρου ονομάτων της IBM εκτύπωση (int);

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

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

Χώρος ονομάτων libs_R_us ( int max(int, int); int max(double, double); extern void print(int);
εξωτερική κενή εκτύπωση (διπλή)
) // χρήση-δηλώσεις
χρησιμοποιώντας libs_R_us::max;
χρησιμοποιώντας libs_R_us::print(double); // σφάλμα void func()
{
max(87, 65); // καλεί libs_R_us::max(int, int)
max(35,5, 76,6); // καλεί libs_R_us::max(διπλό, διπλό)

Η πρώτη δήλωση που χρησιμοποιεί φέρνει και τις δύο συναρτήσεις libs_R_us::max στο καθολικό εύρος. Τώρα οποιαδήποτε από τις συναρτήσεις max() μπορεί να κληθεί μέσα στη func(). Οι τύποι των ορισμάτων καθορίζουν ποια συνάρτηση θα κληθεί. Η δεύτερη δήλωση που χρησιμοποιεί είναι ένα σφάλμα: δεν μπορεί να έχει λίστα παραμέτρων. Η συνάρτηση libs_R_us::print() δηλώνεται μόνο ως εξής:

Χρήση libs_R_us::print;

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

Εκτύπωση(88);

ο συγγραφέας του χώρου ονομάτων αναμένει να κληθεί η συνάρτηση libs_R_us::print(int). Εάν επιτρέψετε στον χρήστη να συμπεριλάβει επιλεκτικά μόνο μία από τις πολλές υπερφορτωμένες συναρτήσεις στο πεδίο εφαρμογής, τότε η συμπεριφορά του προγράμματος γίνεται απρόβλεπτη.
Τι συμβαίνει εάν η δήλωση χρήσης φέρει στο πεδίο εφαρμογής μια συνάρτηση με ήδη υπάρχον όνομα; Αυτές οι συναρτήσεις μοιάζουν σαν να δηλώνονται ακριβώς εκεί που εμφανίζεται η δήλωση χρήσης. Επομένως, οι συναρτήσεις που εισάγονται συμμετέχουν στη διαδικασία επίλυσης των ονομάτων όλων των υπερφορτωμένων συναρτήσεων που υπάρχουν στο συγκεκριμένο εύρος:

#περιλαμβάνω namespace libs_R_us ( extern void print(int); extern void print(double); ) extern void print(const string &); // libs_R_us::print(int) και libs_R_us::print(double)
// υπερφόρτωση εκτύπωσης (συμβολοσειρά const &)
χρησιμοποιώντας libs_R_us::print; void fooBar(int ival)
{
// εκτύπωση (συμβολοσειρά const &)
}

Η δήλωση χρήσης προσθέτει δύο δηλώσεις στο καθολικό εύρος: μία για εκτύπωση(int) και μία για εκτύπωση (διπλή). Είναι ψευδώνυμα στο χώρο libs_R_us και περιλαμβάνονται σε ένα σύνολο υπερφορτωμένων συναρτήσεων με το όνομα print όπου υπάρχει ήδη η καθολική εκτύπωση (συμβολοσειρά const &). Κατά την επίλυση της υπερφόρτωσης εκτύπωσης στο fooBar, λαμβάνονται υπόψη και οι τρεις λειτουργίες.
Εάν μια δήλωση χρήσης εισάγει κάποια συνάρτηση σε ένα πεδίο που έχει ήδη μια συνάρτηση με το ίδιο όνομα και την ίδια λίστα παραμέτρων, θεωρείται σφάλμα. Μια δήλωση χρήσης δεν μπορεί να είναι ψευδώνυμο μιας συνάρτησης print(int) στον χώρο ονομάτων libs_R_us εάν η print(int) υπάρχει ήδη στο καθολικό εύρος. Για παράδειγμα:

Χώρος ονομάτων libs_R_us ( void print(int); void print(double); ) void print(int); χρησιμοποιώντας libs_R_us::print; // σφάλμα: επαναλαμβανόμενη δήλωση print(int) void fooBar(int ival)
{
print(ival); // ποια εκτύπωση; ::print ή libs_R_us::print
}

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

#περιλαμβάνω namespace libs_R_us ( extern void print(int); extern void print(double); ) extern void print(const string &); // χρησιμοποιώντας οδηγία
// print(int), print(double) και print(const string &) είναι στοιχεία
// το ίδιο σύνολο υπερφορτωμένων συναρτήσεων
χρησιμοποιώντας το χώρο ονομάτων libs_R_us; void fooBar(int ival)
{
print("Τιμή: "); // καλεί την καθολική συνάρτηση
// εκτύπωση (συμβολοσειρά const &)
print(ival); // καλεί libs_R_us::print(int)
}

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

Χώρος ονομάτων Η IBM ( int print(int); ) namespace Disney ( double print(double); ) // using-directive // ​​​παράγει πολλές υπερφορτωμένες συναρτήσεις από διαφορετικούς // χώρους ονομάτων χρησιμοποιώντας χώρο ονομάτων IBM. χρησιμοποιώντας χώρο ονομάτων Disney? μακριά διπλή εκτύπωση (μακριά διπλή) int main() (
print(1); // ονομάζεται IBM::print(int)
print(3.1); // καλέστε Disney::print(διπλή)
επιστροφή 0;
}

Το σύνολο των υπερφορτωμένων συναρτήσεων που ονομάζεται print στο καθολικό εύρος περιλαμβάνει τις λειτουργίες print(int), print (double) και print (long double). Όλα αυτά εξετάζονται στο main() κατά την ανάλυση υπερφόρτωσης, αν και αρχικά ορίστηκαν σε διαφορετικούς χώρους ονομάτων.
Έτσι, πάλι, οι υπερφορτωμένες συναρτήσεις βρίσκονται στο ίδιο πεδίο. Συγκεκριμένα, καταλήγουν εκεί ως αποτέλεσμα της χρήσης δηλώσεων και της χρήσης οδηγιών που καθιστούν διαθέσιμα ονόματα από άλλα πεδία.

9.1.5. εξωτερική οδηγία "C" και υπερφορτωμένες λειτουργίες A

Είδαμε στην Ενότητα 7.7 ότι η οδηγία εξωτερικής σύνδεσης "C" μπορεί να χρησιμοποιηθεί σε ένα πρόγραμμα C++ για να υποδείξει ότι κάποιο αντικείμενο βρίσκεται σε τμήμα C. Πώς επηρεάζει αυτή η οδηγία τις δηλώσεις συναρτήσεων υπερφόρτωσης; Μπορούν οι συναρτήσεις που γράφονται τόσο σε C++ όσο και σε C να βρίσκονται στο ίδιο σύνολο;
Μια οδηγία δέσμευσης επιτρέπεται να καθορίζει μόνο μία από τις πολλές υπερφορτωμένες λειτουργίες. Για παράδειγμα, το παρακάτω πρόγραμμα είναι λάθος:

// error: οδηγία καθορίζεται για δύο υπερφορτωμένες συναρτήσεις extern "C" extern "C" void print(const char*); extern "C" void print(int);

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

ClassSmallInt(/* ... */); κλάση BigNum(/* ... */); // μια συνάρτηση γραμμένη σε C μπορεί να κληθεί και από ένα πρόγραμμα,
// γραμμένο σε C ή από πρόγραμμα γραμμένο σε C++.
// Οι συναρτήσεις C++ χειρίζονται παραμέτρους που είναι κλάσεις
εξωτερικό "C" διπλό calc(διπλό);
extern SmallInt calc(const SmallInt&);
εξωτερικό BigNum calc(const BigNum&);

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

Smallint si = 8; int main() ( calc(34); // κλήση συνάρτησης C calc(double) calc(si); // κλήση συνάρτησης C++ calc(const SmallInt &) // ... επιστροφή 0; )

9.1.6. Δείκτες σε υπερφορτωμένες συναρτήσεις Α

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

εξωτερικό κενό ff(διάνυσμα ) extern void ff(unsigned int); // σε ποια συνάρτηση δείχνει το pf1;
void (*pf1)(unsigned int) =

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

εξωτερικό κενό ff(διάνυσμα ) extern void ff(unsigned int); // σφάλμα: η αντιστοίχιση δεν βρέθηκε: μη έγκυρη λίστα παραμέτρων void (*pf2)(int) = // σφάλμα: η αντιστοίχιση δεν βρέθηκε: λάθος τύπος επιστροφής double (*pf3)(διάνυσμα ) = &ff;

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

Matrix calc(const matrix &); intcalc(int, int); int (*pc1)(int, int) = 0;
int (*pc2)(int, double) = 0; //...
// correct: έχει επιλεγεί η συνάρτηση calc(int, int).
pc1 = // σφάλμα: καμία αντιστοίχιση: μη έγκυρος τύπος δεύτερης παραμέτρου
pc2=

9.1.7. Ασφαλής δέσιμο Α

Όταν χρησιμοποιείτε υπερφόρτωση, έχετε την εντύπωση ότι ένα πρόγραμμα μπορεί να έχει πολλές συναρτήσεις με το ίδιο όνομα με διαφορετικές λίστες παραμέτρων. Ωστόσο, αυτή η λεξιλογική ευκολία υπάρχει μόνο σε επίπεδο κειμένου πηγής. Στα περισσότερα συστήματα μεταγλώττισης, τα προγράμματα που επεξεργάζονται αυτό το κείμενο για να παράγουν εκτελέσιμο κώδικα απαιτούν όλα τα ονόματα να είναι διακριτά. Οι συντάκτες συνδέσμων το επιτρέπουν συνήθως εξωτερικοί σύνδεσμοιλεξιλογικά. Εάν ένας τέτοιος επεξεργαστής συναντήσει την εκτύπωση ονόματος δύο ή περισσότερες φορές, δεν μπορεί να τα διακρίνει με ανάλυση τύπου (οι πληροφορίες τύπου συνήθως χάνονται σε αυτό το σημείο). Έτσι απλώς εκτυπώνει ένα μήνυμα σχετικά με την εκ νέου καθορισμένη εκτύπωση χαρακτήρων και εξέρχεται.
Για να λυθεί αυτό το πρόβλημα, το όνομα της συνάρτησης, μαζί με τη λίστα παραμέτρων της, είναι διακοσμημένο για να δώσει ένα μοναδικό εσωτερικό όνομα. Τα προγράμματα που καλούνται μετά τον μεταγλωττιστή βλέπουν μόνο αυτό το εσωτερικό όνομα. Το πώς ακριβώς γίνεται αυτή η ανάλυση ονόματος εξαρτάται από την υλοποίηση. Η γενική ιδέα είναι να αναπαραστήσετε τον αριθμό και τους τύπους των παραμέτρων ως συμβολοσειρά χαρακτήρων και να την προσαρτήσετε στο όνομα της συνάρτησης.
Όπως αναφέρεται στην ενότητα 8.2, αυτή η κωδικοποίηση εγγυάται, ειδικότερα, ότι δύο δηλώσεις συναρτήσεων με το ίδιο όνομα με διαφορετικές λίστες παραμέτρων που βρίσκονται σε διαφορετικά αρχεία δεν γίνονται αντιληπτές από το σύνδεσμο ως δηλώσεις της ίδιας συνάρτησης. Δεδομένου ότι αυτή η μέθοδος βοηθά στη διάκριση μεταξύ υπερφορτωμένων λειτουργιών κατά τη φάση επεξεργασίας συνδέσμων, μιλάμε για ασφαλή σύνδεση.
Η διακόσμηση ονομάτων δεν ισχύει για συναρτήσεις που δηλώνονται με την εξωτερική οδηγία "C", καθώς μόνο μία από τις πολλές υπερφορτωμένες συναρτήσεις μπορεί να γραφτεί σε καθαρό C. Δύο συναρτήσεις με διαφορετικές λίστες παραμέτρων που δηλώνονται ως εξωτερικό "C" αντιμετωπίζονται από το σύνδεσμο ως μία και τον ίδιο χαρακτήρα.

Άσκηση 9.1

Γιατί θα πρέπει να δηλώσετε υπερφορτωμένες συναρτήσεις;

Άσκηση 9.2

Πώς να δηλώσετε τις υπερφορτωμένες εκδόσεις της συνάρτησης error() έτσι ώστε οι ακόλουθες κλήσεις να είναι σωστές:

Int index; int upperBound; char selectVal; // ... error("Array out of bounds: ", index, upperBound); error("Διαίρεση με μηδέν"); error("Μη έγκυρη επιλογή", selectVal);

Άσκηση 9.3

Εξηγήστε την επίδραση της δεύτερης δήλωσης σε καθένα από τα ακόλουθα παραδείγματα:

(α) intcalc(int, int); int calc(const int, const int); (β) int get(); double get(); (γ) int *reset(int *); double *reset(double *): (d) extern "C" int compute(int *, int); extern "C" double compute(double *, double);

Άσκηση 9.4

Ποια από τις ακόλουθες αρχικοποιήσεις οδηγεί σε σφάλμα; Γιατί;

(α) void reset(int *); void (*pf)(void *) = reset; (β) intcalc(int, int); int (*pf1)(int, int) = υπολογ. (γ) εξωτερικό "C" int compute(int *, int); int (*pf3)(int*, int) = υπολογισμός; (δ) void (*pf4)(const matrix &) = 0;

9.2. Τρία βήματα ανάλυσης υπερφόρτωσης

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

T t1, t2; void f(int, int); void f(float, float); int main() (
f(t1, t2);
επιστροφή 0;
}

Εδώ, κατά τη διαδικασία επίλυσης υπερφόρτωσης, ανάλογα με τον τύπο του T, καθορίζεται εάν η συνάρτηση f(int,int) ή f(float,float) θα κληθεί κατά την επεξεργασία της έκφρασης f(t1,t2) ή ένα σφάλμα θα καταγραφεί.
Η ανάλυση υπερφόρτωσης συναρτήσεων είναι μια από τις πιο σύνθετες πτυχές της γλώσσας C++. Προσπαθώντας να κατανοήσουν όλες τις λεπτομέρειες, οι αρχάριοι προγραμματιστές θα αντιμετωπίσουν σοβαρές δυσκολίες. Επομένως, σε αυτός ο τομέαςθα παρουσιάσουμε μόνο σύντομη κριτικήγια το πώς λειτουργεί η ανάλυση υπερφόρτωσης, ώστε να έχετε τουλάχιστον κάποια εντύπωση για τη διαδικασία. Για όσους θέλουν να μάθουν περισσότερα, οι επόμενες δύο ενότητες παρέχουν μια πιο λεπτομερή περιγραφή.
Η διαδικασία επίλυσης μιας υπερφόρτωσης συνάρτησης αποτελείται από τρία βήματα, τα οποία θα δείξουμε στο ακόλουθο παράδειγμα:

void f(); void f(int); void f(double, double = 3,4); void f(char *, char *); void main() (
f(5.6);
επιστροφή 0;
}

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

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

Ας εξετάσουμε κάθε στοιχείο με τη σειρά.
Το πρώτο βήμα είναι να προσδιορίσετε το σύνολο των υπερφορτωμένων συναρτήσεων που θα ληφθούν υπόψη σε αυτήν την κλήση. Οι λειτουργίες που περιλαμβάνονται σε αυτό το σύνολο ονομάζονται υποψήφιοι. Μια υποψήφια συνάρτηση είναι μια συνάρτηση με το ίδιο όνομα με αυτήν που καλείται και η δήλωσή της είναι ορατή στο σημείο κλήσης. Στο παράδειγμά μας, υπάρχουν τέσσερις τέτοιοι υποψήφιοι: f(), f(int), f(double, double) και f(char*, char*).
Μετά από αυτό, προσδιορίζονται οι ιδιότητες της λίστας με τα περασμένα ορίσματα, δηλ. τον αριθμό και τους τύπους τους. Στο παράδειγμά μας, η λίστα αποτελείται από δύο διπλά ορίσματα.
Στο δεύτερο βήμα, επιλέγονται βιώσιμα από το σύνολο των υποψηφίων - αυτά που μπορούν να κληθούν με τα δεδομένα ορίσματα. Η μόνιμη συνάρτηση είτε έχει τόσες τυπικές παραμέτρους όσες είναι ο αριθμός των πραγματικών ορισμάτων που διαβιβάστηκαν στην καλούμενη συνάρτηση ή περισσότερες, αλλά στη συνέχεια, για κάθε πρόσθετη παράμετρο, προεπιλεγμένη τιμή. Για να θεωρείται μια συνάρτηση μόνιμη, κάθε πραγματικό όρισμα που μεταβιβάζεται στην κλήση πρέπει να έχει μια μετατροπή στον τύπο της επίσημης παραμέτρου που καθορίζεται στη δήλωση.

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

  • η συνάρτηση f(int) επιβίωσε επειδή έχει μόνο μία παράμετρο και υπάρχει μετατροπή του πραγματικού διπλού ορίσματος στην επίσημη παράμετρο int.
  • η συνάρτηση f(double,double) επιβίωσε επειδή υπάρχει μια προεπιλεγμένη τιμή για το δεύτερο όρισμα και η πρώτη επίσημη παράμετρος είναι τύπου double, που είναι ακριβώς ο τύπος του πραγματικού ορίσματος.

Εάν μετά το δεύτερο βήμα δεν βρέθηκαν σταθερές συναρτήσεις, τότε η κλήση θεωρείται λανθασμένη. Σε τέτοιες περιπτώσεις λέμε ότι λείπει η αλληλογραφία.
Το τρίτο βήμα είναι να επιλέξετε τη λειτουργία που ταιριάζει καλύτερα στο πλαίσιο της κλήσης. Μια τέτοια λειτουργία ονομάζεται η καλύτερη στάση (ή η πιο κατάλληλη). Σε αυτό το βήμα, κατατάσσονται οι μετατροπές που χρησιμοποιούνται για τη μετάδοση των τύπων των πραγματικών ορισμάτων στους τύπους των τυπικών παραμέτρων της καθιερωμένης συνάρτησης. Η καταλληλότερη λειτουργία θεωρείται αυτή για την οποία πληρούνται οι ακόλουθες προϋποθέσεις:
Οι μετασχηματισμοί που εφαρμόζονται στα πραγματικά ορίσματα δεν είναι χειρότεροι από τους μετασχηματισμούς που απαιτούνται για την κλήση οποιασδήποτε άλλης καλά εδραιωμένης συνάρτησης.
για ορισμένα ορίσματα, οι μετατροπές που εφαρμόζονται είναι καλύτερες από τις μετατροπές που απαιτούνται για τη μετάδοση των ίδιων ορισμάτων σε κλήσεις προς άλλες καθιερωμένες συναρτήσεις.
Οι μετατροπές τύπων και η κατάταξή τους αναλύονται λεπτομερέστερα στην ενότητα 9.3. Εδώ, θα δούμε μόνο εν συντομία τους μετασχηματισμούς κατάταξης για το παράδειγμά μας. Για την καθιερωμένη συνάρτηση f(int), πρέπει να εφαρμοστεί το τυπικό cast του πραγματικού ορίσματος τύπου double σε int. Για την καθιερωμένη συνάρτηση f(double,double) ο τύπος του πραγματικού ορίσματος double ταιριάζει ακριβώς με τον τύπο της τυπικής παραμέτρου. Δεδομένου ότι μια ακριβής αντιστοίχιση είναι καλύτερη από μια τυπική μετατροπή (καμία μετατροπή δεν είναι πάντα καλύτερη από την ύπαρξη μιας), η f(double,double) θεωρείται η καταλληλότερη συνάρτηση για αυτήν την κλήση.
Εάν στο τρίτο βήμα δεν είναι δυνατό να βρεθεί η μόνη καλύτερη από τις καθιερωμένες συναρτήσεις, με άλλα λόγια, δεν υπάρχει τέτοια καθιερωμένη συνάρτηση που να ταιριάζει καλύτερα από όλες τις άλλες, τότε η κλήση θεωρείται διφορούμενη, δηλ. σφαλμένος.
(Η ενότητα 9.4 περιγράφει όλα τα βήματα ανάλυσης υπερφόρτωσης με περισσότερες λεπτομέρειες. Η διαδικασία επίλυσης χρησιμοποιείται επίσης όταν καλείται μια συνάρτηση μέλους κλάσης με υπερφόρτωση και ένας υπερφορτωμένος τελεστής. Η ενότητα 15.10 εξετάζει τους κανόνες ανάλυσης υπερφόρτωσης που ισχύουν για τις συναρτήσεις μέλους κλάσης και η Ενότητα 15.11 εξετάζει το κανόνες για (υπερφορτωμένους τελεστές. Η ανάλυση υπερφόρτωσης θα πρέπει επίσης να λαμβάνει υπόψη τις λειτουργίες που δημιουργούνται από πρότυπα. Η ενότητα 10.8 περιγράφει πώς τα πρότυπα επηρεάζουν αυτήν την ανάλυση.)

Άσκηση 9.5

Τι συμβαίνει στο τελευταίο (τρίτο) βήμα της διαδικασίας επίλυσης υπερφόρτωσης συναρτήσεων;

9.3. Μετατροπές τύπου επιχειρήματος Α

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

  • ακριβής αντιστοιχία. Ο τύπος του πραγματικού ορίσματος ταιριάζει ακριβώς με τον τύπο της τυπικής παραμέτρου. Για παράδειγμα, εάν το σύνολο των συναρτήσεων υπερφορτωμένων print() έχει τις εξής: void print(unsigned int); void print(const char*); void print(char);
  • τότε καθεμία από τις ακόλουθες τρεις κλήσεις δίνει μια ακριβή αντιστοίχιση:
    ανυπόγραφο int a?
print("a"); // ταιριάζει με το print(char); print("a"); // ταιριάζει με την εκτύπωση (const char*); print(a); // ταιριάζει με την εκτύπωση (unsigned int);
  • αντιστοίχιση με μετατροπή τύπου. Ο τύπος του πραγματικού ορίσματος δεν ταιριάζει με τον τύπο της επίσημης παραμέτρου, αλλά μπορεί να μετατραπεί σε αυτό: void ff(char); ff(0); // το όρισμα int μεταφέρεται σε char
  • έλλειψη συμμόρφωσης. Ο τύπος του πραγματικού ορίσματος δεν μπορεί να μεταφερθεί στον τύπο της επίσημης παραμέτρου στη δήλωση συνάρτησης επειδή δεν υπάρχει η απαιτούμενη μετατροπή. Δεν υπάρχει αντιστοιχία για καθεμία από τις ακόλουθες δύο κλήσεις στη συνάρτηση print():
  • Οι // print() συναρτήσεις δηλώνονται όπως παραπάνω int *ip; κλάση SmallInt ( /* ... */ ); SmallInt si; print(ip); // σφάλμα: δεν ταιριάζει
    print(si); // σφάλμα: δεν ταιριάζει
  • Για να καθοριστεί μια ακριβής αντιστοίχιση, ο τύπος του πραγματικού ορίσματος δεν χρειάζεται να ταιριάζει με τον τύπο της επίσημης παραμέτρου. Μπορούν να εφαρμοστούν ορισμένοι ασήμαντοι μετασχηματισμοί στο όρισμα, και συγκεκριμένα:

    • μετατροπή l-value σε r-value.
    • μετατροπή ενός πίνακα σε δείκτη.
    • μετατροπή μιας συνάρτησης σε δείκτη.
    • μετατροπές προσδιοριστή.

    (Αναλυθούν λεπτομερέστερα παρακάτω.) Η κατηγορία της συμμόρφωσης με τη μετατροπή τύπου είναι η πιο περίπλοκη. Υπάρχουν πολλά είδη τέτοιου cast που πρέπει να λάβετε υπόψη: επεκτάσεις τύπου (προωθήσεις), τυπικές μετατροπές και μετατροπές που καθορίζονται από τον χρήστη. (Οι επεκτάσεις τύπων και οι τυπικές μετατροπές καλύπτονται σε αυτό το κεφάλαιο. Οι μετατροπές που καθορίζονται από το χρήστη θα παρουσιαστούν αργότερα, αφού οι κλάσεις έχουν συζητηθεί λεπτομερώς· εκτελούνται από τον μετατροπέα, μια συνάρτηση μέλους που επιτρέπει σε μια κλάση να ορίσει το δικό της σύνολο « τυπικοί μετασχηματισμοί. Στο Κεφάλαιο 15, θα μάθουμε για τους μετατροπείς και πώς επηρεάζουν την ανάλυση υπερφόρτωσης συναρτήσεων.)
    Κατά την επιλογή της καλύτερης καθιερωμένης συνάρτησης για μια δεδομένη κλήση, ο μεταγλωττιστής αναζητά τη συνάρτηση για την οποία οι μετασχηματισμοί που εφαρμόζονται στα πραγματικά ορίσματα είναι οι "καλύτεροι". Οι μετατροπές τύπων ταξινομούνται ως εξής: μια ακριβής αντιστοίχιση είναι καλύτερη από μια επέκταση τύπου, μια επέκταση τύπου είναι καλύτερη από μια τυπική μετατροπή και αυτή με τη σειρά της είναι καλύτερη από μια μετατροπή που ορίζεται από τον χρήστη. Θα επανέλθουμε στην κατάταξη στην Ενότητα 9.4, αλλά προς το παρόν απλά παραδείγματαΑς δείξουμε πώς βοηθάει η επιλογή της καταλληλότερης λειτουργίας.

    9.3.1. Μάθετε περισσότερα για την Ακριβή αντιστοίχιση

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

    int max(int, int); διπλό μέγιστο (διπλό, διπλό); int i1; void calc (διπλό d1) (
    max(56, i1); // αντιστοιχεί ακριβώς στο max(int, int);
    max(d1, 66,9); // αντιστοιχεί ακριβώς στο μέγιστο (διπλό, διπλό);
    }

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

    Enum Tokens ( INLINE = 128; VIRTUAL = 129; ); Tokens curTok = INLINE; enum Stat(Αποτυχία, Pass); extern void ff(Tokens);
    extern void ff(Stat);
    extern void ff(int); int main() (
    ff(Pass); // ταιριάζει ακριβώς με ff(Στατ.)
    ff(0); // ταιριάζει ακριβώς με ff(int)
    ff(curTok); // ταιριάζει ακριβώς με ff(Tokens)
    // ...
    }

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

    • μπορείτε να πάρετε τη διεύθυνση του αντικειμένου.
    • Μπορείτε να πάρετε την τιμή ενός αντικειμένου.
    • Αυτή η τιμή είναι εύκολο να τροποποιηθεί (εκτός αν υπάρχει ένας προσδιοριστής const στη δήλωση αντικειμένου).

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

    intcalc(int); int main() ( int lval, res; lval = 5; // lvalue: lval; rvalue: 5
    res = calc(lval);
    // lvalue:res
    // rvalue: προσωρινό αντικείμενο για την αποθήκευση της τιμής,
    // επιστρέφεται από τη συνάρτηση calc().
    επιστροφή 0;
    }

    Στην πρώτη δήλωση εκχώρησης, η μεταβλητή lval είναι η τιμή l και η κυριολεκτική 5 είναι η τιμή r. Στη δεύτερη πρόταση εκχώρησης, το res είναι η τιμή l και το προσωρινό αντικείμενο που αποθηκεύει το αποτέλεσμα που επιστρέφεται από τη συνάρτηση calc() είναι η τιμή r.
    Σε ορισμένες περιπτώσεις, σε ένα περιβάλλον όπου αναμένεται μια τιμή, μπορεί να χρησιμοποιηθεί μια έκφραση που είναι τιμή l:

    intobj1; intobj2; int main() (
    // ...
    intlocal = obj1 + obj2;
    επιστροφή 0;
    }

    Εδώ το obj1 και το obj2 είναι τιμές l. Ωστόσο, για να πραγματοποιηθεί η προσθήκη στη συνάρτηση main(), οι τιμές τους εξάγονται από τις μεταβλητές obj1 και obj2. Η πράξη εξαγωγής της τιμής ενός αντικειμένου που αντιπροσωπεύεται από μια έκφραση τιμής l ονομάζεται μετατροπή μιας τιμής l σε τιμή r.
    Όταν η συνάρτηση αναμένει ένα όρισμα μεταβιβασμένο από τιμή, εάν το όρισμα είναι τιμή l, μετατρέπεται σε τιμή r:

    #περιλαμβάνω stringcolor ("μωβ"); voidprint(string); int main() (
    εκτύπωση (έγχρωμη); // ακριβής αντιστοίχιση: μετατροπή lvalue
    // σε rvalue
    επιστροφή 0;
    }

    Εφόσον το όρισμα στην κλήση print(color) μεταβιβάζεται με τιμή, η τιμή l μετατρέπεται σε τιμή r για να εξαχθεί η τιμή του χρώματος και να μεταβιβαστεί στη συνάρτηση πρωτότυπου print(string). Ωστόσο, παρόλο που έχει πραγματοποιηθεί μια τέτοια χύτευση, το πραγματικό όρισμα χρώματος θεωρείται ότι ταιριάζει ακριβώς με τη δήλωση εκτύπωσης (string).
    Κατά την κλήση συναρτήσεων, δεν είναι πάντα απαραίτητο να εφαρμόζεται μια τέτοια μετατροπή σε ορίσματα. Η αναφορά είναι μια τιμή l. εάν η συνάρτηση έχει μια παράμετρο αναφοράς, τότε όταν καλείται η συνάρτηση, λαμβάνει μια τιμή l. Επομένως, ο μετασχηματισμός που περιγράφεται δεν εφαρμόζεται στο πραγματικό όρισμα στο οποίο αντιστοιχεί η επίσημη παράμετρος αναφοράς. Για παράδειγμα, ας υποθέσουμε ότι έχει δηλωθεί η ακόλουθη συνάρτηση:

    #περιλαμβάνω void print(list &);

    Στην κλήση παρακάτω, το li είναι η τιμή l που αντιπροσωπεύει το αντικείμενο της λίστας , μεταβιβάστηκε στη συνάρτηση print():

    Λίστα li(20); int main() (
    // ...
    print(li); // ακριβής αντιστοίχιση: καμία μετατροπή από lvalue σε
    // rvalue
    επιστροφή 0;
    }

    Η αντιστοίχιση li με μια παράμετρο αναφοράς θεωρείται ακριβής αντιστοίχιση.
    Η δεύτερη μετατροπή, η οποία εξακολουθεί να διορθώνει μια ακριβή αντιστοίχιση, είναι η μετατροπή ενός πίνακα σε δείκτη. Όπως σημειώθηκε στην Ενότητα 7.3, μια παράμετρος συνάρτησης δεν είναι ποτέ τύπος πίνακα, αλλά μετατρέπεται σε δείκτη στο πρώτο της στοιχείο. Ομοίως, ένα όρισμα πραγματικού τύπου πίνακα από το NT (όπου N είναι ο αριθμός των στοιχείων στον πίνακα και T είναι ο τύπος κάθε στοιχείου) μεταφέρεται πάντα σε έναν δείκτη σε T. Αυτή η μετατροπή τύπου του πραγματικού ορίσματος ονομάζεται πίνακας σε -μετατροπή δείκτη. Παρόλα αυτά, το πραγματικό όρισμα θεωρείται ότι ταιριάζει ακριβώς με την επίσημη παράμετρο του τύπου "δείκτης προς Τ". Για παράδειγμα:

    int ai; void putValues(int*); int main() (
    // ...
    putValues(ai); // ακριβής αντιστοίχιση: μετατροπή πίνακα σε
    // δείκτης
    επιστροφή 0;
    }

    Πριν καλέσετε τη συνάρτηση putValues(), ο πίνακας μετατρέπεται σε δείκτη, με αποτέλεσμα το πραγματικό όρισμα ai (ένας πίνακας τριών ακεραίων) να μεταφέρεται σε δείκτη σε int. Αν και η επίσημη παράμετρος της συνάρτησης putValues() είναι δείκτης και το πραγματικό όρισμα μετατρέπεται όταν καλείται, δημιουργείται μια ακριβής αντιστοιχία μεταξύ τους.
    Κατά τον καθορισμό μιας ακριβούς αντιστοίχισης, είναι επίσης δυνατή η μετατροπή της συνάρτησης σε δείκτη. (Αναφέρθηκε στην Ενότητα 7.9.) Όπως μια παράμετρος πίνακα, μια παράμετρος συνάρτησης γίνεται δείκτης συνάρτησης. Το πραγματικό όρισμα τύπου "function" μεταδίδεται αυτόματα στον τύπο ενός δείκτη συνάρτησης. Αυτή η μετατροπή τύπου του πραγματικού ορίσματος ονομάζεται μετατροπή συνάρτησης σε δείκτη. Ενώ εκτελείται ο μετασχηματισμός, το πραγματικό όρισμα θεωρείται ότι ταιριάζει ακριβώς με την επίσημη παράμετρο. Για παράδειγμα:

    Int lexicoCompare(const string &, const string &); typedef int (*PFI)(const string &, const string &);
    void sort(string *, string *, PFI); χορδή ως? int main()
    {
    // ...
    ταξινόμηση (όπως,
    ως + μέγεθος(ως)/μέγεθος(ως - 1),
    lexicoΣύγκριση // ακριβής αντιστοίχιση
    // μετατροπή συνάρτησης σε δείκτη
    ) επιστροφή 0;
    }

    Πριν από την κλήση sort(), εφαρμόζεται μια μετατροπή συνάρτησης σε δείκτη, η οποία μεταφέρει το όρισμα lexicoCompare από τον τύπο "function" στον τύπο "pointer to function". Αν και το επίσημο όρισμα της συνάρτησης είναι δείκτης και το πραγματικό όρισμα είναι το όνομα της συνάρτησης, και επομένως η συνάρτηση έχει μετατραπεί σε δείκτη, το πραγματικό όρισμα θεωρείται ότι είναι ακριβώς η τρίτη επίσημη παράμετρος της συνάρτησης sort() .
    Το τελευταίο από τα παραπάνω είναι ο μετασχηματισμός των προσδιοριστών. Ισχύει μόνο για δείκτες και αποτελείται από την προσθήκη προσδιοριστών σταθερότητας ή πτητικού (ή και των δύο) στον τύπο που απευθύνεται στον συγκεκριμένο δείκτη:

    Int a = (4454, 7864, 92, 421, 938); int*pi = a; bool is_equal(const int * , const int *); void func(int *parm) ( // ακριβής αντιστοίχιση μεταξύ pi και parm: μετατροπή προσδιοριστών
    if (is_equal(pi, parm))
    // ... επιστροφή 0;
    }

    Πριν καλέσετε τη συνάρτηση is_equal(), τα πραγματικά ορίσματα pi και parm μετατρέπονται από τον τύπο "pointer to int" στον τύπο "pointer to const int". Αυτός ο μετασχηματισμός συνίσταται στην προσθήκη ενός προσδιοριστή const στον τύπο διεύθυνσης, και επομένως ανήκει στην κατηγορία των μετασχηματισμών προσδιοριστή. Παρόλο που η συνάρτηση αναμένει να λάβει δύο δείκτες σε μια const int και τα πραγματικά ορίσματα είναι δείκτες σε μια int, υποτίθεται ότι υπάρχει ακριβής αντιστοίχιση μεταξύ των τυπικών και πραγματικών παραμέτρων της συνάρτησης is_equal().
    Η μετατροπή προσδιοριστή ισχύει μόνο για τον τύπο στον οποίο απευθύνεται ο δείκτης. Δεν χρησιμοποιείται όταν η τυπική παράμετρος έχει προσδιοριστή const ή πτητικό, αλλά το πραγματικό όρισμα δεν έχει.

    extern void takeCI(const int); int main() (
    int ii = ...;
    takeCI(ii); // η μετατροπή προσδιοριστή δεν εφαρμόζεται
    επιστροφή 0;
    }

    Αν και η επίσημη παράμετρος της συνάρτησης takeCI() είναι τύπου const int και καλείται με όρισμα ii τύπου int, δεν υπάρχει μετατροπή προσδιοριστή: υπάρχει ακριβής αντιστοίχιση μεταξύ του πραγματικού ορίσματος και της τυπικής παραμέτρου.
    Όλα τα παραπάνω ισχύουν επίσης για την περίπτωση που το όρισμα είναι δείκτης και οι προσδιοριστές const ή volatile αναφέρονται σε αυτόν τον δείκτη:

    extern void init(int *const); extern int *pi; int main() (
    // ...
    init(pi); // η μετατροπή προσδιοριστή δεν εφαρμόζεται
    επιστροφή 0;
    }

    Ο προσδιοριστής const στην επίσημη παράμετρο της συνάρτησης init() αναφέρεται στον ίδιο τον δείκτη, όχι στον τύπο που απευθύνεται. Επομένως, ο μεταγλωττιστής δεν λαμβάνει υπόψη αυτόν τον προσδιοριστή κατά την ανάλυση των μετατροπών που θα εφαρμοστούν στο πραγματικό όρισμα. Δεν εφαρμόζεται μετατροπή προσδιοριστή στο όρισμα pi: αυτό το όρισμα και η επίσημη παράμετρος θεωρείται ότι ταιριάζουν ακριβώς.
    Οι τρεις πρώτοι από αυτούς τους μετασχηματισμούς (l-value σε r-value, array-to-pointer και function-to-pointer) αναφέρονται συχνά ως μετασχηματισμοί τιμής l. (Θα δούμε στην Ενότητα 9.4 ότι αν και τόσο οι μετασχηματισμοί τιμής l όσο και οι μετασχηματισμοί προσδιοριστή εμπίπτουν στην κατηγορία των μετασχηματισμών που δεν παραβιάζουν μια ακριβή αντιστοίχιση, ο βαθμός του θεωρείται υψηλότερος όταν χρειάζεται μόνο ο πρώτος μετασχηματισμός. Θα μιλήσουμε για αυτό στο λίγο περισσότερες λεπτομέρειες στην επόμενη ενότητα. .)
    Μια ακριβής αντιστοίχιση μπορεί να εξαναγκαστεί χρησιμοποιώντας χύτευση σαφούς τύπου. Για παράδειγμα, εάν υπάρχουν δύο υπερφορτωμένες λειτουργίες:

    extern void ff(int); extern void ff(void*);

    FF(0xffbc); // κλήση ff(int)

    θα ταιριάζει ακριβώς με το ff(int), παρόλο που η κυριολεκτική 0xffbc γράφεται ως δεκαεξαδική σταθερά. Ο προγραμματιστής μπορεί να αναγκάσει τον μεταγλωττιστή να καλέσει τη συνάρτηση ff(void *) εκτελώντας ρητά μια λειτουργία cast:

    FF(reinterpret_cast (0xffbc)); // κλήση ff(void*)

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

    9.3.2. Περισσότερα για την επέκταση τύπου

    Μια επέκταση τύπου είναι μία από τις ακόλουθες μετατροπές:

    • το πραγματικό όρισμα τύπου char, ανυπόγραφο char ή short επεκτείνεται σε τύπο int. Το πραγματικό όρισμα του τύπου unsigned short επεκτείνεται στον τύπο int εάν το μέγεθος μηχανής του int είναι μεγαλύτερο από το μέγεθος του short και στον τύπο unsigned int διαφορετικά.
    • το όρισμα τύπου float επεκτείνεται στον τύπο double.
    • ένα όρισμα απαριθμημένου τύπου επεκτείνεται στον πρώτο από τους ακόλουθους τύπους που είναι ικανός να αναπαριστά όλες τις τιμές μελών enum: int, unsigned int, long, unsigned long;
    • Το όρισμα bool επεκτείνεται σε τύπο int.

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

    extern void manip(int); int main() (
    manip("a"); // πληκτρολογήστε char επεκτείνεται σε int
    επιστροφή 0;
    }

    Ένας χαρακτήρας literal είναι τύπου char. Επεκτείνεται σε ενθ. Εφόσον ο εκτεταμένος τύπος αντιστοιχεί στον τύπο της επίσημης παραμέτρου της συνάρτησης manip(), λέμε ότι η κλήση του απαιτεί επέκταση του τύπου ορίσματος.
    Εξετάστε το ακόλουθο παράδειγμα:

    extern void print (unsigned int); extern void print(int); extern void print(char); ανυπόγραφο char uc;
    print(uc); // print(int); uc χρειάζεται μόνο μια επέκταση τύπου

    Σε μια πλατφόρμα υλικού όπου ο ανυπόγραφος χαρακτήρας παίρνει ένα byte μνήμης και το int τέσσερα byte, η επέκταση μετατρέπει τον ανυπόγραφο χαρακτήρα σε int επειδή μπορεί να αντιπροσωπεύει όλες τις τιμές ανυπόγραφου χαρακτήρες. Για μια τέτοια αρχιτεκτονική μηχανής, από τις πολλές υπερφορτωμένες συναρτήσεις που εμφανίζονται στο παράδειγμα, το print(int) παρέχει την καλύτερη αντιστοίχιση για ένα όρισμα ανυπόγραφου χαρακτήρα. Για τις άλλες δύο λειτουργίες, η αντιστοίχιση απαιτεί ένα τυπικό καστ.
    Το ακόλουθο παράδειγμα επεξηγεί την επέκταση ενός πραγματικού επιχειρήματος απαριθμημένου τύπου:

    Enum Stat(Αποτυχία, Pass); extern void ff(int);
    extern void ff(char); int main() (
    // correct: enum Member Pass επεκτείνεται για να πληκτρολογήσει int
    ff(Pass); // ff(int)
    ff(0); // ff(int)
    }

    Μερικές φορές η επέκταση των αριθμών φέρνει εκπλήξεις. Οι μεταγλωττιστές συχνά επιλέγουν την αναπαράσταση ενός enum με βάση τις τιμές των στοιχείων του. Ας υποθέσουμε ότι η παραπάνω αρχιτεκτονική (ένα byte για char και τέσσερα byte για int) ορίζει ένα enum όπως αυτό:

    Αριθμός e1 (a1, b1, c1);

    Δεδομένου ότι υπάρχουν μόνο τρία στοιχεία a1, b1 και c1 με τιμές 0, 1 και 2 αντίστοιχα - και δεδομένου ότι όλες αυτές οι τιμές μπορούν να αναπαρασταθούν με τύπο char, τότε ο μεταγλωττιστής θα επιλέξει συνήθως το char για να αναπαραστήσει τον τύπο e1. Σκεφτείτε, ωστόσο, την απαρίθμηση e2 με το ακόλουθο σύνολο στοιχείων:

    Αριθμός e2 (a2, b2, c2=0x80000000 );

    Εφόσον μία από τις σταθερές έχει την τιμή 0x80000000, ο μεταγλωττιστής πρέπει να επιλέξει να αναπαραστήσει το e2 με έναν τύπο που είναι επαρκής για την αποθήκευση της τιμής 0x80000000, δηλαδή, χωρίς υπογραφή int.
    Έτσι, αν και τόσο το e1 όσο και το e2 είναι αριθμοί, οι αναπαραστάσεις τους είναι διαφορετικές. Εξαιτίας αυτού, τα e1 και e2 επεκτείνονται σε διαφορετικούς τύπους:

    #περιλαμβάνω μορφή συμβολοσειράς (int);
    μορφή συμβολοσειράς (ανυπόγραφο int); int main() (
    μορφή(a1); // μορφή κλήσης (int)
    μορφή(a2); // μορφή κλήσης (μη υπογεγραμμένο int)
    επιστροφή 0;
    }

    Καλείται η πρώτη φορά format(), το πραγματικό όρισμα επεκτείνεται στον τύπο int επειδή το char χρησιμοποιείται για να αναπαραστήσει τον τύπο e1 και επομένως καλείται η υπερφορτωμένη συνάρτηση format(int). Στη δεύτερη κλήση, ο τύπος του πραγματικού ορίσματος e2 είναι unsigned int και το όρισμα επεκτείνεται σε unsigned int, γεγονός που προκαλεί την κλήση της συνάρτησης υπερφορτωμένης μορφής (unsigned int). Επομένως, θα πρέπει να γνωρίζετε ότι η συμπεριφορά δύο αριθμών σε σχέση με τη διαδικασία επίλυσης υπερφόρτωσης μπορεί να είναι διαφορετική και να εξαρτάται από τις τιμές των στοιχείων που καθορίζουν τον τρόπο με τον οποίο συμβαίνει η επέκταση τύπου.

    9.3.3. Μάθετε περισσότερα σχετικά με την τυπική μετατροπή

    Υπάρχουν πέντε τύποι τυπικών μετασχηματισμών, και συγκεκριμένα:

    1. Μετατροπές ακέραιου τύπου: μετάδοση από έναν ακέραιο τύπο ή απαρίθμηση σε οποιονδήποτε άλλο ακέραιο τύπο (εξαιρουμένων των μετασχηματισμών, οι οποίοι κατηγοριοποιήθηκαν παραπάνω ως επεκτάσεις τύπου).
    2. Μετατροπές τύπου κινητής υποδιαστολής: χύτευση από οποιονδήποτε τύπο κινητής υποδιαστολής σε οποιονδήποτε άλλο τύπο κινητής υποδιαστολής (εξαιρουμένων των μετασχηματισμών, οι οποίοι κατηγοριοποιήθηκαν παραπάνω ως επεκτάσεις τύπου).
    3. μετατροπές μεταξύ ενός ακέραιου τύπου και ενός τύπου κινητής υποδιαστολής: χύτευση από οποιονδήποτε τύπο κινητής υποδιαστολής σε οποιονδήποτε ακέραιο τύπο ή αντίστροφα.
    4. μετατροπές δείκτη: μετατροπή της ακέραιας τιμής 0 σε έναν τύπο δείκτη ή μετατροπή ενός δείκτη οποιουδήποτε τύπου σε τύπο κενού*.
    5. μετατροπές σε τύπο bool: μετάδοση από οποιονδήποτε ακέραιο τύπο, τύπο κινητής υποδιαστολής, απαριθμημένο τύπο ή τύπο δείκτη σε τύπο bool.

    Ορίστε μερικά παραδείγματα:

    extern void print(void*); εξωτερική κενή εκτύπωση (διπλή) int main() (
    int i?
    print(i); // ταιριάζει με την εκτύπωση (διπλό);
    // Το i υφίσταται την τυπική μετατροπή από int σε διπλό
    print(&i); // ταιριάζει με την εκτύπωση(void*);
    // &i υποβάλλεται σε τυπική μετατροπή
    // από int* σε void*
    επιστροφή 0;
    }

    Οι μετατροπές που ανήκουν στις ομάδες 1, 2 και 3 είναι δυνητικά επικίνδυνες επειδή ο τύπος στόχου μπορεί να μην αντιπροσωπεύει όλες τις τιμές του τύπου πηγής. Για παράδειγμα, οι floats δεν μπορούν να αντιπροσωπεύουν επαρκώς όλες τις τιμές int. Για αυτόν τον λόγο οι μετασχηματισμοί που περιλαμβάνονται σε αυτές τις ομάδες κατηγοριοποιούνται ως τυπικοί μετασχηματισμοί και όχι ως επεκτάσεις τύπου.

    int i? voidcalc(float); int main() ( calc(i); // η τυπική μετατροπή μεταξύ ενός ακέραιου τύπου και ενός // τύπου κινητής υποδιαστολής είναι δυνητικά επικίνδυνη ανάλογα με // με την τιμή του i επιστρέφει 0; )

    Όταν καλείται η συνάρτηση calc(), εφαρμόζεται η τυπική μετατροπή από τον ακέραιο τύπο int στον τύπο κινητής υποδιαστολής. Ανάλογα με την τιμή της μεταβλητής i, ενδέχεται να μην είναι δυνατή η αποθήκευση της ως float χωρίς απώλεια ακρίβειας.
    Υποτίθεται ότι όλες οι τυπικές αλλαγές απαιτούν την ίδια ποσότητα εργασίας. Για παράδειγμα, η μετατροπή από char σε ανυπόγραφο δεν έχει μεγαλύτερη προτεραιότητα από τη μετατροπή από char σε διπλό. Η εγγύτητα των τύπων δεν λαμβάνεται υπόψη. Εάν δύο καθιερωμένες συναρτήσεις απαιτούν έναν τυπικό μετασχηματισμό του πραγματικού ορίσματος για να ταιριάζει, τότε η κλήση θεωρείται ασαφής και επισημαίνεται ως σφάλμα από τον μεταγλωττιστή. Για παράδειγμα, δίνονται δύο υπερφορτωμένες συναρτήσεις:

    εξωτερικός χειρισμός κενού(μακρύ); εξωτερικός χειρισμός κενού(float);

    τότε η ακόλουθη κλήση είναι διφορούμενη:

    int main() ( manip(3.14); // error: ambiguity // manip(float) δεν είναι καλύτερο από το manip(int) return 0; )

    Η σταθερά 3,14 είναι τύπου διπλή. Με τη βοήθεια ενός ή του άλλου τυπικού μετασχηματισμού, μπορεί να δημιουργηθεί μια αντιστοιχία με οποιαδήποτε από τις υπερφορτωμένες συναρτήσεις. Δεδομένου ότι υπάρχουν δύο μετασχηματισμοί που οδηγούν στον στόχο, η κλήση θεωρείται διφορούμενη. Κανένας μετασχηματισμός δεν έχει προτεραιότητα έναντι του άλλου. Ο προγραμματιστής μπορεί να επιλύσει την ασάφεια είτε με χύτευση ρητού τύπου:

    Manip(static_cast (3.14)); // χειραγώγηση (μακρύ)

    ή χρησιμοποιώντας ένα επίθημα που δηλώνει ότι η σταθερά είναι τύπου float:

    Manip (3.14F)); // χειραγώγηση (float)

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

    extern void farith(unsigned int); extern void farith(float); int main() (
    // καθεμία από τις ακόλουθες κλήσεις είναι διφορούμενη
    farith ("a"); // το όρισμα είναι τύπου char
    farith(0); // το όρισμα είναι τύπου int
    farith(2ul); // το όρισμα είναι τύπου unsigned long
    farith(3.14159); // το όρισμα είναι τύπου double
    farith (αληθής); Το όρισμα // είναι τύπου bool
    }

    Οι τυπικές μετατροπές δεικτών είναι μερικές φορές αντιφατικές. Συγκεκριμένα, η τιμή 0 μεταδίδεται σε δείκτη οποιουδήποτε τύπου. ο δείκτης που προκύπτει ονομάζεται μηδενικός. Η τιμή 0 μπορεί να αναπαρασταθεί ως μια σταθερή έκφραση ενός ακέραιου τύπου:

    void set(int*); int main() (
    // Η μετατροπή δείκτη από 0 σε int* εφαρμόζεται σε ορίσματα
    // και στις δύο κλήσεις
    set(0L);
    set(0x00);
    επιστροφή 0;
    }

    Η σταθερή έκφραση 0L (τιμή 0 τύπου long int) και η σταθερή έκφραση 0x00 (δεκαεξαδική ακέραια τιμή 0) είναι ακέραιου τύπου και επομένως μπορούν να μετατραπούν σε μηδενικό δείκτη τύπου int*.
    Αλλά επειδή τα enums δεν είναι ακέραιοι τύποι, το στοιχείο ίσο με 0 δεν μεταφέρεται σε έναν τύπο δείκτη:

    Αριθμός EN ( zr = 0 ); set(zr); // σφάλμα: το zr δεν μπορεί να μετατραπεί σε τύπο int*

    Η κλήση της συνάρτησης set() είναι σφάλμα επειδή δεν υπάρχει μετατροπή μεταξύ της τιμής zr του στοιχείου απαρίθμησης και της τυπικής παραμέτρου τύπου int*, παρόλο που το zr είναι 0.
    Σημειώστε ότι η σταθερά έκφραση 0 είναι τύπου int. Για να μεταδοθεί σε έναν τύπο δείκτη, απαιτείται μια τυπική μετατροπή. Εάν υπάρχει μια συνάρτηση με επίσημη παράμετρο τύπου int στο σύνολο των υπερφορτωμένων συναρτήσεων, τότε η υπερφόρτωση θα επιτρέπεται υπέρ της στην περίπτωση που το πραγματικό όρισμα είναι 0:

    Voidprint(int); voidprint(void*); void set(const char*);
    void set(char *); int main()(
    print(0); // ονομάζεται print(int);
    set(0); // ασάφεια
    επιστροφή 0;
    }

    Μια κλήση για εκτύπωση (int) είναι ακριβής αντιστοίχιση, ενώ μια κλήση για εκτύπωση (void*) απαιτεί τη μετάδοση της τιμής 0 σε έναν τύπο δείκτη. Δεδομένου ότι μια αντιστοίχιση είναι καλύτερη από μια μετατροπή, για να επιλύσετε αυτήν την κλήση, επιλέγετε λειτουργία εκτύπωσης(int). Η κλήση στη set() είναι διφορούμενη, καθώς το 0 ταιριάζει με τις τυπικές παραμέτρους και των δύο υπερφορτωμένων συναρτήσεων εφαρμόζοντας τον τυπικό μετασχηματισμό. Δεδομένου ότι και οι δύο λειτουργίες είναι εξίσου καλές, διορθώνεται μια ασάφεια.
    Η τελευταία δυνατή μετατροπή δείκτη σάς επιτρέπει να μεταφέρετε έναν δείκτη οποιουδήποτε τύπου στον τύπο void*, καθώς το void* είναι ένας γενικός δείκτης σε οποιονδήποτε τύπο δεδομένων. Ορίστε μερικά παραδείγματα:

    #περιλαμβάνω extern void reset(void *); void func(int *pi, string *ps) (
    // ...
    επαναφορά (pi); // μετατροπή δείκτη: int* σε void*
    /// ...
    επαναφορά (ps); // μετατροπή δείκτη: string* σε void*
    }

    Μόνο οι δείκτες σε τύπους δεδομένων μπορούν να μεταδοθούν σε κενό* χρησιμοποιώντας την τυπική μετατροπή, δεν μπορείτε να το κάνετε αυτό με δείκτες συνάρτησης:

    Typedef int(*PFV)(); Εξωτερικές περιπτώσεις δοκιμών PFV; // πίνακας δεικτών σε συναρτήσεις extern void reset(void *); int main() (
    // ...
    επαναφορά (περιπτώσεις κειμένου); // σφάλμα: δεν υπάρχει τυπική μετατροπή
    // μεταξύ int(*)() και void*
    επιστροφή 0;
    }

    9.3.4. Συνδέσεις

    Το πραγματικό όρισμα ή η επίσημη παράμετρος μιας συνάρτησης μπορεί να είναι αναφορές. Πώς επηρεάζει αυτό τους κανόνες μετατροπής τύπου;
    Σκεφτείτε τι συμβαίνει όταν η αναφορά είναι ένα πραγματικό επιχείρημα. Ο τύπος του δεν είναι ποτέ τύπος αναφοράς. Το όρισμα αναφοράς αντιμετωπίζεται ως μια τιμή l της οποίας ο τύπος είναι ίδιος με τον τύπο του αντίστοιχου αντικειμένου:

    int i? int& ri = i; voidprint(int); int main() (
    print(i); Το όρισμα // είναι μια τιμή l του τύπου int
    print(ri); // ίδιο
    επιστροφή 0;
    }

    Το πραγματικό όρισμα και στις δύο κλήσεις είναι τύπου int. Η χρήση μιας αναφοράς για τη μεταβίβασή της στη δεύτερη κλήση δεν επηρεάζει τον τύπο του ίδιου του ορίσματος.
    Οι μετατροπές και οι επεκτάσεις τυπικού τύπου που λαμβάνονται υπόψη από τον μεταγλωττιστή είναι οι ίδιες όταν το πραγματικό όρισμα είναι αναφορά στον τύπο Τ και όταν είναι το ίδιο αυτού του τύπου. Για παράδειγμα:

    int i? int& ri = i; voidcalc(διπλό); int main() (
    calc(i); // Τυπική μετατροπή μεταξύ ακέραιου τύπου
    // και τύπος κινητής υποδιαστολής
    calc(ri); // ίδιο
    επιστροφή 0;
    }

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

    • το πραγματικό όρισμα είναι κατάλληλο ως αρχικοποιητής παραμέτρων αναφοράς. Σε αυτήν την περίπτωση, λέμε ότι υπάρχει μια ακριβής αντιστοίχιση μεταξύ τους: void swap(int &, int &); void manip(int i1, int i2) (
      // ...
      swap(i1, i2); // σωστό: ανταλλαγή κλήσεων (int &, int &)
      // ...
      επιστροφή 0;
      }
    • το πραγματικό όρισμα δεν μπορεί να αρχικοποιήσει την παράμετρο αναφοράς. Σε μια τέτοια περίπτωση, δεν υπάρχει ακριβής αντιστοίχιση και το όρισμα δεν μπορεί να χρησιμοποιηθεί για την κλήση της συνάρτησης. Για παράδειγμα:
    • int obj; void fred(double &); int main() ( frd(obj); // σφάλμα: η παράμετρος πρέπει να είναι τύπου const double & return 0; )
    • Η κλήση της συνάρτησης frd() είναι σφάλμα. Το πραγματικό όρισμα είναι τύπου int και πρέπει να μετατραπεί σε τύπου double για να ταιριάζει με την επίσημη παράμετρο αναφοράς. Το αποτέλεσμα αυτού του μετασχηματισμού είναι μια προσωρινή μεταβλητή. Εφόσον μια αναφορά δεν έχει προσδιοριστή const, τέτοιες μεταβλητές δεν μπορούν να χρησιμοποιηθούν για την αρχικοποίησή της.
      Ακολουθεί ένα άλλο παράδειγμα όπου δεν υπάρχει αντιστοίχιση μεταξύ της τυπικής παραμέτρου αναφοράς και του πραγματικού ορίσματος:
    • κατηγορία Β; void takeB(B&); BgiveB(); int main() (
      takeB(giveB()); // σφάλμα: η παράμετρος πρέπει να είναι τύπου const B &
      επιστροφή 0;
      }
    • Η κλήση της συνάρτησης takeB() είναι σφάλμα. Το πραγματικό όρισμα είναι η τιμή επιστροφής, δηλ. μια προσωρινή μεταβλητή που δεν μπορεί να χρησιμοποιηθεί για την προετοιμασία μιας αναφοράς χωρίς τον προσδιοριστή const.
      Και στις δύο περιπτώσεις, βλέπουμε ότι εάν η τυπική παράμετρος αναφοράς έχει τον προσδιοριστή const, τότε μπορεί να καθοριστεί μια ακριβής αντιστοίχιση μεταξύ αυτής και του πραγματικού ορίσματος.

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

    Voidprint(int); voidprint(int&); intiobj;
    int &ri = iobj; int main() (
    print(iobj); // σφάλμα: αμφισημία
    print(ri); // σφάλμα: αμφισημία
    print(86); // correct: call print(int)
    επιστροφή 0;
    }

    Το αντικείμενο iobj είναι ένα όρισμα που μπορεί να αντιστοιχιστεί και στις δύο συναρτήσεις print(), δηλαδή η κλήση είναι διφορούμενη. Το ίδιο ισχύει και για την επόμενη γραμμή, όπου η αναφορά ri υποδηλώνει το αντικείμενο που αντιστοιχεί και στις δύο συναρτήσεις print(). Με το τρίτο κάλεσμα όμως όλα είναι εντάξει. Για αυτόν, το print(int&) δεν είναι καθιερωμένο. Μια ακέραια σταθερά είναι μια τιμή r, επομένως δεν μπορεί να προετοιμάσει μια παράμετρο αναφοράς. Η μόνη καθιερωμένη συνάρτηση για την κλήση της print(86) είναι η print(int), γι' αυτό επιλέγεται σε ανάλυση υπερφόρτωσης.
    Εν ολίγοις, εάν το επίσημο όρισμα είναι μια αναφορά, τότε το πραγματικό όρισμα είναι μια ακριβής αντιστοίχιση εάν μπορεί να αρχικοποιήσει την αναφορά, και όχι διαφορετικά.

    Άσκηση 9.6

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

    Άσκηση 9.7

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

    (α) void print(int *, int); int arr? print(arr, 6); // κλήση συνάρτησης (β) void manip(int, int); manip("a", "z"); // κλήση συνάρτησης (c) int calc(int, int); διπλό dobj? double = calc(55.4, dobj) // κλήση συνάρτησης (d) void set(const int *); int*pi; set(pi); // κλήση συνάρτησης

    Άσκηση 9.8

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

    (α) enum Stat (Αποτυχία, Pass ); void test(Stat); text(0); // κλήση συνάρτησης (β) void reset(void *); επαναφορά (0); // κλήση συνάρτησης (c) void set(void *); int*pi; set(pi); // κλήση συνάρτησης (δ) #include λίστα ΛΥΡΙΚΗ ΣΚΗΝΗ(); void print(oper()); // κλήση συνάρτησης (e) void print(const int); intiobj; print(iobj); // κλήση συνάρτησης

    9.4. Λεπτομέρειες ανάλυσης υπερφόρτωσης λειτουργίας

    Αναφέραμε ήδη στην Ενότητα 9.2 ότι η διαδικασία επίλυσης υπερφόρτωσης συναρτήσεων αποτελείται από τρία βήματα:

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

    Τώρα είμαστε έτοιμοι να εξερευνήσουμε αυτά τα βήματα με περισσότερες λεπτομέρειες.

    9.4.1. Λειτουργίες υποψηφίου

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

    • η δήλωση συνάρτησης είναι ορατή στο σημείο της κλήσης. Στο παρακάτω παράδειγμα
      void f(); void f(int); void f(double, double = 3,4); void f(char*, char*); int main() (
      f(5.6); // Υπάρχουν τέσσερις υποψήφιοι για την επίλυση αυτής της κλήσης
      επιστροφή 0;
      }
    • Και οι τέσσερις συναρτήσεις f() ικανοποιούν αυτήν την συνθήκη. Επομένως, το σύνολο των υποψηφίων περιέχει τέσσερα στοιχεία.
    • Εάν ο τύπος του πραγματικού ορίσματος δηλώνεται μέσα σε κάποιο χώρο ονομάτων, τότε συναρτήσεις μέλους αυτού του χώρου που έχουν το ίδιο όνομα με την καλούμενη συνάρτηση προστίθενται στο υποψήφιο σύνολο: namespace NS ( class C ( /* ... */ ); void takeC( C&); ) // type cobj είναι μια κλάση C που δηλώνεται στον χώρο ονομάτων NS
      NS::Cobj; int main() (
      // καμία από τις συναρτήσεις takeC() δεν είναι ορατή στο σημείο κλήσης
      takeC(cobj); // correct: NS::takeC(C&) ονομάζεται,
      // επειδή το όρισμα είναι τύπου NS::C, οπότε
      // λαμβάνεται υπόψη η συνάρτηση takeC(),
      // δηλώθηκε στον χώρο ονομάτων NS
      επιστροφή 0;
      }

    Έτσι, το σύνολο των υποψηφίων είναι το σωματείο σύνολο λειτουργιών, ορατό στο σημείο κλήσης και ένα σύνολο συναρτήσεων που δηλώνονται στον ίδιο χώρο ονομάτων με τους πραγματικούς τύπους ορισμάτων.
    Κατά τον προσδιορισμό του συνόλου των υπερφορτωμένων λειτουργιών ορατών στο σημείο κλήσης, ισχύουν οι κανόνες που έχουν ήδη συζητηθεί νωρίτερα.
    Μια συνάρτηση που δηλώνεται σε ένα ένθετο εύρος κρύβει αντί να υπερφορτώνει τη συνάρτηση με το ίδιο όνομα στο εξωτερικό εύρος. Σε μια τέτοια κατάσταση, μόνο συναρτήσεις εντός του ένθετου πεδίου θα είναι υποψήφιες, δηλ. αυτά που δεν κρύβονται όταν καλούνται. Στο παρακάτω παράδειγμα, οι υποψήφιες συναρτήσεις που είναι ορατές στο σημείο κλήσης είναι format(double) και format(char*):

    Μορφή Char*(int); void g() ( char *format(double); char* format(char*); format(3); // calling format(double)
    }

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

    Χώρος ονομάτων libs_R_us ( int max(int, int); double max(double, double); ) char max(char, char); void func()
    {
    // Οι συναρτήσεις από τον χώρο ονομάτων είναι αόρατες
    // και οι τρεις κλήσεις επιλύονται υπέρ της καθολικής συνάρτησης max(char, char)
    max(87, 65);
    max(35,5, 76,6);
    max ("J", "L");
    }

    Οι συναρτήσεις max() που ορίζονται στον χώρο ονομάτων libs_R_us είναι αόρατες στο σημείο κλήσης. Η μόνη ορατή είναι η συνάρτηση max() από το καθολικό εύρος. μόνο περιλαμβάνεται στο σύνολο των υποψήφιων συναρτήσεων και καλείται σε καθεμία από τις τρεις κλήσεις στη λειτουργία (). Μπορούμε να χρησιμοποιήσουμε τη δήλωση χρήσης για να εκθέσουμε τις συναρτήσεις max() από τον χώρο ονομάτων libs_R_us. Πού να τοποθετήσετε τη δήλωση χρήσης; Εάν το συμπεριλάβετε στην παγκόσμια εμβέλεια:

    Char max(char, char); χρησιμοποιώντας libs_R_us::max; // using-declaration

    τότε οι συναρτήσεις max() από το libs_R_us προστίθενται στο σύνολο των υπερφορτωμένων συναρτήσεων που περιέχει ήδη το max() που έχει δηλωθεί στο καθολικό εύρος. Τώρα και οι τρεις συναρτήσεις είναι ορατές μέσα στο func() και γίνονται υποψήφιοι. Σε αυτήν την περίπτωση, οι κλήσεις func() επιλύονται ως εξής:

    Void func() ( max(87, 65); // ονομάζεται libs_R_us::max(int, int) max("J", "L"); // ονομάζεται::max(char, char) )

    Τι συμβαίνει όμως εάν εισάγουμε μια δήλωση χρήσης στο τοπικό πεδίο εφαρμογής της συνάρτησης func(), όπως φαίνεται σε αυτό το παράδειγμα;

    void func() ( // using-declaration using libs_R_us::max; // κλήσεις ίδιας συνάρτησης όπως παραπάνω
    }

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

    Libs_R_us::max(int, int); libs_R_us::max(double, double);

    Αυτοί είναι οι υποψήφιοι. Τώρα οι κλήσεις func() επιλύονται ως εξής:

    Void func() ( // using-declaration // η καθολική συνάρτηση max(char, char) είναι κρυμμένη χρησιμοποιώντας libs_R_us::max; max(87, 65); // libs_R_us::max(int, int) καλείται
    max(35,5, 76,6); // libs_R_us::max(double, double) καλείται
    max ("J", "L"); // call libs_R_us::max(int, int)
    }

    Οι οδηγίες χρήσης επηρεάζουν επίσης τη σύνθεση του συνόλου των υποψήφιων συναρτήσεων. Ας υποθέσουμε ότι αποφασίσαμε να τις χρησιμοποιήσουμε για να κάνουμε τις συναρτήσεις max() από τον χώρο ονομάτων libs_R_us ορατές στο func(). Αν τοποθετήσουμε την ακόλουθη οδηγία χρήσης στο καθολικό εύρος, τότε το σύνολο των υποψήφιων συναρτήσεων θα αποτελείται από την καθολική συνάρτηση max(char, char) και τις συναρτήσεις max(int, int) και max(double, double) που δηλώνονται στο libs_R_us:

    Χώρος ονομάτων libs_R_us ( int max(int, int); double max(double, double); ) char max(char, char);
    χρησιμοποιώντας το χώρο ονομάτων libs_R_us; // χρησιμοποιώντας την οδηγία void func()
    {
    max(87, 65); // call libs_R_us::max(int, int)
    max(35,5, 76,6); // libs_R_us::max(double, double) καλείται
    }

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

    Void func() ( // using-directive using namespace libs_R_us; // καλεί την ίδια συνάρτηση όπως παραπάνω
    }

    Ποια από τις συναρτήσεις max() θα είναι μεταξύ των υποψηφίων; Θυμηθείτε ότι μια οδηγία χρήσης κάνει τα μέλη του χώρου ονομάτων ορατά σαν να είχαν δηλωθεί εκτός αυτού του χώρου, στο σημείο όπου τοποθετείται μια τέτοια οδηγία. Στο παράδειγμά μας, τα μέλη του libs_R_us είναι ορατά στο τοπικό εύρος της συνάρτησης func(), σαν να είχαν δηλωθεί εκτός χώρου - στο καθολικό εύρος. Από αυτό προκύπτει ότι το σύνολο των υπερφορτωμένων συναρτήσεων που είναι ορατές μέσα στο func() είναι το ίδιο με πριν, δηλ. περιλαμβάνει

    Max(char, char); libs_R_us::max(int, int); libs_R_us::max(double, double);

    Εμφανίζεται μια οδηγία χρήσης σε τοπικό ή παγκόσμιο εύρος, η ανάλυση των κλήσεων στη συνάρτηση func() δεν επηρεάζεται:

    Void func() ( χρησιμοποιώντας χώρο ονομάτων libs_R_us; max(87, 65); // κλήση libs_R_us::max(int, int)
    max(35,5, 76,6); // libs_R_us::max(double, double) καλείται
    max ("J", "L"); // ονομάζεται::max(int, int)
    }

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

    Χώρος ονομάτων basicLib ( int print(int); double print(double); ) namespace matrixLib ( class matrix ( /* ... */); void print(const maxtrix &); ) void display() ( using basicLib::print matrixLib::μήτρα mObj;
    print(mObj); // κλήση maxtrixLib::print(const maxtrix &) print(87); // BasicLib::print(const maxtrix &) καλείται
    }

    Υποψήφιοι για print(mObj) είναι οι δηλώσεις use-declaration inside display() των συναρτήσεων basicLib::print(int) και basicLib::print(double) επειδή είναι ορατές στο σημείο κλήσης. Εφόσον το όρισμα της πραγματικής συνάρτησης είναι τύπου matrixLib::matrix, η συνάρτηση print() που δηλώνεται στον χώρο ονομάτων matrixLib θα είναι επίσης υποψήφια. Ποιες είναι οι υποψήφιες συναρτήσεις για εκτύπωση(87); Μόνο τα basicLib::print(int) και basicLib::print(double) είναι ορατά στο σημείο κλήσης. Εφόσον το όρισμα είναι τύπου int, δεν λαμβάνεται υπόψη κανένας πρόσθετος χώρος ονομάτων στην αναζήτηση άλλων υποψηφίων.

    9.4.2. Καθιερωμένες λειτουργίες

    Μια καθιερωμένη λειτουργία είναι ένας από τους υποψηφίους. Η λίστα των τυπικών παραμέτρων της έχει είτε τον ίδιο αριθμό στοιχείων με τη λίστα των πραγματικών ορισμάτων της συνάρτησης που καλείται, είτε περισσότερα. Στην τελευταία περίπτωση, για επιπλέον επιλογέςδίνονται οι προεπιλεγμένες τιμές, διαφορετικά η συνάρτηση δεν μπορεί να κληθεί με τον δεδομένο αριθμό ορισμάτων. Για να θεωρηθεί μια συνάρτηση σταθερή, πρέπει να υπάρχει μια μετατροπή από κάθε πραγματικό όρισμα στον τύπο της αντίστοιχης τυπικής παραμέτρου. (Τέτοιοι μετασχηματισμοί συζητήθηκαν στην Ενότητα 9.3.)
    Στο ακόλουθο παράδειγμα, η κλήση στο f(5.6) έχει δύο καθιερωμένες συναρτήσεις: f(int) και f(double).

    void f(); void f(int); void f(διπλό); void f(char*, char*); int main() (
    f(5.6); // 2 καθιερωμένες συναρτήσεις: f(int) και f(double)
    επιστροφή 0;
    }

    Η συνάρτηση f(int) επιβίωσε επειδή έχει μόνο μία επίσημη παράμετρο, η οποία αντιστοιχεί στον αριθμό των πραγματικών ορισμάτων στην κλήση. Επιπλέον, υπάρχει μια τυπική μετατροπή ενός ορίσματος τύπου double σε int. Η συνάρτηση f(double) επιβίωσε επίσης. έχει επίσης μία παράμετρο τύπου double, και ταιριάζει ακριβώς με το πραγματικό όρισμα. Οι υποψήφιες συναρτήσεις f() και f(char*, char*) εξαιρούνται από τη λίστα των σωζόμενων συναρτήσεων επειδή δεν μπορούν να κληθούν με ένα μόνο όρισμα.
    Στο παρακάτω παράδειγμα, η μόνη καθιερωμένη συνάρτηση για την κλήση της μορφής (3) είναι η μορφή (διπλή). Παρόλο που η υποψήφια μορφή(char*) μπορεί να κληθεί με ένα μόνο όρισμα, δεν υπάρχει μετατροπή από τον τύπο του πραγματικού ορίσματος int στον τύπο της επίσημης παραμέτρου char* και επομένως η συνάρτηση δεν μπορεί να θεωρηθεί καλά εδραιωμένη.

    Μορφή Char*(int); void g() ( // η συνολική μορφή συνάρτησης(int) είναι κρυφή μορφή char* (διπλή), μορφή char*(char*); μορφή(3); // υπάρχει μόνο μία καθιερωμένη συνάρτηση: format(double) )

    Στο παρακάτω παράδειγμα, και οι τρεις υποψήφιες συναρτήσεις καταλήγουν να μπορούν να καλέσουν τη max() μέσα στη func(). Όλα αυτά μπορούν να κληθούν με δύο ορίσματα. Δεδομένου ότι τα πραγματικά ορίσματα είναι τύπου int, αντιστοιχούν ακριβώς στις τυπικές παραμέτρους της συνάρτησης libs_R_us::max(int, int) και μπορούν να μεταφερθούν στους τύπους των παραμέτρων της συνάρτησης libs_R_us::max(double, double) με τη μετατροπή ακεραίων σε floats, καθώς και σε τύπους libs_R_us::max(char, char) παραμέτρων συνάρτησης μέσω μετατροπής ακέραιου τύπου.


    χρησιμοποιώντας libs_R_us::max; char max(char, char);
    void func()
    {
    // και οι τρεις συναρτήσεις max() είναι καλά εδραιωμένες
    max(87, 65); // καλείται χρησιμοποιώντας libs_R_us::max(int, int)
    }

    Σημειώστε ότι μια υποψήφια συνάρτηση με πολλαπλές παραμέτρους αφαιρείται από την κατάσταση, μόλις διαπιστωθεί ότι ένα από τα πραγματικά ορίσματα δεν μπορεί να μεταφερθεί στον τύπο της αντίστοιχης επίσημης παραμέτρου, παρόλο που μια τέτοια μετατροπή υπάρχει για όλα τα άλλα ορίσματα. Στο παρακάτω παράδειγμα, η συνάρτηση min(char *, int) εξαιρείται από το σύνολο των σωζόμενων, καθώς δεν είναι δυνατή η μετατροπή του τύπου του πρώτου ορίσματος int στον τύπο της αντίστοιχης παραμέτρου char *. Και αυτό συμβαίνει παρά το γεγονός ότι το δεύτερο όρισμα ταιριάζει ακριβώς με τη δεύτερη παράμετρο.

    εξωτερικό διπλό min (διπλό, διπλό); εξωτερικό διπλό min(char*, int); void func()
    {
    // μία υποψήφια συνάρτηση min(διπλό, διπλό)
    min(87, 65); // ελάχιστη κλήση (διπλό, διπλό)
    }

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

    void print (unsigned int); voidprint(char*); void print(char); int*ip;
    κλάση SmallInt ( /* ... */ );
    SmallInt si; int main() (
    print(ip); // σφάλμα: δεν υπάρχουν καθιερωμένες συναρτήσεις: δεν βρέθηκε αντιστοιχία
    print(si); // σφάλμα: δεν υπάρχουν καθιερωμένες συναρτήσεις: δεν βρέθηκε αντιστοιχία
    επιστροφή 0;
    }

    9.4.3. Το καλύτερο καθιερωμένο χαρακτηριστικό

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

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

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

    Int arr; void putValues(const int *); int main() (
    putValues(arr); // Απαιτούνται 2 μετατροπές
    // μετατροπή πίνακα σε δείκτη + προσδιοριστή
    επιστροφή 0;
    }

    για να μεταφέρει το όρισμα arr από τον τύπο "πίνακας τριών int" στον τύπο "δείκτης σε const int", εφαρμόζεται μια ακολουθία μετατροπών:

    1. Μετατροπή πίνακα σε δείκτη, η οποία μετατρέπει έναν πίνακα τριών int σε δείκτη σε int.
    2. Μετατροπή προσδιοριστή που μετατρέπει έναν δείκτη σε int σε δείκτη σε const int.

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

    Μετατροπή l-value -> επέκταση τύπου ή τυπική μετατροπή -> μετατροπή προσδιοριστή

    Ο όρος μετατροπή τιμής l αναφέρεται στους τρεις πρώτους μετασχηματισμούς στην κατηγορία ακριβούς αντιστοίχισης που συζητείται στην Ενότητα 9.2: μετατροπή τιμής l σε τιμή r, μετατροπή πίνακα σε δείκτη και μετατροπή συνάρτησης σε δείκτη. Η ακολουθία μετασχηματισμών αποτελείται από μηδενική ή μία μετατροπή τιμής l, ακολουθούμενη από μηδενική ή μία επέκταση τύπου ή τυπική μετατροπή και, τέλος, μηδενική ή μία μετατροπή προσδιοριστή. Μόνο ένας μετασχηματισμός κάθε είδους μπορεί να εφαρμοστεί για να δοθεί ένα πραγματικό όρισμα στον τύπο μιας τυπικής παραμέτρου.

    Η περιγραφόμενη ακολουθία ονομάζεται ακολουθία τυπικών μετασχηματισμών. Υπάρχει επίσης μια ακολουθία μετατροπών που ορίζονται από το χρήστη που σχετίζεται με μια συνάρτηση μετατροπέα μέλους. (Οι μετατροπείς και οι ακολουθίες μετατροπών που καθορίζονται από το χρήστη συζητούνται στο Κεφάλαιο 15.)

    Ποιες είναι οι ακολουθίες στις οποίες αλλάζουν τα πραγματικά ορίσματα στο παρακάτω παράδειγμα;

    Χώρος ονομάτων libs_R_us ( int max(int, int); double max(double, double); ) // using-declaration
    χρησιμοποιώντας libs_R_us::max; void func()
    {
    char c1, c2;
    max(c1, c2); // call libs_R_us::max(int, int)
    }

    Τα ορίσματα για την κλήση της συνάρτησης max() είναι τύπου char. Η ακολουθία μετατροπών ορισμάτων κατά την κλήση της συνάρτησης libs_R_us::max(int,int) είναι η εξής:

    1α. Εφόσον τα ορίσματα μεταβιβάζονται με τιμή, η μετατροπή της τιμής l σε τιμή r εξάγει τις τιμές των ορισμάτων c1 και c2.

    2α. Τα ορίσματα μετατρέπονται από char σε int χρησιμοποιώντας την επέκταση τύπου.
    Η ακολουθία μετασχηματισμών ορισμάτων κατά την κλήση της συνάρτησης libs_R_us::max(double,double) είναι η εξής:
    1β. Μετατρέποντας την τιμή l σε τιμή r, εξάγονται οι τιμές των ορισμάτων c1 και c2.

    2β. Η τυπική μετατροπή μεταξύ ακέραιων και τύπων float εκπέμπει ορίσματα από τον τύπο char σε τύπο double.

    Η κατάταξη της πρώτης ακολουθίας είναι μια επέκταση τύπου (η χειρότερη αλλαγή που εφαρμόστηκε), ενώ η κατάταξη της δεύτερης είναι μια τυπική μετατροπή. Δεδομένου ότι η επέκταση τύπου είναι καλύτερη από τη μετατροπή τύπου, η συνάρτηση libs_R_us::max(int,int) επιλέγεται ως η καλύτερη εφαρμογή για αυτήν την κλήση.
    Εάν οι ακολουθίες κατάταξης μετασχηματισμών ορισμάτων δεν μπορούν να αποκαλύψουν μια μεμονωμένη καλά εδραιωμένη συνάρτηση, τότε η κλήση θεωρείται ασαφής. Σε αυτό το παράδειγμα, και οι δύο κλήσεις στο calc() απαιτούν την ακόλουθη ακολουθία:

    1. Μετατρέψτε το l-value σε r-value για να εξαγάγετε τιμές ορίσματος i και j.
    2. Μια τυπική μετατροπή για τη χύτευση των πραγματικών ορισμάτων στις αντίστοιχες επίσημες παραμέτρους.

    Εφόσον κανείς δεν μπορεί να πει ποια από αυτές τις ακολουθίες είναι καλύτερη από την άλλη, η κλήση είναι διφορούμενη:

    Int i, j; εξωτερικό μακρύ calc(μακρύ, μακρύ); εξωτερικός διπλός υπολογισμός (διπλός, διπλός) void jj() ( // σφάλμα: ασάφεια, καμία καλύτερη αντιστοίχιση
    calc(i, j);
    }

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

    void reset(int *); void reset(const int *); int*pi; int main() (
    επαναφορά (pi); // χωρίς μετατροπή προσδιοριστών είναι καλύτερο:
    // επιλέξτε επαναφορά (int *)
    επιστροφή 0;
    }

    Η ακολουθία τυπικών μετατροπών που εφαρμόζεται στο πραγματικό όρισμα για την πρώτη υποψήφια επαναφορά συνάρτησης (int*) είναι ακριβής αντιστοίχιση, χρειάζεται μόνο μετάβαση από την τιμή l στην τιμή r για να εξαχθεί η τιμή του ορίσματος. Για τη δεύτερη υποψήφια επαναφορά (const int *), εφαρμόζεται επίσης ο μετασχηματισμός τιμής l σε τιμή r, αλλά ακολουθείται επίσης από τη μετατροπή του προσδιοριστή για να μεταδοθεί η προκύπτουσα τιμή από δείκτη σε int σε δείκτη σε const int. Και οι δύο σεκάνς ταιριάζουν ακριβώς, αλλά δεν υπάρχει αμφισημία. Δεδομένου ότι η δεύτερη ακολουθία διαφέρει από την πρώτη από την παρουσία ενός μετασχηματισμού προσδιοριστή στο τέλος, η ακολουθία χωρίς τέτοιο μετασχηματισμό θεωρείται η καλύτερη. Επομένως, η επαναφορά (int*) είναι η καλύτερη όρθια συνάρτηση.
    Ακολουθεί ένα άλλο παράδειγμα όπου οι προσδιοριστές casting επηρεάζουν τη σειρά που επιλέγεται:

    int extract(void*);
    int extract(const void *);

    int main() (
    εκχύλισμα (pi); // επιλογή εξαγωγής (κενό *)
    επιστροφή 0;
    }

    Υπάρχουν δύο καθιερωμένες συναρτήσεις για κλήση εδώ: extract(void*) και extract(const void*). Η ακολουθία μετασχηματισμών για τη συνάρτηση απόσπασμα(κενό*) συνίσταται στον μετασχηματισμό της τιμής l σε μια τιμή r για εξαγωγή τιμές επιχειρήματος, ακολουθούμενη από την τυπική μετατροπή δείκτη: από δείκτη σε int σε δείκτη σε κενό. Για τη συνάρτηση εξαγωγής (const void*), αυτή η ακολουθία διαφέρει από την πρώτη με μια πρόσθετη μετατροπή προσδιοριστών για τη μετάδοση του τύπου αποτελέσματος από δείκτη σε κενό σε δείκτη σε κενό. Δεδομένου ότι οι ακολουθίες διαφέρουν μόνο από αυτόν τον μετασχηματισμό, η πρώτη επιλέγεται ως καταλληλότερη και, επομένως, η συνάρτηση εξαγωγής (const void*) θα είναι η καλύτερη στάσιμη.
    Οι προσδιοριστές const και volatile επηρεάζουν επίσης την κατάταξη της αρχικοποίησης των παραμέτρων αναφοράς. Εάν δύο τέτοιες αρχικοποιήσεις διαφέρουν μόνο ως προς την προσθήκη ενός προσδιοριστή const και volatile, τότε η προετοιμασία χωρίς τον επιπλέον προσδιοριστή θεωρείται καλύτερη σε ανάλυση υπερφόρτωσης:

    #περιλαμβάνω χειραγώγηση κενού (διάνυσμα &); void manip(διάνυσμα const &); διάνυσμα φά();
    εξωτερικό διάνυσμα vec; int main() (
    manip(vec); // επιλογή χειραγώγησης (διάνυσμα &)
    manip(f()); // επιλογή χειραγώγησης (διάνυσμα συν &)
    επιστροφή 0;
    }

    Στην πρώτη κλήση, η προετοιμασία αναφοράς για οποιαδήποτε κλήση συνάρτησης είναι ακριβής αντιστοίχιση. Αλλά αυτή η πρόκληση δεν θα είναι ακόμα διφορούμενη. Δεδομένου ότι και οι δύο αρχικοποιήσεις είναι ίδιες από κάθε άποψη, εκτός από την παρουσία μιας πρόσθετης προδιαγραφής const στη δεύτερη περίπτωση, η αρχικοποίηση χωρίς τέτοια προδιαγραφή θεωρείται καλύτερη, επομένως η υπερφόρτωση θα επιλυθεί υπέρ του καθιερωμένου χειρισμού (διάνυσμα &).
    Για τη δεύτερη κλήση, υπάρχει μόνο ένας καλά εδραιωμένος χειρισμός συνάρτησης (διάνυσμα const &). Επειδή το πραγματικό όρισμα είναι μια προσωρινή μεταβλητή που περιέχει το αποτέλεσμα που επιστρέφεται από την f(), ένα τέτοιο όρισμα είναι μια τιμή r που δεν μπορεί να χρησιμοποιηθεί για την προετοιμασία της μη-const επίσημης παραμέτρου αναφοράς του manip(vector &). Επομένως, το μόνο καθιερωμένο manip(const vector &).
    Φυσικά, οι συναρτήσεις μπορούν να έχουν πολλά πραγματικά ορίσματα. Η επιλογή των καλύτερων από τα μόνιμα θα πρέπει να γίνει λαμβάνοντας υπόψη την κατάταξη των ακολουθιών μετασχηματισμών όλων των ορισμάτων. Εξετάστε ένα παράδειγμα:

    extern int ff(char*, int); extern int ff(int, int); int main() (ff(0, "a"); // ff(int, int)
    επιστροφή 0;
    }

    Η συνάρτηση ff(), η οποία παίρνει δύο ορίσματα int, επιλέγεται ως η καλύτερη μόνιμη συνάρτηση για τους ακόλουθους λόγους:

    1. το πρώτο της επιχείρημα είναι καλύτερο. Το 0 ταιριάζει ακριβώς με μια επίσημη παράμετρο int, ενώ η τυπική μετατροπή δείκτη απαιτείται για να ταιριάζει με μια παράμετρο τύπου char *.
    2. το δεύτερο επιχείρημά του έχει την ίδια κατάταξη. Στο όρισμα "a" του τύπου char, για να δημιουργηθεί μια αντιστοιχία με τη δεύτερη επίσημη παράμετρο οποιασδήποτε από τις δύο συναρτήσεις, πρέπει να εφαρμοστεί μια ακολουθία μετατροπών που έχει την κατάταξη επέκτασης τύπου.

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

    int compute(const int&, short); int compute(int&, double); extern intiobj;
    int main() (
    compute(iobj, "c"); // υπολογισμός(int&, double)
    επιστροφή 0;
    }

    Και οι δύο συναρτήσεις compute(const int&, short) και compute(int&, double) επιβίωσαν. Το δεύτερο επιλέγεται ως το καλύτερο για τους εξής λόγους:

    1. το πρώτο της επιχείρημα είναι καλύτερο. Η προετοιμασία αναφοράς για την πρώτη καθιερωμένη συνάρτηση είναι χειρότερη επειδή απαιτεί την προσθήκη ενός προσδιοριστή const, ο οποίος δεν χρειάζεται για τη δεύτερη συνάρτηση.
    2. το δεύτερο επιχείρημά του έχει την ίδια κατάταξη. Στο όρισμα "c" του τύπου char, για να δημιουργηθεί μια αντιστοιχία με τη δεύτερη τυπική παράμετρο οποιασδήποτε από τις δύο συναρτήσεις, πρέπει να εφαρμοστεί μια ακολουθία μετασχηματισμών που έχουν την κατάταξη ενός τυπικού μετασχηματισμού.

    9.4.4. Ορίσματα με προεπιλεγμένες τιμές

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

    extern void ff(int); extern void ff(long, int = 0); int main() (
    ff(2L); // ταιριάζει με ff(long, 0); ff(0, 0); // ταιριάζει ff(long, int);
    ff(0); // ταιριάζει με ff(int);
    ff(3.14); // σφάλμα: αμφισημία
    }

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

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

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

    Άσκηση 9.9

    Εξηγήστε τι συμβαίνει όταν επιλύετε μια υπερφόρτωση για να καλέσετε την compute() στο main(). Ποια χαρακτηριστικά είναι οι υποψήφιοι; Ποιος από αυτούς θα σταθεί μετά το πρώτο βήμα; Ποια ακολουθία μετασχηματισμών πρέπει να εφαρμοστεί στο πραγματικό όρισμα ώστε να αντιστοιχεί στην επίσημη παράμετρο για κάθε καθιερωμένη συνάρτηση; Ποια συνάρτηση θα είναι η καλύτερη;

    Χώρος ονομάτων primerLib ( void compute(); void compute(const void *); ) χρησιμοποιώντας primerLib::compute;
    void compute(int);
    void compute(double, double = 3,4);
    void compute(char*, char* = 0); int main() (
    compute(0);
    επιστροφή 0;
    }

    Τι συμβαίνει εάν η δήλωση χρήσης τοποθετηθεί μέσα στην main() πριν καλέσετε την compute(); Απαντήστε στις ίδιες ερωτήσεις.

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

    Η υπερφόρτωση συναρτήσεων σάς επιτρέπει να χρησιμοποιείτε το ίδιο όνομα για πολλές λειτουργίες με ΔΙΑΦΟΡΕΤΙΚΟΙ ΤΥΠΟΙΠαράμετροι.

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

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

    ΠΡΩΤΗ ΕΙΣΑΓΩΓΗ ΣΤΗΝ ΥΠΕΡΦΟΡΤΩΣΗ ΛΕΙΤΟΥΡΓΙΩΝ

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

    #περιλαμβάνω

    int add_values(int a,int b)

    {
    επιστροφή(a+b);
    )

    int add_values ​​(int a, int b, int c)

    (
    επιστροφή(a+b+c);
    )

    {
    cout<< «200 + 801 = » << add_values(200, 801) << endl;
    cout<< «100 + 201 + 700 = » << add_values(100, 201, 700) << endl;
    }

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

    Ομοίως, το ακόλουθο πρόγραμμα MSG_OVR.CPP υπερφορτώνει τη συνάρτηση show_message. Η πρώτη συνάρτηση με το όνομα show_message εμφανίζει ένα τυπικό μήνυμα, δεν μεταβιβάζονται παράμετροι σε αυτήν. Το δεύτερο εξάγει το μήνυμα που του δόθηκε και το τρίτο εξάγει δύο μηνύματα:

    #περιλαμβάνω

    void show_message(void)

    {
    cout<< «Стандартное сообщение: » << «Учимся программировать на C++» << endl;
    }

    void show_message(char *message)

    {
    cout<< message << endl;
    }

    void show_message (χαρακτήρας *πρώτο, χαρακτήρας *δεύτερος)

    {
    cout<< first << endl;
    cout<< second << endl;
    }

    {
    show_message();
    show_message("Μάθετε να προγραμματίζετε σε C++!");
    show_message("Δεν υπάρχουν προκαταλήψεις στη C++!", "Η υπερφόρτωση είναι ωραία!");
    }

    ΟΤΑΝ ΕΙΝΑΙ ΑΠΑΡΑΙΤΗΤΗ Η ΥΠΕΡΦΟΡΤΩΣΗ

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

    int day_of_week (int julian_day)

    {
    // Χειριστές
    }

    int day_of_week (int month, int day, int year)

    {
    // Χειριστές
    }

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

    Η υπερφόρτωση συναρτήσεων βελτιώνει την αναγνωσιμότητα του προγράμματος

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

    ΤΙ ΠΡΕΠΕΙ ΝΑ ΓΝΩΡΙΖΕΤΕ

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

    1. Η υπερφόρτωση συναρτήσεων παρέχει πολλαπλές "προβολές" της ίδιας λειτουργίας στο πρόγραμμά σας.
    2. Για υπερφόρτωση συναρτήσεων, απλώς ορίστε πολλαπλές συναρτήσεις με το ίδιο όνομα και τύπο επιστροφής που διαφέρουν μόνο ως προς τον αριθμό και τον τύπο των παραμέτρων.
    3. Κατά τη μεταγλώττιση, η C++ θα καθορίσει ποια συνάρτηση θα καλέσει με βάση τον αριθμό και τον τύπο των παραμέτρων που έχουν περάσει.
    4. Η υπερφόρτωση συναρτήσεων απλοποιεί τον προγραμματισμό επιτρέποντας στους προγραμματιστές να εργάζονται με ένα μόνο όνομα συνάρτησης.

    Σχόλιο: Η διάλεξη συζητά τις έννοιες, τη δήλωση και τη χρήση ενσωματωμένων και υπερφορτωμένων συναρτήσεων σε προγράμματα 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); } // конец функции



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

    Υπάρχει τρόπος να επιτευχθεί υπερφόρτωση συναρτήσεων στο 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(NUMLGS_MAX_LIMIT(t) if(mingsHE_t) 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, ( foo(); foo(7); foo(10.12); foo(12.10, 7); foo((char)"s"); επιστροφή 0; )

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

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

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

    Υπερφόρτωση συναρτήσεων σε C++

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

    Παρακάτω είναι ένα παράδειγμα όπου η ίδια λειτουργία Τυπώνω()χρησιμοποιείται για την εκτύπωση διαφορετικών τύπων δεδομένων -

    #περιλαμβάνω χρησιμοποιώντας το namespace std? class printData ( public: void print(int i) ( cout<< "Printing int: " << i << endl; } void print(double f) { cout << "Printing float: " << f << endl; } void print(char* c) { cout << "Printing character: " << c << endl; } }; int main(void) { printData pd; // Call print to print integer pd.print(5); // Call print to print float pd.print(500.263); // Call print to print character pd.print("Hello C++"); return 0; }

    Ένταση εκτύπωσης: 5 Πλωτήρας εκτύπωσης: 500.263 Χαρακτήρας εκτύπωσης: Hello C++

    Υπερφόρτωση χειριστή σε C++

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

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

    Box operator+(const Box&);

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

    Box operator+(const Box&, const Box&);

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

    #περιλαμβάνω χρησιμοποιώντας το namespace std? class Box ( public: double getVolume(void) ( return μήκος * πλάτος * ύψος; ) void setLength(double len) ( length = len; ) void setBreadth(double bre) ( width = bre; ) void setHeight(double hei) ( ύψος = hei; ) // Υπερφόρτωση + τελεστής για προσθήκη δύο αντικειμένων Box. Operator Box+(const Box& b) ( Box box; box.length = this->length + b.length; box.breadth = this->breadth + b .πλάτος, κουτί. ύψος = αυτό-> ύψος + β. ύψος, κουτί επιστροφής; ) ιδιωτικό: διπλό μήκος· // Μήκος κουτιού διπλό πλάτος· // Πλάτος κουτιού διπλό ύψος· // Ύψος κουτιού ) ; // Κύρια συνάρτηση για το πρόγραμμα int main() ( Box Box1; // Declare Box1 τύπου Box Box Box2; // Declare Box2 of type Box Box Box3; // Declare Box3 of type Box double volume = 0,0; // Store ο όγκος ενός κουτιού εδώ // πλαίσιο 1 προδιαγραφή Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // πλαίσιο 2 προδιαγραφή Box2.setLength(12.0); ;Box2.setHeight(10.0 ); // τόμος πλαισίου 1 τόμος = Box1.getVolume(); cout<< "Volume of Box1: " << volume <

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

    Volume of Box1: 210 Volume of Box2: 1560 Volume of Box3: 5400

    Overloadable / Non-overloadable Operators

    Παρακάτω είναι μια λίστα τελεστών που μπορεί να υπερφορτωθούν.