Kiedy wektory są przydzielane, czy używają pamięci na stercie czy na stosie?

151

Czy wszystkie poniższe stwierdzenia są prawdziwe?

vector<Type> vect; //allocates vect on stack and each of the Type (using std::allocator) also will be on the stack

vector<Type> *vect = new vector<Type>; //allocates vect on heap and each of the Type will be allocated on stack

vector<Type*> vect; //vect will be on stack and Type* will be on heap. 

Jak jest wewnętrznie przydzielana pamięć Typew vectorlub innym kontenerze STL?

Phelodas
źródło
1
Możliwy duplikat członków klasy i wyraźny podział stosu / sterty
podkreślenie_d

Odpowiedzi:

222
vector<Type> vect;

alokuje vector, tj. informacje nagłówka, na stosie, ale elementy z wolnego magazynu („sterta”).

vector<Type> *vect = new vector<Type>;

przydziela wszystko w darmowym sklepie.

vector<Type*> vect;

przydzieli vectorna stosie i kilka wskaźników w wolnym sklepie, ale gdzie ten punkt jest określony przez sposób ich użycia (możesz wskazać element 0 na wolny magazyn, a element 1 na stos, powiedzmy).

Fred Foo
źródło
3
Ale mam scenariusz, w którym otrzymuję błąd segmentacji, gdy Type rośnie duży na drugiej deklaracji. Zakładałem, że dzieje się tak, ponieważ Type był przydzielany na stosie.
Phelodas
4
@Phelodas: bez zobaczenia kodu nie da się tego ocenić. Otwórz nowe pytanie.
Fred Foo
2
O tym, vector<Type> vect;że elementy są na stercie, a informacje nagłówka są na stosie, kiedy informacje nagłówka są usuwane z pamięci, na przykład funkcja powrotu funkcji, co się stanie z pamięcią elementów? Czy są odzyskiwane z informacjami w nagłówku, czy nie? Jeśli tak nie jest, czy spowoduje to wyciek pamięci?
flyrain
3
@flyrain: wektory czyszczą się po sobie. Przeczytaj o RAII .
Fred Foo
1
@flyrain: to powinno działać. Napisz nowe pytanie, podając więcej szczegółów. Jeśli zamieścisz tutaj link, może będę mógł go obejrzeć.
Fred Foo
25
vector<Type> vect; //allocates vect on stack and each of the Type (using std::allocator) also will be on the stack

Nie, vectbędzie na stosie, ale tablica, której używa wewnętrznie do przechowywania elementów, będzie na stercie. Elementy będą znajdować się w tej tablicy.

vector<Type> *vect = new vector<Type>; //allocates vect on heap and each of the Type will be allocated on stack

Nie. Tak samo jak powyżej, z tą różnicą, że vectorklasa również będzie na stercie.

vector<Type*> vect; //vect will be on stack and Type* will be on heap. 

vectbędzie na stosie, jego przedmioty (wskaźniki do Type) będą na stercie i nie możesz powiedzieć, gdzie będą te, na Typektóre wskazują wskaźniki. Może znajdować się na stosie, może znajdować się na stercie, może znajdować się w danych globalnych, może nigdzie nie być (tj. NULLWskaźników).

Przy okazji, implementacja mogłaby w rzeczywistości całkowicie przechowywać niektóre wektory (zazwyczaj o małym rozmiarze) na stosie. Nie żebym wiedział o takiej implementacji, ale tak.

jpalecek
źródło
23

Zakładając implementację, która faktycznie ma stos i stertę (standardowy C ++ nie wymaga posiadania takich rzeczy), jedyną prawdziwą instrukcją jest ostatnia.

vector<Type> vect;
//allocates vect on stack and each of the Type (using std::allocator) also will be on the stack

To prawda, z wyjątkiem ostatniej części ( Typenie będzie na stosie). Wyobrażać sobie:

  void foo(vector<Type>& vec) {
     // Can't be on stack - how would the stack "expand"
     // to make the extra space required between main and foo?
     vec.push_back(Type());
  }

  int main() {
    vector<Type> bar;
    foo(bar);
  }

Również:

 vector<Type> *vect = new vector<Type>; //allocates vect on heap and each of the Type will be allocated on stack

Prawda z wyjątkiem ostatniej części, z podobnym przykładem licznika:

  void foo(vector<Type> *vec) {
     // Can't be on stack - how would the stack "expand"
     // to make the extra space required between main and foo?
     vec->push_back(Type());
  }

  int main() {
    vector<Type> *bar = new vector<Type>;
    foo(bar);
  }

Dla:

vector<Type*> vect; //vect will be on stack and Type* will be on heap. 

to prawda, ale zauważ, że Type*wskaźniki będą na stercie, ale Typeinstancje, które wskazują, nie muszą być:

  int main() {
    vector<Type*> bar;
    Type foo;
    bar.push_back(&foo);
  }
Flexo
źródło
w jakim kontekście nie miałbyś stosu? Rozumiem, że mówisz, że standard tego nie wymaga, ale praktycznie mówiąc, kiedy byłbyś bez stacka?
Nerdtron,
3
@Nerdtron - IIRC na niektórych małych mikrokontrolerach masz stos wywołań, który nie może przechowywać nic innego niż komputer (licznik programu) w momencie ostatniego wywołania, gotowy do RET. Twój kompilator może więc zdecydować się na umieszczenie „automatycznego przechowywania” (dla funkcji nierekurencyjnych) w stałej lokalizacji z niewielkim / żadnym związkiem z przepływem wykonywania. To mogłoby całkiem sensownie spłaszczyć cały program. Nawet w przypadku rekurencji można mieć strategię „stos na funkcję” lub oddzielny stos dla zmiennych automatycznych i adresów zwrotnych, co sprawia, że ​​wyrażenie „stos” jest nieco bez znaczenia.
Flexo
Możesz użyć alokacji opartej na stercie dla wszystkiego i mieć „stos porządkowy”, który zarządza automatycznym magazynowaniem (i być może deletetakże).
Flexo
3

Tylko to stwierdzenie jest prawdziwe:

vector <Type*> vect; //vect will be on stack and Type* will be on heap.

Type* wskaźniki są przydzielane na stercie, ponieważ liczba wskaźników może zmieniać się dynamicznie.

vect w tym przypadku jest alokowany na stosie, ponieważ zdefiniowałeś go jako lokalną zmienną stosu.

Aleksiej Chlebnikow
źródło
2
Typ * nie wskazuje alokacji sterty, po prostu wskaźnik do obiektu Type. To powiedziawszy, wektor przechowuje wskaźnik na stercie. int a = 5; int * ptr_to_a = & a; wektor <int *> vec; vec.push_back (ptr_to_a); (patrz odpowiedź jpalecek)
Matthew Russell
1

wektor ma wewnętrzny, allocatorktóry jest odpowiedzialny za przydzielanie / zwalnianie pamięci heapdla vector element. Więc bez względu na to, jak utworzysz wektor, elementjest on zawsze przydzielany do heap. Jeśli chodzi o metadane wektora, zależy to od sposobu ich utworzenia.

Bingo
źródło