Care sunt diferențele între o variabilă pointer și o referință variabilă în C++?

Știu că referințele sunt sintactică de zahăr, astfel încât codul este mai ușor să scrie și să citească.

Dar care sunt diferențele?


Rezumat de răspunsuri și link-uri de mai jos:

  1. Un pointer poate fi re-alocate orice număr de ori, în timp ce o trimitere nu poate fi re-alocate după legare.
  2. Pointerii pot punctul de nicăieri (NULL), întrucât o trimitere se referă întotdeauna la un obiect.
  3. Puteți't ia adresa de trimitere ca tine poate cu indicii.
  4. Nu's nu "de referință aritmetică" (dar poti lua adresa unui obiect indicat de o referință și de a face aritmetica pointerilor pe ea ca într &obj + 5).

Pentru a clarifica o concepție greșită:

C++ standard este foarte atenți pentru a evita să dicteze cum un compilator poate punerea în aplicare referințe, dar de fiecare compilator C++ pune în aplicare referințele la fel de indicii. Asta este, o declarație, cum ar fi:

int &ri = i;

dacă nu's nu este optimizat în întregime, alocă aceeași sumă de stocare ca un pointer, și locuri adresa de " i " în care de stocare.

Deci, un pointer și o referință ambele folosesc aceeași cantitate de memorie.

Ca regulă generală,

  • Utilizați referințe în funcție de parametri și tipuri de a reveni pentru a oferi utile și de auto-documentarea interfețe.
  • Utilizați indicii pentru implementarea unor algoritmi și structuri de date.

Interesant de citit:

Comentarii la întrebare (33)
  1. Un pointer poate fi re-alocate:

int x = 5; int y = 6; int p; p = &x; p = &y; p = 10; afirma(x == 5); afirma(y == 10);

O trimitere nu poate, și trebuie să fie atribuite la inițializare:

int x = 5; int y = 6; int &r = x;

  1. Un pointer are propria adresă de memorie și dimensiunea pe stiva (4 bytes pe x86), întrucât o referință împărtășește aceeași adresă de memorie (cu variabila originală), dar, de asemenea, nevoie de mai mult spațiu pe stivă. Când o referință are aceeași adresă ca variabila originală în sine, este sigur să se gândească la o referință ca un alt nume pentru aceeași variabilă. Notă: Ce este un pointer de puncte pentru a putea fi pe stiva sau heap. Idem referință. Cererea mea în această declarație nu este ca un pointer trebuie să punctul de la stivă. Un pointer este o variabilă care deține o adresă de memorie. Această variabilă este pe stiva. Când o referință are propriul spațiu pe stivă, iar din adresa este aceeași ca și variabila face referire. Mai pe stack vs heap. Acest lucru implică faptul că există o reală adresa o referință pe care compilatorul nu va spune.

int x = 0; int &r = x; int p = &x; int p2 = &r; assert(p == p2);

  1. Puteți avea pointeri la pointeri la pointeri oferă niveluri suplimentare de indirectare. Întrucât referințele oferi numai un nivel de indirectare.

int x = 0; int y = 0; int p = &x; int q = &y; int *pp = &p; pp = &q;//pp = q **pp = 4; afirma(y == 4); afirma(x == 0);

  1. Indicatorul poate fi atribuit nullptr direct, întrucât referință nu poate. Dacă încercați destul de greu, și știi cum, poți face la adresa de trimitere nullptr. De asemenea, dacă încercați greu, poti avea o referință la un pointer, și atunci de referință poate conține nullptr.

int p = nullptr; int &r = nullptr; <--- eroare de compilare int &r = p; <--- probabil nici o eroare de compilare, mai ales dacă nullptr este ascuns în spatele un apel de funcție, dar nu se referă la un non-existent int la adresa 0

  1. Pointerii pot repeta peste o matrice, puteți utiliza ++ pentru a merge la următorul articol că un pointer este îndreptat la, și + 4 pentru a merge la al 5-lea element. Aceasta este, indiferent de mărimea obiectului este că indicatorul puncte.

  2. Un indicator trebuie să fie dereferenced cu * pentru a accesa locația de memorie a puncte a, întrucât o referință pot fi utilizate direct. Un pointer la o clasă/struct foloseste-> accesul la it's membrii întrucât o referință foloseste un ..

  3. Un pointer este o variabilă care deține o adresă de memorie. Indiferent de cum o referință este pusă în aplicare, o referință are aceeași adresă de memorie ca obiect trimiteri.

  4. Trimiterile nu pot fi umplute într-o matrice, în timp ce indicii pot fi (Menționat de către utilizator @litb)

  5. Const referințe pot fi obligate să temporari. Indicii nu pot (nu fără unele indirectare):

