Funkcja zwracająca refernencje do lokalnego obiektu

0

Cześć
Mam pytanie jak działa kompilator w takim przypadku? Napisałem proty program aby sprawdzić czy tak mogę i generalnie wartość zwracana jest poprawnie, czy w takim momencie nie zostaje usuwany obiekt lokalny, czy może obiekt jest usuwany a w tym małym programie działa to poprawnie ponieważ ta część pamięci nie jest używana przez inna część programu?
Z góry dzięki za odpowiedź.

1

To 2.

0

A może kod czy mamy wróżyć na podstawie twojego enigmatycznego opisu?

0
#include <iostream>
using namespace std;
int& help(){
	int a=5;
	return a;
}
void help2(){
	int p=17;
}
int main(){
		int& b=help();
		cout<<b<<endl;
		help2();
		cout<<b<<endl;
} 

Tutaj bardzo dobrze widać zachowanie kompilatora w tej sytuacji.

1

g++ podczas kompilacji wyświetla warning w takiej sytuacji:

warning: reference to local variable ‘a’ returned

0

Jest to kod dozwolony, ale merytorycznie niepoprawny (kolejne miejsce w którym C++ jest zwalone: gdyby taka sytuacja była jawnie zabroniona, świat byłby piękniejszy).
Obiekt w momencie wyjścia z funkcji jest „niszczony” czy też „usuwany”, ale w sensie abstrakcyjnym: nie jest zerowana pamięć po nim, jest tylko wywoływany destruktor (którego w przypadku int

a nie ma). Zerowanie pamięci byłoby zbędnym spowalnianiem programu. Pamięć komputera jest ciągłym obszarem bajtów, które istnieją przez cały czas, i cały czas przyjmują jakieś wartości (choćby zera) i nie ma w niej pojęć typu „tu jest int”, „a tu nie ma inta bo został «usunięty»”. Te pojęcia istnieją tylko na poziomie języka programowania.
Obiekt lokalny więc fizycznie w pamięci pozostaje (w sensie: bajty, które zajmował, zachowują wartości), ale ten fragment pamięci może ulec (i ulegnie) wkrótce zamazaniu przez zmienne lokalne innych wywoływanych funkcji.
0

Nie wiem czy dozwalanie takiego zachowania jest aż tak dużą wadą C++. Kompilator ostrzega o tym nawet bez włączonych ostrzeżeń. W niektórych zastosowaniach może to być nawet przydatne (tak około 100 razy rzadziej niż goto, ale zawsze). O wiele gorsza jest zbliżona nienaturalna składnia porównania i przypisania, z czego wynika chyba najczęściej popełniany błąd w językach wywodzących się z C.

0

Jakoś nie widzę zastosowania dla referencji pokazującej w kosmos? Możesz podać przykład takiego zastosowania?

0

Wydaje mi się że w przypadku jednowątkowego programu, przy zwracaniu dużego obiektu, jeśli potrzebujemy np. tylko go wypisać, tzn zrobić:

A& funkcja()
{
  A lokalnyObiekt;
  //
  return lokalnyObiekt
}

//
cout<<funkcja();

I chcemy oszczędzić sobie czas potrzebny na kopiowanie tego obiektu. Jeśli program jest jednowątkowy to raczej możemy sie spodziewać że w chwili wywołania ten obiekt jeszcze sie tam znajduje. Gdy mamy więcej wątków to nie jest to juz takie oczywiste i nie można tak kombinować :)

0

W takich przypadkach lepiej zastosować shared_ptr

0

@Shalom: imho takie kombinowanie może się źle skończyć. Szczególnie jak np. za refaktoryzację takiego kodu zajmie się kolega 2 biurka dalej ;) Później znalezienie takiego błędu może nie być takie proste.

"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." (Martin Golding)

0

Ale ja doskonale zdaje sobie sprawę z tego że tak się robić nie powinno ;) Chciałem tylko pokazać że istnieją powody dla których ktoś moze chcieć tak zrobic ;)

0

