Zwracanie referencji przez funkcję

0

Witam. Mam problem ze zrozumieniem zwracania referencji przez funkcje. Problem był już kiedyś poruszany na forum, jednak nadal nie mogę tego zrozumieć. xd
Mamy taki prosty program, w którym przez referencje chcemy przekazać do funkcji liczbę i zwrócić kwadrat tej liczby.


#include <iostream>
#include <string>

using namespace std;

int & wynik(int & a);

int main()
{
    int liczba = 4;

    int & rf = liczba;

    cout<<"Liczba = "<<liczba<<endl;
    cout<<"Wyslanie liczby przez referencje do funkcji i zwrocenie przez referencje jej kwadratu.\n"<<endl;

    rf = wynik(rf);

    cout<<"Teraz zmienna liczba = "<<liczba<<endl;

    return 0;
}

int & wynik(int & a)
{
    int w=0;

    w = a*a;
    return w; //nie zadziala
}

Tutaj póki co chyba wszystko rozumiem. Program nie działa, bo funkcja ma zwrócić referencje, ale przez return zwracamy zmienną lokalną w, która przestaje istnieć w momencie zakończenie funkcji, więc program się wiesza, bo po powrocie do funkcji main próbuje się odwołać do obszaru pamięci, którego już nie ma. Dobrze myślę? Poprawna wersja tej funkcji to:


int & wynik(int & a)
{
    a *= a;
    return a;

}

Tutaj wszystko zadziała, a wynik to będzie 16. Tylko nie wiem co się dzieje z tą zmienną a, w momencie zakończenia działania funkcji wynik i powrotu do main. Przecież po zakończeniu działania funkcji wynik wszystkie zmienne w niej są "niszczone", to dlaczego w przypadku przypisania wyniku do zmiennej lokalnej i zwróceniu jej, program przerywa działanie, ale w przypadku zwrócenia referencji z parametru funkcji już wszystko działa normalnie? Gdzieś czytałem, że w przypadku zwracania referencji program tworzy jakąś zmienną tymczasową/anonimową, do której przypisywana jest ta wartość, a następnie jest ona przypisywana do zmiennej rf w main? Czy chodzi o to, że druga wersja funkcji zadziałała, bo zwróciliśmy referencje która jest parametrem w funkcji?

Może opisałem mój problem trochę chaotycznie, ale czy może mi ktoś wytłumaczyć, na jakiej zasadzie działa to zwracanie referencji przez funkcje?

0

Zmodyfikowałem kod jeszcze tak:

int & wynik(int & a)
{
    a *= a;    
    int & b = a;
    return b;

}

W takim przypadku program też zadziała. Czyli mam rozumieć, że jak funkcja zwraca referencje, to wartością przekazaną do return musi być inna referencja? Tylko dlaczego po zakończeniu działania funkcji, funkcja main otrzymuje wyliczoną wartość, a w przypadku zwykłej zmiennej lokalnej (tak, jak pokazałem w kodzie pierwszym), program przerywa działanie, bo nie może znaleźć tej zmiennej w pamięci?

edit: Jeszcze jedno. Po wprowadzeniu do funkcji main i wynik couta, który wyświetlił mi adres zmiennej 'rf' z main i zmiennej 'a' z wynik, okazało się, że obie zmienne mają ten sam adres w pamięci. Czyli mam rozumieć, że pomimo zakończenia działania funkcji wynik, zmienne referencyjna którą zwróciłem przez wynik, nadal istnieje w pamięci?

1

Sam sobie Odpowiedziałeś na pytanie z postu pierwszego, nie "dotknąłeś" nawet przyjętej referencji, tylko Zwróciłeś zmienną lokalną - wiadomo: lipa. A w drugim przypadku i również w "drugim" drugim, Przyjąłeś referencję (czyli adres) Zmodyfikowałeś go i Zwróciłeś, OK.

0
lion137 napisał(a):

Sam sobie Odpowiedziałeś na pytanie z postu pierwszego, nie "dotknąłeś" nawet przyjętej referencji, tylko Zwróciłeś zmienną lokalną - wiadomo: lipa. A w drugim przypadku i również w "drugim" drugim, Przyjąłeś referencję (czyli adres) Zmodyfikowałeś go i Zwróciłeś, OK.

No dobra, ale przecież zwracamy referencje. Referencja to jest alias do innej zmiennej (można też powiedzieć, że to też taki "uproszczony" wskaźnik). W przypadku zwrócenia zmiennej 'w' w pierwszym listingu, program jest przerywany, bo zwrócona referencja (czy alias do zmiennej w) próbuje odwołać się do nie istniejącej zmiennej. W przypadku zwrócenia zmiennej referencyjnej wszystko działa, czyli mam rozumieć, że nawet po zakończeniu funkcji, ta zmienna referencyjna z funkcji nadal istnieje, i odwołuje się do niej zmienna referencyjna rf (bo wynik działania funkcji, czyli zwrócona referencja, został przypisany do rf)?