const int &x = int(12); //juridice C++ int *y = &int(12); //ilegal să dereference temporar.

Acest lucru face `const& mai sigur pentru utilizare în argument liste și așa mai departe.

Comentarii (43)

Ce's o C++ de referință (pentru C programatori)

O referință poate fi gândit ca un constantă pointer (a nu se confunda cu un pointer la o valoare constantă!) cu automate de indirectare, de exemplu, compilatorul va aplica * operator pentru tine.

Toate trimiterile trebuie să fie inițializat cu o non-valoare nulă sau compilare va eșua. L's nici posibil pentru a obține adresa de trimitere - adresa operatorul va returna adresa de referință valoarea-în schimb - nici nu este posibil să se facă aritmetica pe referințe.

C programatorii ar putea displace C++ referințe ca nu va mai fi evident atunci când indirectare se întâmplă sau dacă un argument devine transmise prin valoare sau prin pointer fără să se uite la funcția de semnături.

Programatori C++ s-ar putea displace folosind indicii ca acestea sunt considerate nesigure - deși referințele sunt't într-adevăr mai în siguranță decât constanta de indicatori, cu excepția în cele mai banale cazuri - lipsa confortul de automate de indirectare și transporta o altă conotație semantică.

Luați în considerare următoarea declarație de C++ FAQ:

Chiar dacă o referință este adesea implementată folosind o adresă în de fond al sistemului limbaj de asamblare, vă rugăm să nu nu cred că de o referință ca un caraghios pointer la un obiect. O referință e obiect. Este nu un pointer la obiect, nici o copie a obiectului. A este obiect.

Dar dacă o referință într-adevăr ** au fost obiect, cum ar fi marionetă referințe? În unmanaged limbi,'s imposibil de referințe să fie nici o 'mai sigur' decât indicii - acolo, în general, e't un fel de fiabil alias valori peste limitele de aplicare!

De ce consider C++ referințe utile

Venind de la un C fundal, C++ referințe poate arata ca o oarecum o prostie concept, dar unul ar trebui să încă să le utilizeze în loc de indicii, acolo unde este posibil: Automate de indirectare e convenabil, și referințe devenit deosebit de util atunci când se ocupă cu GARA - dar nu pentru orice perceput de siguranță avantaj, ci mai degrabă pentru că face scrierea de cod idiomatic mai puțin ciudat.

Acest fragment este unul dintre conceptele centrale de C++, dar interacționează non-trivial cu copierea semantica. Trecerea obiecte de referință evită aceste probleme, ca nu copierea este implicat. Dacă referințele nu au fost prezente în limbă,'d trebuie să utilizați indicii în schimb, care sunt mai greoaie de a folosi, încălcând astfel de limbaj de design principiul că cea mai practică soluție ar trebui să fie mai ușor decât alternativele.

Comentarii (15)

Dacă vrei să fii foarte pedant, există un lucru care le puteți face cu o trimitere care le poate't face cu un pointer: extinde durata de viata a unui obiect temporar. În C++ dacă se leagă de un const referință la un obiect temporar, la viata de acel obiect devine viață de referință.

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

În acest exemplu s3_copy copii temporară obiect care este un rezultat de concatenare. Întrucât s3_reference, în esență, devine temporar obiect. L'e într-adevăr o referință la un obiect temporar care acum are aceeași durată de viață ca referință.

Dacă încercați acest lucru fără const ar eșua pentru a compila. Se poate lega un non-const referință la un obiect temporar, nici nu se poate lua o adresă pentru care contează.

Comentarii (8)

În afară de zahăr sintactic, o referință este un const pointer (nu pointer la o const). Trebuie să se stabilească la ce se referă atunci când declară de referință variabilă, și nu poți schimba asta mai târziu.

Update: acum că mă gândesc mai mult, există o diferență importantă.

Un pointer const's-țintă poate fi înlocuit prin luarea adresa sa și folosind o const exprimate.

O trimitere's-țintă nu poate fi înlocuit în orice mod scurtă de UB.

Acest lucru ar trebui să permită compilator pentru a face mai mult de optimizare pe o referință.

Comentarii (5)

Contrar opiniei populare, este posibil să aveți o referință, care este NULĂ.

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

Desigur, este mult mai greu de a face cu o trimitere - dar dacă reușești, te'll rupe parul din cap încercând să-l găsească. Referințele sunt nu în mod inerent în condiții de siguranță în C++!

Din punct de vedere tehnic aceasta este o invalid de referință, nu o referință nulă. C++ nu't suport null referințe ca un concept ca s-ar putea găsi în alte limbi. Există și alte tipuri de invalid referiri la fel de bine. Orice invalid de referință ridică spectrul de comportament nedefinit, doar ca folosind un invalid pointer-ar.

Eroarea reală este în dereferencing de pointer NUL, înainte de misiune pentru o trimitere. Dar eu'm nu are cunoștință de orice compilatoare, care va genera orice erori pe care starea - eroarea se propagă de la un punct mai departe în cod. Ca's ceea ce face ca această problemă atât de insidios. Cele mai multe din timp, dacă dereference NUL indicatorul, accident chiar în acel loc și nu't lua mai mult de depanare ca să-mi dau seama.

Exemplul meu de mai sus este scurt și contrived. Aici's o mai exemplu din lumea reală.

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

Vreau să reiterez faptul că singura modalitate de a obține o referință nulă este prin malformat cod, și odată ce o ai te're obtinerea comportament nedefinit. L n face sens pentru a verifica pentru o referință nulă; de exemplu, puteți încerca dacă(&bar==NULL)... dar compilatorul ar putea optimiza declarație de existență! O referință valabilă nu poate fi NULL atât de compilator's vezi comparația este întotdeauna falsă, și este gratuit pentru a elimina "dacă" clauza ca mort cod - aceasta este esența de comportament nedefinit.

Modul corect de a sta departe de probleme este de a evita dereferencing un pointer NULL pentru a crea o referință. Aici's mod automat pentru a realiza acest lucru.

template
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

Pentru un mai vechi cu privire la această problemă de la cineva cu mai bine abilitățile de scriere, a se vedea Null Referințe de la Jim Hyslop și Herb Sutter.

Pentru un alt exemplu de pericolele de dereferencing un pointer nul vezi Expunerea nedefinit comportament atunci când încearcă să port codul de la o altă platformă de Raymond Chen.

Comentarii (18)

Ai uitat partea cea mai importanta:

membru-acces cu indicii foloseste-><br/> membru-acces cu referințe foloseste.`

