Когда лучше использовать стек вместо кучи и наоборот?

В С++, когда лучше всего использовать стек? Когда лучше использовать кучу?

4b9b3361

Используйте стек, когда ваша переменная не будет использоваться после возвращения текущей функции. Используйте кучу, когда данные в переменной необходимы за время жизни текущей функции.

49
ответ дан 19 сент. '08 в 17:01
источник

Как правило, избегайте создания огромных объектов в стеке.

  • Создание объекта в стеке освобождает вас от бремени запоминания для очистки (чтения) объекта. Но создание слишком большого количества объектов в стеке увеличивает вероятность.
  • Если вы используете кучу для объекта, вы получаете столько памяти, которую может предоставить ОС, намного больше, чем стек, но затем вы должны быть уверены, что освободите память, когда закончите. Кроме того, слишком много слишком много объектов в куче будет иметь тенденцию фрагментировать память, что, в свою очередь, будет влиять на производительность вашего приложения.
26
ответ дан 19 сент. '08 в 17:04
источник

Используйте стек, когда используемая память строго ограничена областью, в которой вы ее создаете. Это полезно, чтобы избежать утечек памяти, потому что вы точно знаете, где вы хотите использовать память, и знаете, когда она вам больше не нужна, поэтому память будет очищена для вас.

int main()
{ 
   if (...)
   {
      int i = 0;
   }
   // I know that i is no longer needed here, so declaring i in the above block 
   // limits the scope appropriately
}

Однако куча полезна, когда к вашей памяти можно получить доступ за пределами области ее создания, и вы не хотите копировать переменную стека. Это может дать вам четкий контроль над распределением и освобождением памяти.

Object* CreateObject();

int main()
{
    Object* obj = CreateObject();
    // I can continue to manipulate object and I decide when I'm done with it

    // ..
    // I'm done
    delete obj;
    // .. keep going if you wish
    return 0;
}

Object* CreateObject()
{
   Object* returnValue = new Object();
   // ... do a bunch of stuff to returnValue
   return returnValue;
   // Note the object created via new here doesn't go away, its passed back using 
   // a pointer
}

Очевидно, что общая проблема заключается в том, что вы можете забыть удалить свой объект. Это называется утечкой памяти. Эти проблемы более распространены, так как ваша программа становится все менее и менее тривиальной, когда определение "собственности" (или кто именно отвечает за удаление вещей) становится более трудным для определения.

Общие решения в более управляемых языках (С#, Java) предназначены для реализации мусорной коллекции, поэтому вам не нужно думать об удалении вещей. Однако это означает, что в фоновом режиме есть что-то, что выполняется апериодически для проверки ваших данных кучи. В нетривиальной программе это может стать довольно неэффективным, поскольку поток "сбор мусора" всплывает и прошивается, ища данные, которые следует удалить, в то время как остальная часть вашей программы блокируется от выполнения.

В С++ наиболее распространенным и лучшим (на мой взгляд) решением для устранения утечек памяти является использование умного указателя. Наиболее распространенным из них является boost:: shared_ptr, который (ссылка подсчитана)

Итак, чтобы воссоздать приведенный выше пример  boost:: shared_ptr CreateObject();

int main()
{
    boost::shared_ptr<Object> obj = CreateObject();
    // I can continue to manipulate object and I decide when I'm done with it

    // ..
    // I'm done, manually delete
    obj.reset(NULL);
    // .. keep going if you wish
    // here, if you forget to delete obj, the shared_ptr destructor will note
    // that if no other shared_ptr point to this memory 
    // it will automatically get deleted.
    return 0;
}

boost::shared_ptr<Object> CreateObject()
{
   boost::shared_ptr<Object> returnValue(new Object());
   // ... do a bunch of stuff to returnValue
   return returnValue;
   // Note the object created via new here doesn't go away, its passed back to 
   // the receiving shared_ptr, shared_ptr knows that another reference exists
   // to this memory, so it shouldn't delete the memory
}
12
ответ дан 19 сент. '08 в 17:14
источник

Исключением из упомянутого выше правила является то, что вы обычно должны использовать стек для локальных переменных, которые не нужны вне области действия функции:

Рекурсивные функции могут исчерпывать пространство стека, если они выделяют большие локальные переменные или если они рекурсивно вызывают много раз. Если у вас есть рекурсивная функция, использующая память, может быть хорошей идеей использовать память на основе кучи вместо памяти на основе стека.

8
ответ дан 19 сент. '08 в 18:21
источник

как правило, используйте стек, когда можете. т.е. когда переменная никогда не нужна вне этой области.

он быстрее, уменьшает фрагментацию и избегает других накладных расходов, связанных с вызовом malloc или new. выделение стека - это пара ассемблерных операций, malloc или new - несколько сотен строк кода в эффективной реализации.

его никогда не лучше использовать кучу... просто неизбежно.:)

3
ответ дан 19 сент. '08 в 17:20
источник

Используйте кучу только для выделения пространства для объектов во время выполнения. Если вы знаете размер во время компиляции, используйте стек. Вместо того, чтобы возвращать выделенные кучей объекты из функции, передайте буфер в функцию для записи. Таким образом, буфер может быть назначен там, где функция вызывается как массив или другая структура на основе стека.

Чем меньше операторов malloc() у вас есть, тем меньше шансов на утечку памяти.

3
ответ дан 19 сент. '08 в 17:00
источник

Вопрос плохо сформирован.

Есть ситуации, когда вам нужен стек, другие, где вам нужна куча, другие, где вам нужно статическое хранилище, другие, где вам нужны данные постоянной памяти, другие, где вам нужен бесплатный магазин.

Стек выполняется быстро, поскольку распределение - это просто "приращение" по SP, и все "распределение" выполняется во время вызова функции, в которой вы находитесь. Распределение/освобождение кучи (или бесплатное хранилище) более дорогое время и склонность к ошибкам.

0
ответ дан 19 сент. '08 в 17:04
источник