Mai mult
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:
- Un pointer poate fi re-alocate orice număr de ori, în timp ce o trimitere nu poate fi re-alocate după legare.
- Pointerii pot punctul de nicăieri (
NULL
), întrucât o trimitere se referă întotdeauna la un obiect. - Puteți't ia adresa de trimitere ca tine poate cu indicii.
- 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:
- Mele preferate all-time C++ FAQ lite.
- Referințe vs Indicii.
- O Introducere la Referințe.
- Referințe și const.
3095
37
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;
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; afirma(y == 4); afirma(x == 0);
nullptr
direct, întrucât referință nu poate. Dacă încercați destul de greu, și știi cum, poți face la adresa de trimiterenullptr
. De asemenea, dacă încercați greu, poti avea o referință la un pointer, și atunci de referință poate conținenullptr
.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
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.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.
.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.
Trimiterile nu pot fi umplute într-o matrice, în timp ce indicii pot fi (Menționat de către utilizator @litb)
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.
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:
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.
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ță.
Î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ă.În afară de zahăr sintactic, o referință este un
const
pointer (nu pointer la oconst
). 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ță.
Contrar opiniei populare, este posibil să aveți o referință, care este NULĂ.
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ă.
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.
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.
Ai uitat partea cea mai importanta:
membru-acces cu indicii foloseste->
<br/> membru-acces cu referințe foloseste
.`foo.bar
e clar superiorfoo->bar
la fel ca vi clar superior Emacs :-)Referințele sunt foarte similare cu indicii, dar ele sunt în mod special fabricata pentru a fi de ajutor pentru optimizarea compilatoare.
Ca un exemplu:
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:
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
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.
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:
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.
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.
O trimitere nu poate fi
NULL
.Î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:
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:
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.
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ță.
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.
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:
Pe plan intern devine
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:
va imprimare:
Aceasta este limba mecanism care permite ScopeGuard să lucreze.
Aceasta se bazează pe tutorial. Ceea ce este scris face mai clar:
Pur și simplu să ne amintim că,
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,
alias_Tom
poate fi înțeleasă ca un alias de o variabil(diferite cu
typedef, care este
alias 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".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:
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;
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):
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
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: