stos, sterta i inteligentne wskaźniki

0

Cześć ;) Czy decyzja o umieszczeniu danych na stosie lub stercie powinna zależeć od jakichś konkretnych czynników czy jest to wybór programisty? Już kilka razy natknąłem się na tekst gdzie pisało że w naprawdę zaawansowanych aplikacjach wręcz zabrania się dynamicznie alokować pamięć dlatego chciałbym poznać wasze zdanie na ten temat.
A jeśli już wskaźniki to wszystkie inteligentne?

0

Stos: krótki czas dostępu, mniejszy rozmiar, lokalny zakres zmiennych, brak konieczności zarządzania pamięcią
Sterta: długi czas dostępu, "globalny" zakres zmiennych, konieczność ręcznego zarządzania zasobami (ewentualnie RAII), fragmentacja pamięci

1

Jeżeli zamierzasz korzystać tylko ze wskaźników inteligentnych to przesiądź się na C#. Używając wskaźników inteligentnych można mieć wycieki pamięci, a kod wygląda okropnie.

0

@Satirev podstawowe różnice pomiędzy stosem i sterta to ja znam ;) Chodziło mi o bardziej "praktyczne" porównanie ;)
Ale nawet z tego prostego porównania wynika że w większości przypadków lepszym rozwiązaniem jest definiowanie obiektów / zmiennych w klasach na stosie a korzystając z wielu bibliotek widzę że te mimo wszystko jakby "promują" dynamiczne alokowanie danych.

Więc jeszcze raz - czym powinienem się kierować decydując się na stos / stertę? No i właśnie jak to jest z tymi inteligentnymi wskaźnikami, trzeba uważać żeby nie przesadzić z ich ilością? Czy jeżeli już używam wskaźnika to powinienem użyć inteligentnego czy to też zależy od sytuacji?

2

Pomijając sytuacje wyjątkowe (potwierdzone wynikami z profilera), powinieneś wszędzie trzymać się zasady zero: http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html

Inaczej mówiąc, jeśli tylko operujesz na danych, nie przejmując za nie odpowiedzialności, przekazywanie nagiego wskaźnika jest ok. W przeciwnym wypadku używaj inteligentnych wskaźników, w końcu RAII to jedna z największych zalet C++.

Jeśli chodzi o trzymanie danych na stosie/stercie, to domyślnie staram się wszystko pakować na stos, pamiętając jednak o:

  1. tym, że stos ma ograniczoną wielkość.
  2. tym, że dane ze stosu znikną¹ gdy wyjdę z obecnej funkcji, co jest istotne dla kodu bez jasnej hierarchii wywołań

¹ nawet jeśli mówimy o POD, które najprawdopodobniej zostaną nieruszone, zgodnie ze standardem odnoszenie się do takiej pamięci to UB.

2
  1. Jeżeli zależy Ci na wydajności alokacji - stos
  2. Jeżeli alokowane dane są na tyle duże, że mogą spowodować przepełnienie stosu - sterta.
  3. Jeżeli chcesz zmienić domyślną kolejność inicjalizacji pól w klasie - sterta (w zasadzie chodzi o operator new - nie ma znaczenia czy fizycznie wylądują na stosie czy na stercie).
  4. Jeżeli chcesz przekazać dane poza zakres w którym były tworzone (np. chcesz zwrócić duży obiekt z funkcji) - sterta

No i właśnie jak to jest z tymi inteligentnymi wskaźnikami, trzeba uważać żeby nie przesadzić z ich ilością

Inteligentne wskaźniki ze zliczaniem referencji (np. std::shared_ptr) mogą potencjalnie powodować problemy z wydajnością jeżeli są nadużywane:

  1. Inteligentne wskaźniki alokują dodatkowe miejsce dla licznika referencji na stercie (czyli dodatkowy new)
  2. Licznik referencji jest inkrementowany i dekrementowany atomowo co powoduję synchronizacje cache'ow na procesorze (dosyć czasochłonna operacja).
1

A jakie są różnice między stosem a stertą?

Stos jest bezpieczniejszy, ale można go symulować przez RAII i inteligentne wskaźniki.

Jak kontrolować czy obiekt może być alokowany dynamicznie?
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Requiring_or_Prohibiting_Heap-based_Objects

Czy w zaawansowanych aplikacjach unika się alokacji na stercie?
Ktoś kogo teraz nie pomnę w magazynie "Programista" opisał jakiś czas temu ciekawą strategię - może jest na tym forum.

Zamiast alokowania:

class MojaKlasa {
  int kolor;
  double zapach;
};

typedef std::vector<MojaKlasa> MojVector;

lub nawet

typedef std::vector<MojaKlasa *> MojVector;

lepiej jest zrobic:

class MojVector {
  std::vector<int> kolory;
  std::vector<double> zapachy;
  // funkcje dostepowe
};

Ogólnie chodzi o to żeby zamiast alokować tysiące malutkich obiekcików lepiej jest alokować wektory ich właściwości (cache).

2

Czy w zaawansowanych aplikacjach unika się alokacji na stercie?

Sterta jest niedeterministyczna, i po wielu dużych alokacjach pojawia się problem z fragmentacją.
Dlatego unika się alokacji w zastosowaniach krytycznych, gdzie wywalenie aplikacji skutkuje śmiercią albo katastrofą.

0

to pozwolę sobie skorzystać z okazji i zapytać. tablica z 30 ścieżkami do plików, które ustalam w trakcie działania programu.
obecnie alokuje na stertę - a może lepiej opłaca mi się alokować na stos tj. tablica[30][4096]; ?

0

nie ma się co zastanawiać.

string tablica[30];

jeśli na pewno będzie ich zawsze 30, albo

vector<string> tablica;

jeśli jednak nie 30.

1
Azarien napisał(a):
string tablica[30];

jeśli na pewno będzie ich zawsze 30

Albo

std::array<std::string, 30> array;

chociaż to może nie być takie fajne, jak się wydaje. Takiej tablicy nie powinno się przekazywać (będzie kopiowana!), powinno się przekazywać iteratory - iteratory to C++ way. ;-)

0

Jeżeli chcesz przekazać dane poza zakres w którym były tworzone (np. chcesz zwrócić duży obiekt z funkcji) - sterta

Rozumiem że w tym wypadku sterta po to aby uniknąć dodatkowego kopiowania przy zwracaniu obiektu zadeklarowanego w funkcji na stosie?

I jeszcze jedno pytanie a mianowicie jeśli zmienne / inne obiekty zawierane w głównej klasie zadeklaruje na stosie a główną klasę na stercie to dane wylądują na stercie? I odwrotnie - w klasie zmienne wywołuje z operatorem new a ta klasę później w innej klasie już wywołuje na stosie i gdzie wtedy lądują dane?
Według mnie najłatwiej byłoby wszystkie zmienne i obiekty których w danej klasie używam deklarować na stosie

class Utility
{
private:
    std::string property;
    My_class custom;
    // ....
};

problem zasięgu w danej klasie znika, gdy potrzebuje coś przekazać poza klasę to mam wybór - referencja albo kopia ale przy tym korzystam ze wszystkich plusów stosu które już wyżej zostały podane. Przynajmniej tak mi się w tej chwili wydaje ;)

1

Jeśli obiekt klasy będzie na stercie to wszystkie jego pola będą na stercie, to dość oczywiste i tak jest zawsze. Pola obiektu zawsze będą tam gdzie obiekt.
Jeśli obiekt alokuje pamięć przy pomocy new/malloc to pola obiektu nadal będą tam gdzie obiekt, ale zaalokowany obszar zawsze będzie na stercie.

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