A jak zapomnisz : w Delphi?
o co chodzi w Delphi? w Delphi przypisanie wewnątrz warunku jest niemożliwe, gdyż przypisanie nie zwraca przypisywanej wartości; nic nie zwraca – nie jest r-wartością.

1
Shalom napisał(a)

Wydaje mi się że w przypadku jednowątkowego programu, przy zwracaniu dużego obiektu, jeśli potrzebujemy np. tylko go wypisać, tzn zrobić:

A& funkcja()
{
  A lokalnyObiekt;
  //
  return lokalnyObiekt
}

//
cout<<funkcja();

I chcemy oszczędzić sobie czas potrzebny na kopiowanie tego obiektu. Jeśli program jest jednowątkowy to raczej możemy sie spodziewać że w chwili wywołania ten obiekt jeszcze sie tam znajduje. Gdy mamy więcej wątków to nie jest to juz takie oczywiste i nie można tak kombinować :)

Wiem, że to tylko taki przykład, ale powinieneś jednak dopisać, że to ma tylko szansę zadziałać, bo zwrócenie referencji do lokalnej zmiennej z funkcji oznacza, że ta referencja jest już po powrocie funkcji nieprawidłowa (zmienna lokalna po powrocie funkcji zostaje zniszczona). Czyli mamy niezdefiniowane zachowanie jeżeli spróbujemy jej użyć. Może i coś takiego jak napisałeś zadziała, a może nie:

#include <iostream>
#include <string>

std::string& foo() {
    std::string s = "Hello World!\n";
    return s;
}

int main() {
    std::cout << foo();
    
    return 0;
}

Powyższy kod na GCC 4.5.2 nie wyświetla mi niczego, a jeżeli spróbuję go debugować... To się dopiero jajca jak berety dzieją! :D W Visual C++ 2010 również niczego nie wyświetla, a debug daje Access Violation. ;)

Więc jeszcze raz, dla czytających, którym po tym poście Shalom zaświtał pomysł na sprytną optymalizację... Nie można liczyć na to, że taki kod w ogóle zadziała. ;)

Zwrócenie obiektu z funkcji jeszcze nie oznacza, że nastąpi kopia, kompilator może zastosować RVO (Return Value Optimization), możemy go też złapać w const referencję:

T bar() {
    T t;
    return t; 
}
//...
const T& foo = bar(); // tymczasowy obiekt zwrócony z bar() zostanie zniszczony dopiero kiedy referencja foo wyjdzie poza zasięg

Ale dopóki nie zmierzymy, że to daje rzeczywiście jakiś wymierny zysk w naszym programie, trzymałbym się bardziej "normalnych" zwyczajów. ;) (I nie, bez const nie można, tj przynajmniej póki co, bo w C++0x są już r-value references).

0

To teraz jest dobry moment na moje pytanie :D

/**************************************************************
 * A simpler and shorter implementation of ls(1)
 * ls(1) is very similar to the DIR command on DOS and Windows.
 **************************************************************/
#include <stdio.h>
#include <dirent.h>
 
int listdir(const char *path) {
  struct dirent *entry;
  DIR *dp;
 
  dp = opendir(path);
  if (dp == NULL) {
    perror("opendir");
    return -1;
  }
 
  while((entry = readdir(dp)))
    puts(entry->d_name);
 
  closedir(dp);
  return 0;
}
 
int main(int argc, char **argv) {
  int counter = 1;
 
  if (argc == 1)
        listdir(".");
 
  while (++counter <= argc) {
    printf("\nListing %s...\n", argv[counter-1]);
    listdir(argv[counter-1]);
  }
 
  return 0;
}

http://en.wikipedia.org/wiki/Dirent.h

Jak to jest, że od funkcji readdir() otrzymujemy wskaźnik na strukturę dirent, uzywamy jej i nie zwalniamy pamięci?
To samo entry->d_name. Przecież tam jest jakiś ciąg znaków, a my po nim nie sprzątamy

0

Podałeś link, ale widać nie czytałeś co tam jest napisane:

readdir napisał(a)

The memory location pointed to by the return value is managed by the library and may change on subsequent calls to readdir. It should not be free'd by the user.

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