Γνωρίζω ότι οι αναφορές είναι συντακτική ζάχαρη, ώστε ο κώδικας να διαβάζεται και να γράφεται ευκολότερα.
Αλλά ποιες είναι οι διαφορές;
Περίληψη από τις απαντήσεις και τους συνδέσμους παρακάτω:
NULL
), ενώ μια αναφορά αναφέρεται πάντα σε ένα αντικείμενο.&obj + 5
).Για να αποσαφηνίσουμε μια παρανόηση:
Το πρότυπο της C++ είναι πολύ προσεκτικό ώστε να μην υπαγορεύει τον τρόπο με τον οποίο ένας μεταγλωττιστής μπορεί να να υλοποιεί αναφορές, αλλά κάθε μεταγλωττιστής της C++ υλοποιεί τις αναφορές ως δείκτες. Δηλαδή, μια δήλωση όπως:
int &ri = i,
αν δεν βελτιστοποιηθεί εντελώς, *διαθέτει την ίδια ποσότητα αποθηκευτικού χώρου όπως ένας δείκτης, και τοποθετεί τη διεύθυνση του "i" σε αυτόν τον αποθηκευτικό χώρο.
Έτσι, ένας δείκτης και μια αναφορά χρησιμοποιούν το ίδιο ποσό μνήμης.
Ως γενικός κανόνας,
Ενδιαφέρουσα ανάγνωση:
Ένας δείκτης μπορεί να εκχωρηθεί εκ νέου:
int x = 5,
int y = 6,
int *p,
p = &x,
p = &y,
*p = 10;
assert(x == 5),
assert(y == 10),
Μια αναφορά δεν μπορεί και πρέπει να εκχωρηθεί κατά την αρχικοποίηση:
int x = 5,
int y = 6,
int &r = x,
Ένας δείκτης έχει τη δική του διεύθυνση μνήμης και το δικό του μέγεθος στη στοίβα (4 bytes σε x86), ενώ μια αναφορά μοιράζεται την ίδια διεύθυνση μνήμης (με την αρχική μεταβλητή) αλλά καταλαμβάνει επίσης κάποιο χώρο στη στοίβα. Εφόσον μια αναφορά έχει την ίδια διεύθυνση με την ίδια την αρχική μεταβλητή, είναι ασφαλές να θεωρήσουμε μια αναφορά ως ένα άλλο όνομα για την ίδια μεταβλητή. Σημείωση: Αυτό στο οποίο δείχνει ένας δείκτης μπορεί να βρίσκεται στη στοίβα ή στο σωρό. Το ίδιο και μια αναφορά. Ο ισχυρισμός μου σε αυτή τη δήλωση δεν είναι ότι ένας δείκτης πρέπει να δείχνει στη στοίβα. Ένας δείκτης είναι απλώς μια μεταβλητή που κρατά μια διεύθυνση μνήμης. Αυτή η μεταβλητή βρίσκεται στη στοίβα. Εφόσον μια αναφορά έχει το δικό της χώρο στη στοίβα, και εφόσον η διεύθυνση είναι ίδια με τη μεταβλητή στην οποία αναφέρεται. Περισσότερα για το stack vs heap. Αυτό σημαίνει ότι υπάρχει μια πραγματική διεύθυνση μιας αναφοράς την οποία ο μεταγλωττιστής δεν θα σας πει.
int x = 0,
int &r = x,
int *p = &x,
int *p2 = &r,
assert(p == p2),
Μπορείτε να έχετε δείκτες σε δείκτες σε δείκτες που προσφέρουν επιπλέον επίπεδα κατεύθυνσης. Ενώ οι αναφορές προσφέρουν μόνο ένα επίπεδο έμμεσης κατεύθυνσης.
int x = 0,
int y = 0,
int *p = &x,
int *q = &y,
int **pp = &p,
pp = &q;//*pp = q
**pp = 4,
assert(y == 4),
assert(x == 0),
Ο δείκτης μπορεί να εκχωρηθεί άμεσα στο nullptr
, ενώ η αναφορά όχι. Αν προσπαθήσετε αρκετά και ξέρετε πώς, μπορείτε να κάνετε τη διεύθυνση μιας αναφοράς nullptr
. Ομοίως, αν προσπαθήσετε αρκετά σκληρά, μπορείτε να έχετε μια αναφορά σε έναν δείκτη, και τότε αυτή η αναφορά μπορεί να περιέχει nullptr
.
int *p = nullptr,
int &r = nullptr; <--- σφάλμα μεταγλώττισης
int &r = *p; <--- πιθανότατα δεν υπάρχει σφάλμα μεταγλώττισης, ειδικά αν ο nullptr είναι κρυμμένος πίσω από μια κλήση συνάρτησης, αλλά αναφέρεται σε έναν ανύπαρκτο int στη διεύθυνση 0
Οι δείκτες μπορούν να κάνουν επανάληψη σε έναν πίνακα, μπορείτε να χρησιμοποιήσετε το ++
για να πάτε στο επόμενο στοιχείο στο οποίο δείχνει ένας δείκτης, και το + 4
για να πάτε στο 5ο στοιχείο. Αυτό γίνεται ανεξάρτητα από το μέγεθος του αντικειμένου στο οποίο δείχνει ο δείκτης.
Ένας δείκτης πρέπει να απορριφθεί με *
για να προσπελάσετε τη θέση μνήμης στην οποία δείχνει, ενώ μια αναφορά μπορεί να χρησιμοποιηθεί απευθείας. Ένας δείκτης σε μια κλάση/δομή χρησιμοποιεί το ->
για να προσπελάσει τα μέλη της, ενώ μια αναφορά χρησιμοποιεί το .
.
Ένας δείκτης είναι μια μεταβλητή που κατέχει μια διεύθυνση μνήμης. Ανεξάρτητα από τον τρόπο υλοποίησης μιας αναφοράς, μια αναφορά έχει την ίδια διεύθυνση μνήμης με το στοιχείο στο οποίο αναφέρεται.
Οι αναφορές δεν μπορούν να γεμίσουν σε έναν πίνακα, ενώ οι δείκτες μπορούν (Μνεία από τον χρήστη @litb)
Οι αναφορές Const μπορούν να συνδεθούν με προσωρινά στοιχεία. Οι δείκτες δεν μπορούν (όχι χωρίς κάποια ανακατεύθυνση):
x = int(12); //νόμιμη C++
int *y = &int(12); //απαράδεκτη η ανάκληση μιας προσωρινής αναφοράς.
Αυτό κάνει το const&
ασφαλέστερο για χρήση σε λίστες επιχειρημάτων και ούτω καθεξής.
Πέρα από τη συντακτική ζάχαρη, μια αναφορά είναι ένας δείκτης const
(όχι δείκτης σε const
). Πρέπει να καθορίσετε σε τι αναφέρεται όταν δηλώνετε τη μεταβλητή αναφοράς, και δεν μπορείτε να το αλλάξετε αργότερα.
Ενημέρωση: τώρα που το σκέφτομαι λίγο περισσότερο, υπάρχει μια σημαντική διαφορά.
Ο στόχος ενός const δείκτη'μπορεί να αντικατασταθεί παίρνοντας τη διεύθυνσή του και χρησιμοποιώντας ένα const cast.
Ο στόχος μιας αναφοράς δεν μπορεί να αντικατασταθεί με κανέναν τρόπο εκτός από το UB.
Αυτό θα πρέπει να επιτρέπει στον μεταγλωττιστή να κάνει περισσότερη βελτιστοποίηση σε μια αναφορά.
Μια αναφορά δεν μπορεί ποτέ να είναι "NULL".