foo.bar e clar superior foo->bar la fel ca vi clar superior Emacs :-)

Comentarii (6)

Referințele sunt foarte similare cu indicii, dar ele sunt în mod special fabricata pentru a fi de ajutor pentru optimizarea compilatoare.

  • Trimiterile sunt concepute astfel încât este mult mai ușor pentru compilator pentru a urmări care fac trimitere la pseudonime care variabile. Două caracteristici majore sunt foarte importante: nu există "de referință aritmetică" și nu realocarea de referințe. Acestea permit compilatorului să-mi dau seama care trimiterile alias care variabile la compilare.
  • Trimiterile sunt permise pentru a se referi la variabile care nu au adrese de memorie, cum ar fi cele compilatorul alege pentru a pune în registre. Dacă luați adresa unei variabile locale, este foarte greu pentru compilatorul să-l pună într-un registru.

Ca un exemplu:

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

O optimizarea compiler poate da seama că suntem accesarea a[0] si a[1] destul de o grămadă. Ar dragoste pentru a optimiza algoritmul pentru:

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

Pentru a face o astfel de optimizare, de care are nevoie pentru a dovedi că nimic nu poate schimba matrice[1] în timpul apelului. Acest lucru este destul de ușor de a face. nu este niciodată mai puțin de 2, deci matrice[i] poate nu se referă la matrice[1]. maybeModify() este dat a0 ca o referință (aliasing matrice[0]). Pentru că nu există nici o "de referință" aritmetica, compilatorul are doar pentru a dovedi că maybeModify nu se adresa lui x, și s-a dovedit că nimic nu se schimbă matrice[1].

Ea are, de asemenea, pentru a dovedi că nu există modalități de viitor suna putea citi/scrie a[0] în timp ce avem o temporar registrul copie în format a0. Acest lucru este de multe ori trivial pentru a dovedi, pentru că în multe cazuri este evident că referirea nu este niciodată stocate într-o structură permanentă, ca un exemplu de clasă.

Acum face același lucru cu pointeri

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

Comportamentul este la fel; numai că acum este mult mai greu pentru a dovedi că maybeModify nu modifice niciodată matrice[1], pentru că deja am dat un pointer; pisica din sac. Acum ea are de a face mult mai dificil dovada: o analiza statica a maybeModify pentru a dovedi că nu scrie &x + 1. Ea are, de asemenea, pentru a dovedi că nu salvează pe un indicator care se poate referi la matrice[0], care este la fel de dificil.

Modern compilatoare sunt obtinerea mai bine și mai bine la analiza statica, dar este întotdeauna frumos pentru a le ajuta și de a folosi referințe.

Desigur, în ipoteza că astfel de inteligent optimizări, compilatoare va transforma într-adevăr referințe în indicii atunci când este necesar.

EDIT: după Cinci ani de a posta acest raspuns, am găsit o reală diferență tehnică în cazul în care referințele sunt diferite decât doar un mod diferit de a privi aceeași abordarea conceptului. Referințe pot modifica durata de viata de obiectele temporare într-un mod care indicii pot.


F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout 
Comentarii (4)

De fapt, o trimitere nu este într-adevăr ca un pointer.

Un compilator ține "referințe" pentru variabile, asocierea unui nume cu o adresă de memorie; ca's de locuri de muncă de a traduce orice nume de variabilă de la o adresă de memorie atunci când compilarea.

Atunci când creați o referință, trebuie doar să spuneți compiler care ar atribui un alt nume pentru variabila pointer; ca's de ce referințe nu pot "punct de nul", pentru că o variabilă nu poate fi, și să nu fie.

Pointerii sunt variabile; ele conțin adresa de alte variabile, sau poate fi nul. Cel mai important lucru este că un pointer are o valoare, în timp ce numai o referință a o variabilă care se referă.

Acum o explicație reală cod:

int a = 0;
int& b = a;

Aici nu sunt crearea de o altă variabilă care punctele de la "a"; sunt doar adăugând un alt nume pentru conținutul memoriei deține valoarea de "a". Această memorie are acum două nume, " a " și "b", și nu pot fi abordate folosind fie nume.

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

Atunci când apelați o funcție, compilatorul generează, de obicei, spații de memorie pentru argumentele pentru a fi copiate. Funcția semnătura definește spațiile care ar trebui să fie creat și dă numele pe care ar trebui să fie utilizate pentru aceste spații. Declararea unui parametru ca referință doar spune compilatorului să utilizați de intrare variabilă de spațiu de memorie în loc de alocarea unui nou spațiu de memorie în timpul apel de metodă. Poate părea ciudat să spun că funcția va fi direct manipulează o variabilă declarată în asteptare domeniul de aplicare, dar amintiți-vă că atunci când execută cod compilat, nu mai este domeniul de aplicare; nu există pur și simplu plat memorie, și funcția de cod ar putea manipula variabile.

Acum, pot exista unele cazuri în care dumneavoastră compiler nu poate fi în măsură să știe de referință atunci când compilarea, ca atunci când se utilizează un extern variabil. Deci o trimitere poate sau nu poate fi implementat ca un pointer în codul de bază. Dar în exemplele ți-am dat, că cel mai probabil nu vor fi puse în aplicare cu un pointer.

Comentarii (5)

O trimitere nu poate fi NULL.

Comentarii (10)

