Ποιες είναι οι διαφορές μεταξύ μιας μεταβλητής δείκτη και μιας μεταβλητής αναφοράς στη C++;

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

Αλλά ποιες είναι οι διαφορές;


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

  1. Ένας δείκτης μπορεί να επαναπροσδιοριστεί οποιεσδήποτε φορές, ενώ μια αναφορά δεν μπορεί να επαναπροσδιοριστεί μετά τη δέσμευση.
  2. Οι δείκτες μπορούν να δείχνουν πουθενά (NULL), ενώ μια αναφορά αναφέρεται πάντα σε ένα αντικείμενο.
  3. Δεν μπορείτε να πάρετε τη διεύθυνση μιας αναφοράς όπως μπορείτε με τους δείκτες.
  4. Δεν υπάρχει "αριθμητική αναφορών" (αλλά μπορείτε να πάρετε τη διεύθυνση ενός αντικειμένου που δείχνει μια αναφορά και να κάνετε αριθμητική δεικτών σε αυτήν, όπως στο &obj + 5).

Για να αποσαφηνίσουμε μια παρανόηση:

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

int &ri = i,

αν δεν βελτιστοποιηθεί εντελώς, *διαθέτει την ίδια ποσότητα αποθηκευτικού χώρου όπως ένας δείκτης, και τοποθετεί τη διεύθυνση του "i" σε αυτόν τον αποθηκευτικό χώρο.

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

Ως γενικός κανόνας,

  • Χρησιμοποιείτε αναφορές σε παραμέτρους και τύπους επιστροφής συναρτήσεων για να παρέχετε χρήσιμες και αυτο-τεκμηριωμένες διεπαφές.
  • Χρησιμοποιήστε δείκτες για την υλοποίηση αλγορίθμων και δομών δεδομένων.

Ενδιαφέρουσα ανάγνωση:

  1. Ένας δείκτης μπορεί να εκχωρηθεί εκ νέου:

    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,
  2. Ένας δείκτης έχει τη δική του διεύθυνση μνήμης και το δικό του μέγεθος στη στοίβα (4 bytes σε x86), ενώ μια αναφορά μοιράζεται την ίδια διεύθυνση μνήμης (με την αρχική μεταβλητή) αλλά καταλαμβάνει επίσης κάποιο χώρο στη στοίβα. Εφόσον μια αναφορά έχει την ίδια διεύθυνση με την ίδια την αρχική μεταβλητή, είναι ασφαλές να θεωρήσουμε μια αναφορά ως ένα άλλο όνομα για την ίδια μεταβλητή. Σημείωση: Αυτό στο οποίο δείχνει ένας δείκτης μπορεί να βρίσκεται στη στοίβα ή στο σωρό. Το ίδιο και μια αναφορά. Ο ισχυρισμός μου σε αυτή τη δήλωση δεν είναι ότι ένας δείκτης πρέπει να δείχνει στη στοίβα. Ένας δείκτης είναι απλώς μια μεταβλητή που κρατά μια διεύθυνση μνήμης. Αυτή η μεταβλητή βρίσκεται στη στοίβα. Εφόσον μια αναφορά έχει το δικό της χώρο στη στοίβα, και εφόσον η διεύθυνση είναι ίδια με τη μεταβλητή στην οποία αναφέρεται. Περισσότερα για το [stack vs heap][1]. Αυτό σημαίνει ότι υπάρχει μια πραγματική διεύθυνση μιας αναφοράς την οποία ο μεταγλωττιστής δεν θα σας πει.

    int x = 0,
    int &r = x,
    int *p = &x,
    int *p2 = &r,
    assert(p == p2),
  3. Μπορείτε να έχετε δείκτες σε δείκτες σε δείκτες που προσφέρουν επιπλέον επίπεδα κατεύθυνσης. Ενώ οι αναφορές προσφέρουν μόνο ένα επίπεδο έμμεσης κατεύθυνσης.

    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),
  4. Ο δείκτης μπορεί να εκχωρηθεί άμεσα στο nullptr, ενώ η αναφορά όχι. Αν προσπαθήσετε αρκετά και ξέρετε πώς, μπορείτε να κάνετε τη διεύθυνση μιας αναφοράς nullptr. Ομοίως, αν προσπαθήσετε αρκετά σκληρά, μπορείτε να έχετε μια αναφορά σε έναν δείκτη, και τότε αυτή η αναφορά μπορεί να περιέχει nullptr.

    int *p = nullptr,
    int &r = nullptr; 
Σχόλια (43)

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

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

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

Ο στόχος μιας αναφοράς δεν μπορεί να αντικατασταθεί με κανέναν τρόπο εκτός από το UB.

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

Σχόλια (5)

Μια αναφορά δεν μπορεί ποτέ να είναι "NULL".

Σχόλια (10)