Czy poniższy kod (func1 ()) jest poprawny, jeśli ma zwrócić i? Pamiętam, że czytałem gdzieś, że jest problem podczas zwracania odwołania do zmiennej lokalnej. Czym się różni od func2 ()?
int& func1()
{
int i;
i = 1;
return i;
}
int* func2()
{
int* p;
p = new int;
*p = 1;
return p;
}
int& i = * new int;
Odpowiedzi:
Ten fragment kodu:
nie zadziała, ponieważ zwracasz alias (odwołanie) do obiektu z okresem istnienia ograniczonym do zakresu wywołania funkcji. Oznacza to, że po
func1()
powrocieint i
umiera, sprawiając, że odwołanie zwrócone z funkcji jest bezwartościowe, ponieważ teraz odwołuje się do obiektu, który nie istnieje.Druga wersja działa, ponieważ zmienna jest przydzielona do wolnego magazynu, który nie jest powiązany z okresem istnienia wywołania funkcji. Jednak jesteś odpowiedzialny za
delete
przydzieloneint
.Zwykle owinąłbyś wskaźnik w jakąś klasę RAII i / lub funkcję fabryczną, więc sam nie musisz tego robić
delete
.W obu przypadkach możesz po prostu zwrócić samą wartość (chociaż zdaję sobie sprawę, że przykład, który podałeś, został prawdopodobnie wymyślony):
Zwróć uwagę, że zwracanie dużych obiektów w ten sam sposób, w jaki
func3()
zwraca się wartości pierwotne, jest całkowicie w porządku, ponieważ obecnie prawie każdy kompilator implementuje jakąś formę optymalizacji wartości zwracanych :Co ciekawe, wiązanie tymczasowe z odwołaniem do const jest całkowicie legalnym C ++ .
źródło
int* p = func2(); delete p;
Teraz, kiedy usuwałeś 'p', czy to oznacza, że pamięć przydzielona "wewnątrz"func2()
definicji funkcji również została usunięta?func2()
i zwolniona na zewnątrz w następnej linii. Jest to jednak raczej podatny na błędy sposób obsługi pamięci, tak jak powiedziałem, zamiast tego używałbyś jakiegoś wariantu RAII. Nawiasem mówiąc, brzmisz, jakbyś się uczył C ++. Polecam skorzystać z dobrej książki wprowadzającej do języka C ++, z której można się uczyć. Ponadto, jeśli masz jakieś pytania, możesz je zawsze opublikować na stronie Stack Overflow. Komentarze nie służą do zadawania zupełnie nowych pytań.Zmienna lokalna to pamięć na stosie, która nie jest automatycznie unieważniana, gdy wyjdziesz poza zakres. Z funkcji zagnieżdżonej głębiej (wyżej na stosie w pamięci), dostęp do tej pamięci jest całkowicie bezpieczny.
Gdy funkcja powróci i zakończy się, sytuacja stanie się niebezpieczna. Zwykle pamięć nie jest usuwana ani nadpisywana po powrocie, co oznacza, że pamięć pod tymi adresami nadal zawiera dane - wskaźnik wydaje się prawidłowy.
Dopóki inna funkcja nie utworzy stosu i nie nadpisuje go. Dlatego może to działać przez chwilę - a potem nagle przestaje działać po tym, jak jeden szczególnie głęboko zagnieżdżony zestaw funkcji lub funkcja o naprawdę dużych rozmiarach lub wielu lokalnych obiektach ponownie osiągnie tę pamięć stosu.
Może się nawet zdarzyć, że ponownie dotrzesz do tej samej części programu i nadpiszesz swoją starą lokalną zmienną funkcji nową zmienną funkcji. Wszystko to jest bardzo niebezpieczne i należy go mocno odradzać. Nie używaj wskaźników do lokalnych obiektów!
źródło
Warto zapamiętać te proste zasady, które dotyczą zarówno parametrów, jak i typów zwracanych ...
Dla każdego jest czas i miejsce, więc upewnij się, że je znasz. Zmienne lokalne, jak pokazałeś tutaj, są po prostu ograniczone do czasu, w którym żyją lokalnie w zakresie funkcji. W twoim przykładzie zwracanie
int*
i zwracanie typu&i
byłoby równie niepoprawne. W takim przypadku byłoby lepiej, gdybyś zrobił to ...Spowoduje to bezpośrednią zmianę wartości przekazanego parametru. Podczas gdy ten kod ...
nie. Po prostu zmieniłby wartość
oValue
lokalnego wywołania funkcji. Powodem tego jest fakt, że w rzeczywistości zmieniasz tylko „lokalną” kopięoValue
, a nieoValue
siebie.źródło