În timp ce ambele referințe și pointeri sunt utilizate pentru acces indirect o altă valoare, există două diferențe importante între referințe și pointeri. Prima este că o trimitere se referă întotdeauna la un obiect: este o eroare să se definească o referință fără a-l inițializa. Comportamentul de misiune este cea de-a doua diferență importantă: Atribuirea referință schimbă obiectul la care referința este legat; nu rebind referință la un alt obiect. Odată inițializat, o trimitere se referă întotdeauna la același fond al sistemului obiect.

Luați în considerare aceste două program de fragmente. În primul rând, vom atribui un pointer la un alt:

int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;    // pi now points to ival2

După cesiune, t, obiectul adresat de pi rămâne neschimbată. Sarcina schimbă valoarea lui pi, făcându-l punct la un alt obiect. Acum ia în considerare un program similar, care atribuie două referințe:

int &ri = ival, &ri2 = ival2;
ri = ri2;    // assigns ival2 to ival

Această misiune schimbări t, valoarea referite de ri, și nu trimiterea în sine. După cesiune, cele două referințe se referă încă la lor de obiecte originale, și valoarea acestor obiecte este acum la fel ca ei.

Comentarii (1)

Există o diferență semantică care pot apărea ezoterice dacă nu sunteți familiarizați cu studierea calculator limbi într-un rezumat sau chiar academic de moda.

La cel mai înalt nivel, ideea de referințe este că acestea sunt transparente "alias". Calculatorul dumneavoastră poate utiliza o adresă pentru a le face locul de muncă, dar're nu trebuie să vă faceți griji despre asta: te're ar trebui să cred că de ei ca "doar un alt nume" pentru un obiect existent și sintaxa reflectă faptul că. Acestea sunt mai stricte decât indicii deci compilatorul poate mai fiabil vă avertizeze atunci când ești pe cale de a crea o marionetă de referință, decât atunci când sunt pe cale de a crea o marionetă pointer.

Dincolo de asta, există, desigur, unele practice diferențe între pointeri și referințe. Sintaxa pentru utilizarea acestora este în mod evident diferite, și nu se poate "re-scaun" referințe, au trimiteri la neant, sau indicii de referință.

Comentarii (0)

O referință este un alias pentru o altă variabilă, întrucât un pointer deține adresa de memorie a unei variabile. Trimiterile sunt, în general, utilizate ca parametrii de funcționare, astfel încât trecut obiect nu este o copie dar obiectul în sine.

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 
Comentarii (0)

O referință nu este un alt nume dat unor memorie. L's o imuabil pointer, care este în mod automat de-a referit la utilizare. Practic se reduce la:

int& j = i;

Pe plan intern devine

int* const j = &i;
Comentarii (4)

Nu't contează cât de mult spațiu este nevoie, deoarece puteți't vedea de fapt nici un efect secundar (fără executarea de cod) din orice loc s-ar lua în sus.

Pe de altă parte, o diferență majoră între referințe și pointeri este că temporare atribuite const referințe trăi până la const referință iese din domeniul de aplicare.

De exemplu:

class scope_test
{
public:
    ~scope_test() { printf("scope_test done!\n"); }
};

...

{
    const scope_test &test= scope_test();
    printf("in scope\n");
}

va imprimare:

in scope
scope_test done!

Aceasta este limba mecanism care permite ScopeGuard să lucreze.

Comentarii (4)

Aceasta se bazează pe tutorial. Ceea ce este scris face mai clar:

>>> The address that locates a variable within memory is
    what we call a reference to that variable. (5th paragraph at page 63)

>>> The variable that stores the reference to another
    variable is what we call a pointer. (3rd paragraph at page 64)

Pur și simplu să ne amintim că,

>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)

Ce's mai mult, ca să ne putem referi la aproape orice pointer tutorial, un pointer este un obiect care este susținută de aritmetica pointerilor ceea ce face ca indicatorul similar cu o matrice.

Uită-te la următoarea declarație,

int Tom(0);
int & alias_Tom = Tom;

alias_Tom poate fi înțeleasă ca un alias de o variabil(diferite cutypedef, care estealias pentru un tip`) "Tom". Este, de asemenea, BINE pentru a uita terminologia de o astfel de declarație este de a crea o referință de "Tom".

Comentarii (3)

O referință la un pointer este posibil în C++, dar invers nu este posibil înseamnă un pointer la o referință e't posibil. O referință la un pointer oferă un aspirator sintaxa pentru a modifica indicatorul. Uita-te la acest exemplu:


#include
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout
Comentarii (0)

Eu folosesc referințe dacă am nevoie de oricare dintre aceste:

  • Null pointer poate fi folosit ca un valoare santinelă, de multe ori o modalitate ieftină de a evita supraîncărcarea funcție sau de a folosi de un bool.

  • Poți să faci aritmetică pe un pointer. De exemplu, p += offset;

Comentarii (1)

Răspunsul direct ##

Ce este o referință în C++? Un exemplu specific de tip, care nu este un obiect de tip. Ce este un pointer în C++? Un exemplu specific de tip, care este un obiect de tip. De la [ISO C++ definiția de tip obiect](http://eel.is/c++proiect/de bază.tipuri#8):

O obiect tip este un (eventual cv-calificat) tip care nu este o funcție de tip, nu un tip de referință, și nu cv void. Poate fi important să știu, tip de obiect este o categorie de nivel superior de tip univers în C++. De referință este, de asemenea, o categorie de nivel superior. Dar indicatorul nu este. Pointeri și referințe sunt menționate împreună [în contextul tip compus](http://eel.is/c++proiect/de bază.compus#1). Acest lucru este datorat naturii declarator sintaxa moștenit de la (și prelungit) C, care nu are referințe. (În plus, există mai mult de un tip de declarator de referințe din C++ 11, în timp ce indicii sunt încă "unityped": &+&& vs.*.) Deci elaborarea unui limbaj specific prin "extensia" cu stil similar de C, în acest context, este oarecum rezonabil. (Eu încă susțin că sintaxa declarators deșeuri sintactice expresivitate mult, face atât umane, cât utilizatori și implementări de frustrant. Astfel, toate acestea nu sunt calificat să fie built-in într-un nou limbaj de design. Acesta este un subiect cu totul diferit despre PL design, totuși.) În caz contrar, este nesemnificativă că indicii poate fi calificată ca un anumit felul de tipuri, cu trimiteri împreună. Ei pur și simplu partaja prea puține proprietăți comune în afară de sintaxa similitudine, astfel încât nu este nevoie să le-a pus împreună în cele mai multe cazuri. Notă declarațiile de mai sus menționează numai "indicii" și "referințe" ca tipuri. Există unele interesați de întrebări despre cazuri (ca variabile). Există, de asemenea, vin prea multe idei preconcepute. Diferențele de nivel superior categorii deja poate dezvălui multe din beton diferențele nu legat de pointeri direct:

  • Tipuri de obiecte pot fi de nivel superior cv calificare. Referințe nu pot.
  • Variabila de tip obiect face ocupă de stocare ca pe abstract mașină semantica. De referință nu este necesar ocupă de depozitare (a se vedea secțiunea despre concepțiile greșite de mai jos pentru detalii).
  • ... Câteva reguli speciale privind referințe:
  • Compus declarators sunt mai restrictive pe referințe.
  • Referințe pot colaps.
  • Reguli speciale privind `&& parametri (ca "transmiterea referințe") pe baza de referință prăbușește în timpul șablon parametru permite deducerea "perfect forwarding" de parametri.
  • Referințe au reguli speciale în inițializare. Durata variabilă declarată ca un tip de referință pot fi diferite la obiecte obișnuite prin extensie.
  • BTW, câteva alte contexte, cum ar fi inițializarea implică std::initializer_list cum urmează niște reguli similare de referință prelungirea duratei de viață. Aceasta este o altă mâncare de pește.
  • ...

    Conceptiile gresite

    Zahăr sintactic

    știu că referințele sunt sintactică de zahăr, astfel încât codul este mai ușor să scrie și să citească. Din punct de vedere tehnic, acest lucru este gresit. Trimiterile nu sunt sintactică de zahăr de orice alte funcții în C++, pentru că ei nu pot fi exact înlocuite de alte caracteristici, fără nici o diferență semantică. (În mod similar, lambda-expresiee nu sunt * sintactică de zahăr de orice alte funcții în C++ pentru că nu poate fi precis simulate cu "nespecificat" proprietăți ca declarația scopul de a capturat variabile, care pot fi importante pentru inițializarea scopul unor astfel de variabile pot fi semnificative.) C++ are doar câteva tipuri sintactice zaharuri în acest sens strict. Un exemplu este (moștenit de la C) built-in (non-supraîncărcat) operatorul [], care [este definit exact, având aceleași proprietăți semantice specifice forme de asociere peste built-in de operatorul unar `și binar+`](http://eel.is/c++proiect/expr.sub#1).

    De stocare

    Deci, un pointer și o referință ambele folosesc aceeași cantitate de memorie. Declarația de mai sus este pur și simplu greșit. Pentru a evita astfel de concepții greșite, uita-te la ISO C++ reguli în loc: De [[intro.obiect]/1](http://eel.is/c++proiect/intro.obiect nr. 1): ... Un obiect ocupă o regiune de depozitare în perioada de construcție, de-a lungul duratei sale de viață, și în perioada sa de distrugere. ... De [dcl.ref]/4: este nespecificat dacă este sau nu o referință necesită depozitare. Notă acestea sunt semantic proprietăți.

    Pragmatica

    Chiar că indicii nu sunt suficient de calificat pentru a fi puse împreună cu referințe în sensul de limbajul de design, există încă unele argumente făcându-l discutabilă de a face o alegere între ele, în alte contexte, de exemplu, atunci când face alegeri pe tipuri de parametri. Dar acest lucru nu este întreaga poveste. Adică, există mai multe lucruri decât pointeri vs referințe trebuie să ia în considerare. Dacă tu nu't trebuie să rămânem pe astfel de opțiuni specifice, în cele mai multe cazuri răspunsul este scurt: nu au necesitatea de a folosi indicii, astfel încât să don't. Indicii sunt, de obicei, destul de rău pentru că ele implică prea multe lucruri pe care nu't aștepta și se va baza pe prea multe ipoteze implicite subminarea mentenabilitatea și (chiar) portabilitatea codului. Inutil bazându-se pe indicii este cu siguranta un stil rău și ar trebui să fie evitate în sensul modern C++. Reconsideră-ți scopul și veți găsi în cele din urmă că indicatorul este o caracteristică de ultima felul în cele mai multe cazuri.

  • Uneori, limba norme prevede în mod explicit tipurile specifice pentru a fi utilizate. Dacă doriți să utilizați aceste caracteristici, se supun regulilor.
  • Copie constructori necesită anumite tipuri de cv-& tipul de referință ca 1 parametru tipul. (Și, de obicei, ar trebui să fie const calificat.)
  • Mutați constructori necesită anumite tipuri de cv-&& tipul de referință ca 1 parametru tipul. (Și, de obicei, nu ar fi nici o calificare.)
  • Specifice suprasarcini de operatorii de referință sau non tipurile de referință. De exemplu:
  • Supraîncărcat operator= ca funcții membre speciale necesită trimitere tipuri similare de la 1 parametru de copiere/mutare constructori.
  • Postfix ++ necesită dummy int.
  • ...
  • Dacă știi pass-by-value (adică folosind non-tipuri de referință) este suficient, utilizați-l în mod direct, în special atunci când se utilizează o implementare de sprijin C++17 mandatat copia eliziune. (Avertisment: cu toate Acestea, pentru a exhaustiv motive cu privire la necesitatea poate fi foarte complicat.)
  • Dacă doriți să opereze niște mânere cu dreptul de proprietate, de a folosi smart pointeri ca unique_ptr " și " shared_ptr (sau chiar cu homebrew cele de unul singur, dacă aveți nevoie de ele să fie opac), mai degrabă decât de prime indicii.
  • Daca faci unele iterații într-un interval, folosesc iteratori (sau unele intervale care nu sunt oferite de biblioteca standard încă), mai degrabă decât de prime indicii dacă nu sunt convins prime indicii va face mai bine (de exemplu, pentru mai puțin de antet dependențe) în cazuri foarte specifice.
  • Dacă știi pass-by-value este suficientă și vrei ceva explicit null semantica, utilizați ambalajul ca std::opțional, mai degrabă decât de prime indicii.
  • Dacă știi pass-by-value nu este ideal pentru motivele de mai sus, și nu't vreau null semantica, folosiți {lvalue, rvalue, transmiterea} referințe.
  • Chiar și atunci când vrei semantica tradițională pointer, sunt de multe ori ceva mai adecvat, ca `observer_ptr în Bibliotecă Fundamentale TS. Singurele excepții nu poate fi lucrat în jurul valorii de în limbajul curent:
  • Atunci când sunt de punere în aplicare inteligent indicii de mai sus, ai putea avea de-a face cu prime indicii.
  • Limbaj Specific-interoperabilitatea rutine nevoie de indicii, ca operator nou. (Cu toate acestea, *cv*-voideste încă destul de diferite și mai sigur în comparație cu cele obișnuite obiect indicii pentru a exclude neașteptate pointer aritmetica dacă sunteți bazându-se pe unele neconforme extensie pevoid` ca GNU's. a.)
  • Funcția de pointeri poate fi convertit de la expresii lambda, fără a surprinde, în timp ce funcția de referințe nu pot. Trebuie să utilizați funcția de indicii non-codul generic pentru astfel de cazuri, chiar și în mod deliberat nu doresc null valori. Deci, în practică, răspunsul este atât de evident: atunci când în dubiu, pentru a evita pointeri. Trebuie să utilizați indicii numai atunci când există foarte explicit motivele pentru care nimic altceva nu este mai adecvat. Cu excepția câtorva cazuri excepționale menționate mai sus, astfel de alegeri sunt aproape întotdeauna nu pur C++-specifice (dar susceptibile de a fi limba-punerea în aplicare specifice). Astfel de cazuri pot fi:
  • Trebuie să servească la vechiul stil (C) APIs.
  • Trebuie să îndeplinească ABI cerințele specifice C++ implementări.
  • Trebuie să interacționeze în timpul rulării cu limbi diferite implementări (inclusiv diverse ansambluri, language runtime și FFI de un înalt nivel de client limbi), bazat pe ipoteze de implementări specifice.
  • Trebuie să îmbunătățească eficiența traducerea (compilarea & legarea) în unele cazuri extreme.
  • Trebuie sa se evite simbol umfla în unele cazuri extreme.

    Limba neutralitatea limitări

    Dacă ai venit să vezi întrebare prin unele rezultate de căutare Google (nu sunt specifice C++), acest lucru este foarte probabil să fie un loc nepotrivit. Referințe în C++ este destul de "ciudat", cum este, în esență, nu de prima clasa: ele vor fi tratate ca obiecte sau funcțiile fiind menționate, astfel încât acestea nu au nici o șansă să sprijine unele de primă clasă operațiuni ca fiind operand stânga de statele acces operator independent de tipul celor menționate obiect. Alte limbi pot sau nu pot avea restricții similare referințele acestora. Referințe în C++ probabil nu va păstra sensul în diferite limbi. De exemplu, referințe, în general, nu implică nonnull proprietăți pe valori, cum ar fi acestea în C++, deci, astfel de ipoteze ar putea să nu funcționeze în alte limbi (și veți găsi contraexemple destul de ușor, de exemplu, Java, C#, ...). Nu poate fi încă unele proprietăți comune printre referințe în diferite limbaje de programare, în general, dar las's lăsați-l pentru alte întrebări într-ATÂT. (O paranteză: întrebarea poate fi semnificativ mai devreme decât orice "C-ca" limbi sunt implicate, ca ALGOL 68 vs PL/I.)

Comentarii (0)

Există o diferență fundamentală între pointeri și referințe care am't vedea cineva a menționat: referințe permite să treacă de referință semantică în funcție de argumente. Indicii, deși nu este vizibil la prima nu: ele oferă doar trece-de-valoare semantică. Acest lucru a fost foarte frumos descris în acest articol.

În ceea ce privește, &rzej

Comentarii (6)

Cu riscul de a adăuga la confuzie, vreau să arunce în unele intrare, am'sunt sigur că cea mai mare parte depinde de modul în care compilatorul pune în aplicare referințe, dar în cazul ccg ideea că o referință poate doar să o variabilă pe stiva nu este de fapt corecta, de a lua acest exemplu:


#include 
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout 
Comentarii (0)