0

Nie robisz aliasu do "zmiennej referencyjnej" bo referencja to alias. Robisz alias do oryginalnej zmiennej. Tej z funkcji main.

0
kq napisał(a):

Nie robisz aliasu do "zmiennej referencyjnej" bo referencja to alias. Robisz alias do oryginalnej zmiennej. Tej z funkcji main.

No ok, ale dalej nie wiem, dlaczego w przypadku zwrócenia "nie referencji" (tak jak w listingu 1 zwracam przez return zmienna w) program nie działa, ale w przypadku zwrócenia przez return referencji już tak. Mam rozumieć, że nawet po zakończeniu działania funkcji, ta zmienna nadal istnieje w pamięci?

2
int& foo(int& bar)
{
    return bar;
}

int main()
{
    int baz;
    int& qux = foo(baz);
}

bar jest referencją do baz
referencja zwrócona przez foo jest referencją do wartości do której odnosi się bar, czyli jest referencją do baz z main
qux jest referencją do wartości do której odnosi się zwrócona z foo referencja, czyli jest referencją do baz z main

Jak widzisz nie ma tu problemu z czasem życia zmiennej, bo żyje ona w funkcji wywołującej. W przeciwieństwie do zwracania referencji do zmiennej lokalnej.

0
kq napisał(a):

bar jest referencją do baz
referencja zwrócona przez foo jest referencją do wartości do której odnosi się bar, czyli jest referencją do baz z main
qux jest referencją do wartości do której odnosi się zwrócona z foo referencja, czyli jest referencją do baz z main

Jak widzisz nie ma tu problemu z czasem życia zmiennej, bo żyje ona w funkcji wywołującej. W przeciwieństwie do zwracania referencji do zmiennej lokalnej.

Ok, już rozumiem. To teraz jeszcze lekko zmodyfikuje Twój listing:


#include <iostream>
#include <string>

int& foo(int& bar)
{
    int & zip = bar;
    return zip;
}

int main()
{
    int baz;
    int& qux = foo(baz);
}


W takim przypadku program też się skompiluje i nie przerwie działania. Możesz mi wytłumaczyć tutaj zasadę działania przez referencje? Bo nie zwracamy referencji, którą przekazaliśmy jako argument funkcji, tylko całkowicie nową referencje, którą zadeklarowaliśmy w funkcji foo. W pierwszym poście pod postem głównym zrobiłem w zasadzie podobnie, tworząc referencje 'b' i zwracając ją i też wszystko zadziałało.

2

Traktuj referencje tak jakby nie istniały. To cały czas jest efektywnie przekazana zmienna.

1

Bracie @kario97
Traktuj referencje tak, jak traktujesz wskaźniki (referencja to z technicznego punktu widzenia stały wskaźnik na daną) - pomoże ci to zrozumieć sytuację. I tak, patrząc na twój ostatni post:

kario97 napisał(a):

(...)Możesz mi wytłumaczyć tutaj zasadę działania przez referencje? Bo nie zwracamy referencji, którą przekazaliśmy jako argument funkcji, tylko całkowicie nową referencje, którą zadeklarowaliśmy w funkcji foo. W pierwszym poście pod postem głównym zrobiłem w zasadzie podobnie, tworząc referencje 'b' i zwracając ją i też wszystko zadziałało.

Jeśli zgodnie z radą wyobrazisz sobie, że zamiast referencji kod operuje na wskaźnikach:

int* foo(int *bar)
{
    int *zip = bar;// (2) utworzenie wskaźnika zip który pokazuje to na to samo, co bar - czyli na baz z main-a
    return zip;// (3) zwrócenie zip w którym jest adres jaki przechowuje bar z punktu (2)
}
 
int main()
{
    int baz;
    int *qux = foo(&baz);// (1) wywołanie funkcji pobiera adres na baz
    //(4) do qux trafia to, co zwraca funkcja foo(), a jest to adres przechowywany we wskaźniku zip, który to wskaźnik zawiera przesłany przez parametr foo() adres zmiennej baz
}

jasnym staje się, skąd się co bierze, i dlaczego do qux trafia adres zmiennej baz zdefiniowanej w main.
Referencje po prostu pozwalają posługiwać się bezpiecznym wskaźnikiem bez całej tej niewygody związanej z używaniem *.

1 użytkowników online, w tym zalogowanych: 0, gości: 1