Περίληψη
Αυτό το μάθημα μελετά από ποιους δρόμους μπαίνουν τα δεδομένα σε μια συνάρτηση και βγαίνουν τα αποτελέσματά της. Εξ ορισμού, η C++ μεταβιβάζει τα ορίσματα με τιμή: η παράμετρος είναι ένα αντίγραφο, και τίποτα από όσα της κάνει η συνάρτηση δεν φτάνει στο πρωτότυπο. Μια αναφορά, αντίθετα, είναι ένα δεύτερο όνομα για ένα κελί που ήδη υπάρχει, και επιτρέπει στη συνάρτηση να τροποποιήσει τη μεταβλητή εκείνου που την καλεί· η σταθερή αναφορά επιτρέπει να διαβαστεί ένα μεγάλο δεδομένο χωρίς να αντιγραφεί και χωρίς να μπορεί να αλλοιωθεί. Για να επιστραφούν περισσότερα αποτελέσματα, η βιβλιοθήκη προσφέρει τα std::pair και std::tuple, που λαμβάνονται με δομημένες δεσμεύσεις. Ακολουθούν η εμβέλεια ενός ονόματος (πού φαίνεται) και η διάρκεια ζωής μιας μεταβλητής (πότε υπάρχει), με την επισκίαση ενός ονόματος και τις τοπικές μεταβλητές static, και κλείνει η αναφορά που επιζεί της μεταβλητής της. Η έννοια που τα ενοποιεί όλα είναι η ροή των δεδομένων ανάμεσα στα μέρη ενός προγράμματος: αντιγραφή απέναντι σε κοινή χρήση, και η ορατότητα των ονομάτων ως μηχανισμός απομόνωσης.
Μαθησιακοί στόχοι
- Να προβλέπεται η επίδραση μιας συνάρτησης στα ορίσματά της ανάλογα με το αν μεταβιβάζονται με τιμή ή με αναφορά.
- Να επιλέγεται ο τρόπος μεταβίβασης κάθε παραμέτρου (τιμή για μικρούς τύπους, σταθερή αναφορά για μεγάλα δεδομένα, αναφορά για ό,τι τροποποιείται) και να αιτιολογείται η επιλογή.
- Να επιστρέφονται περισσότερα αποτελέσματα με
std::pairήstd::tupleκαι να λαμβάνονται με δομημένες δεσμεύσεις. - Να προσδιορίζονται η εμβέλεια και η διάρκεια ζωής κάθε μεταβλητής ενός προγράμματος, μαζί με τις τοπικές μεταβλητές
staticκαι εκείνες που επισκιάζονται από άλλη. - Να αναγνωρίζεται η αναφορά σε μια τοπική μεταβλητή ή σε μια προσωρινή τιμή που δεν υπάρχει πια, με την προειδοποίηση του μεταγλωττιστή που την αναγγέλλει, και να διορθώνεται.
Μεταβίβαση με τιμή
Η παράμετρος είναι αντίγραφο
Το μάθημα Συναρτήσεις έδειξε ότι κάθε κλήση δημιουργεί ένα πλαίσιο στη στοίβα, με τις παραμέτρους και τις τοπικές μεταβλητές της συνάρτησης, και ότι το πλαίσιο εξαφανίζεται κατά την επιστροφή. Μένει να διευκρινιστεί ποια σχέση έχει μια παράμετρος με το όρισμα που την αρχικοποιεί. Η εξ ορισμού απάντηση της C++ είναι η απλούστερη: η παράμετρος είναι μια νέα τοπική μεταβλητή, που αρχικοποιείται με ένα αντίγραφο της τιμής του ορίσματος. Αν αυτό ισχύει, μια συνάρτηση που τροποποιεί τις παραμέτρους της τροποποιεί τα αντίγραφά της, και οι μεταβλητές εκείνου που την καλεί δεν αλλάζουν. Το πρόγραμμα που ακολουθεί το θέτει σε δοκιμή με μια συνάρτηση που επιχειρεί να αντιμεταθέσει δύο ακεραίους.
#include <print>// Επιχειρεί να αντιμεταθέσει τα a και b· αντιμεταθέτει μόνο τα αντίγραφά τους.void swapValues(int a, int b){ const int saved{a}; a = b; b = saved; std::println("Μέσα: a = {}, b = {}", a, b);}int main(){ int x{1}; int y{2}; swapValues(x, y); std::println("Μετά: x = {}, y = {}", x, y);}Μέσα: a = 2, b = 1Μετά: x = 1, y = 2Μέσα στη συνάρτηση η αντιμετάθεση γίνεται σωστά· μετά την κλήση, η x και η y διατηρούν τις τιμές τους. Ο μεταγλωττιστής δεν προειδοποιεί, γιατί δεν υπάρχει τίποτα για να προειδοποιήσει: ο κώδικας αντιμεταθέτει δύο τοπικές μεταβλητές που εξαφανίζονται όταν τελειώνει. Το σχήμα παρακολουθεί τα πλαίσια της στοίβας κατά την κλήση.
Το πλαίσιο της main περιέχει τη x και τη y· η κλήση δημιουργεί από πάνω το πλαίσιο της swapValues, με δικά του κελιά.
Απόδειξη
Πριν από την κλήση, το πλαίσιο της
mainέχει δύο κελιά: τηxμε 1 και τηyμε 2.Η κλήση δημιουργεί το πλαίσιο της
swapValues. Οι παράμετροί τηςaκαιbείναι νέα κελιά, και σε αυτά αντιγράφονται οι τιμές των ορισμάτων: 1 και 2. Από αυτή τη στιγμή δεν μένει κανένας δεσμός ανάμεσα στηνaκαι στηx, εκτός από το ότι ξεκίνησαν με την ίδια τιμή.Η τοπική μεταβλητή
savedδημιουργείται στο ίδιο πλαίσιο και κρατά ένα αντίγραφο τηςa.Οι δύο αναθέσεις αντιμεταθέτουν την
aκαι τηb. Τα κελιά που αλλάζουν βρίσκονται όλα στο πάνω πλαίσιο· εκείνα τηςmainδεν αγγίζονται.Κατά την επιστροφή, το πλαίσιο της
swapValuesεξαφανίζεται ολόκληρο, μαζί με την αντιμετάθεση που περιείχε. Ηxκαι ηyεξακολουθούν να έχουν τιμές 1 και 2.
Τι προστατεύει το αντίγραφο
Η μεταβίβαση με τιμή δεν είναι ελάττωμα, αλλά ο κανόνας που κάνει τις συναρτήσεις λογικές. Πρώτον, το όρισμα μπορεί να είναι οποιαδήποτε έκφραση, όχι μόνο μια μεταβλητή: η swapValues(x + 1, 7) είναι έγκυρη, γιατί η παράμετρος αρχικοποιείται με μια τιμή, από όπου κι αν προέρχεται. Δεύτερον, όποιος καλεί ξέρει από τη δήλωση, χωρίς να διαβάσει το σώμα, ότι οι μεταβλητές του δεν θα αλλάξουν. Τρίτον, μέσα στη συνάρτηση η παράμετρος είναι μια ακόμη τοπική μεταβλητή, που μπορεί να χρησιμεύσει ως μεταβλητή εργασίας χωρίς συνέπειες έξω.
#include <print>// Πλήθος δεκαδικών ψηφίων του n.// Προσυνθήκη: n >= 0.// Μετασυνθήκη: επιστρέφει το πλήθος των ψηφίων του n (1 για το 0).int digitCount(int n){ int count{1}; while (n >= 10) { n = n / 10; ++count; } return count;}int main(){ const int population{48'210}; std::println("Ο αριθμός {} έχει {} ψηφία", population, digitCount(population));}Ο αριθμός 48210 έχει 5 ψηφίαΗ συνάρτηση καταστρέφει την παράμετρό της διαιρώντας την με το 10 ώσπου να έχει ένα μόνο ψηφίο· επειδή είναι αντίγραφο, η population μένει ανέπαφη και μπορεί να είναι const. Από αυτό έπεται ένας κανόνας ανάγνωσης που ισχύει για όλον τον κύκλο: αν μια παράμετρος δεν έχει το σύμβολο & που μελετάται στην παράγραφο που ακολουθεί, η συνάρτηση δεν μπορεί να τροποποιήσει τη μεταβλητή εκείνου που την καλεί, ό,τι κι αν κάνει με αυτήν.
Φόρτωση του περιεχομένου…
Ολοκληρώθηκε
Συνδεθείτε για να αποθηκεύσετε την πρόοδό σας