Viem, že odkazy sú syntaktickým cukrom, takže kód sa ľahšie číta a píše.
Aké sú však rozdiely?
Zhrnutie odpovedí a odkazov nižšie:
NULL
), zatiaľ čo referencia vždy odkazuje na objekt.&obj + 5
).Na objasnenie mylnej predstavy:
- Štandard C++ sa veľmi opatrne vyhýba diktovaniu toho, ako môže kompilátor implementovať referencie, ale každý kompilátor C++ implementuje referencie ako ukazovatele. To znamená, že deklarácia ako:*
int &ri = i;
ak nie je úplne optimalizovaná, alokuje rovnaké množstvo pamäte ako ukazovateľ a umiestni adresu čísla
i
do tohto úložiska.
Takže ukazovateľ aj odkaz využívajú rovnaké množstvo pamäte.
Všeobecne platí pravidlo,
Zaujímavé čítanie:
Ukazovateľ možno opätovne priradiť:
int x = 5;
int y = 6;
int *p;
p = &x;
p = &y;
*p = 10;
assert(x == 5);
assert(y == 10);
Referencia sa nemôže a musí priradiť pri inicializácii:
pri inicializácii: int x = 5;
int y = 6;
int &r = x;
Ukazovateľ má svoju vlastnú pamäťovú adresu a veľkosť na zásobníku (4 bajty na x86), zatiaľ čo odkaz zdieľa rovnakú pamäťovú adresu (s pôvodnou premennou), ale tiež zaberá určité miesto na zásobníku. Keďže referencia má rovnakú adresu ako samotná pôvodná premenná, je bezpečné považovať referenciu za iné meno pre tú istú premennú. Poznámka: To, na čo ukazuje ukazovateľ, môže byť na zásobníku alebo na halde. To isté platí aj pre odkaz. Moje tvrdenie v tomto výroku nie je, že ukazovateľ musí ukazovať na zásobník. Ukazovateľ je len premenná, ktorá uchováva adresu pamäte. Táto premenná sa nachádza na zásobníku. Keďže odkaz má svoj vlastný priestor na zásobníku a keďže adresa je rovnaká ako adresa premennej, na ktorú odkazuje. Viac informácií o zásobníku a halde. Z toho vyplýva, že existuje skutočná adresa referencie, ktorú vám kompilátor nepovie.
Int x = 0;
int &r = x;
int *p = &x;
int *p2 = &r;
assert(p == p2);
Môžete mať ukazovatele na ukazovatele na ukazovatele, ktoré ponúkajú ďalšie úrovne indirekcie. Zatiaľ čo odkazy ponúkajú len jednu úroveň indirekcie.
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);
Ukazovateľ môže byť priradený nullptr
priamo, zatiaľ čo odkaz nie. Ak sa dostatočne snažíte a viete ako, môžete z adresy odkazu urobiť nullptr
. Podobne, ak sa dostatočne snažíte, môžete mať referenciu na ukazovateľ, a potom táto referencia môže obsahovať nullptr
.
int *p = nullptr;
int &r = nullptr; <--- chyba kompilácie
int &r = *p; <--- pravdepodobne žiadna chyba kompilácie, najmä ak je nullptr skrytý za volaním funkcie, ale odkazuje na neexistujúci int na adrese 0
Ukazovatele môžu iterovať po poli, môžete použiť ++
na prechod na ďalší prvok, na ktorý ukazuje ukazovateľ, a + 4
na prechod na 5. prvok. Pritom nezáleží na veľkosti objektu, na ktorý ukazuje ukazovateľ.
Ukazovateľ je potrebné dereferencovať pomocou *
, aby sa získalo miesto v pamäti, na ktoré ukazuje, zatiaľ čo odkaz možno použiť priamo. Ukazovateľ na triedu/konštrukciu používa ->
na prístup k jej členom, zatiaľ čo odkaz používa .
.
Ukazovateľ je premenná, ktorá uchováva adresu pamäte. Bez ohľadu na to, ako je odkaz implementovaný, má odkaz rovnakú pamäťovú adresu ako položka, na ktorú odkazuje.
Referencie nemožno vložiť do poľa, zatiaľ čo ukazovatele možno (Spomenuté používateľom @litb)
Const referencie môžu byť viazané na temporary. Ukazovatele nemôžu (nie bez určitej indirekcie):
x = int(12); //legálne C++
int *y = &int(12); //nelegálne dereferencovať dočasný objekt.
Vďaka tomu je const&
bezpečnejšie pre použitie v zoznamoch argumentov a pod.
Okrem syntaktického cukru je odkaz ukazovateľ const
(nie ukazovateľ na const
). Pri deklarácii referenčnej premennej musíte určiť, na čo odkazuje, a neskôr to nemôžete zmeniť.
Aktualizácia: keď som sa nad tým teraz zamyslel, zistil som, že je tu dôležitý rozdiel.
Ukazovateľ const'target možno nahradiť tak, že vezmete jeho adresu a použijete const cast.
Referenčný cieľ sa nedá nahradiť žiadnym spôsobom, okrem UB.
To by malo kompilátoru umožniť väčšiu optimalizáciu referencie.