T* foo(/* ... /)
T& bar(/ ... */)
Różnic jest więcej.
foo
może zwrócić nullptr
, co zwykle (praaawie zawsze) traktujesz jako "sentinel value", czyli jako wartość sygnalizującą, że foo
nie mogło zwrócić prawidłowego adresu. Jeżeli foo
zwróci prawidłowy adres - może to być adres:
- na jakąś pojedynczą wartość,
- na wartości (
char*
na c-string, jakaś inna kolekcja z wartością sygnalizującą, albo tablica, gdzie jej rozmiar dostajesz na przykład przez parametr)
- przesunięty o jeden element poza pojedynczą wartość lub koniec tablicy. Na takim wskaźniku nie powinieneś robić dereferencji, a dokumentacja powinna Ci powiedzieć, że dostajesz taki specyficzny adres. Jeżeli nie wiesz dlaczego tak mogłoby się stać - pomyśl o standardowych iteratorach w C++.
Adres zwrócony może pokazywać na pamięć, która jest zarządzana przez kogoś innego w innym miejscu. Jest też możliwość, że zwróci pamięć zaalokowaną przez malloc/calloc/realloc
albo new/new[]
i przekazuje Ci jej własność - w takim przypadku powinieneś taką pamięć zwolnić za pomocą odpowiednio free()
lub delete/delete[]
. Dokumentacja takiej funkcji powinna to wyłuszczyć.
bar
zwróci referencję do jakiegoś obiektu - czyli bar
zawsze jakąś wartość T zwróci, w odróżnieniu od foo
. Nie musisz się martwić zarządzaniem pamięci - ktoś się już tym zajmuje.*
Oczywiście, w obydwu przypadkach jeżeli funkcja zwróci adres/referencję zmiennej lokalnej, bum.
Czy przesyłając do funkcji wskaźnik do jakiegoś obiektu tworzymy kopię takiego adresu i na podstawie kopii możemy zmienić obiekt
W zasadzie tak. Jeśli nie przesyłasz czegoś przez referencję, to zawsze przesyłasz to przez kopię - wskaźniki również. Możesz mieć do pierona wskaźników (T*
) z adresem na Twój obiekt i dereferencja każdego z tych wskaźników pozwoli Ci na zmianę tego obiektu.
a przesyłając referencję przesyłamy adres oryginalnego obiektu i na tym obiekcie działamy?
E, i tak i nie. Bardziej na nie, ale co "pod spodem" kompilator zrobi to inna sprawa.
Mógłbyś myśleć o referencji jak o "self-dereferencing const pointer to T", problem w tym, że biorąc "adres referencji" dostajesz adres obiektu, a nie tego wyimaginowanego "stałego wskaźnika" - bo i żadnego wskaźnika może tak naprawdę nie być. Nie ma referencji na referencje, ani wskaźników na referencje (ale są wskaźniki na wskaźniki). Referencja to w zasadzie alias, coś jak "przezwisko" jak to xfin napisał.
Referencja to nie obiekt, może zajmować miejsce w pamięci, lub nie. (A to ci efemeryczna bestia :P) Łapiąc obiekt przez referencję w funkcji dostajesz ten właśnie obiekt, ale - znowu - referencja to nie obiekt, więc można zwrócić referencję do lokalnej zmiennej, która to referencja jest nieprawidłowa po powrocie z funkcji. (google "dangling reference", "dangling pointer").
Dziwne? A jednak nie takie rzeczy się fizjonomom... ;)
*) troszkę się krzywię to pisząc... :P