Почему элементы в куче не освобождаются после функции? - PullRequest
0 голосов
/ 10 апреля 2020

Кто-то сказал мне, что 'Да. node* new_node = new node; выделяет узел в куче, а node new_node; внутри функции выделяет узел в стеке. Если узлы указывают друг на друга, это все еще связанный список. Остерегайтесь, хотя, что выделенные стеки вещи автоматически освобождаются, когда функция заканчивается. Вот почему удобнее размещать в куче. '

Что это значит? Кто-нибудь может уточнить?

Ответы [ 2 ]

1 голос
/ 10 апреля 2020

Ответ looong.

Automati c длительность хранения

Переменные "стека" (более точно известные как сущности с automati c длительность хранения) уничтожается, как только вы покидаете область, в которой они были объявлены. (То есть они «очищаются» и их память освобождается)

void my_function() {
  node node1;
  if (1 == 1) {
      node node2;
      node* node3_ptr = new node; // this node is *not* cleaned up automatically
  } // node2 is destructed now
  node node4;
} // node1 and node4 are destructed now

В приведенном выше коде node1 и node4 объявляются в разных частях внешней области видимости функции. Они будут «go прочь», когда функции завершатся.

Не имеет значения, выполняется ли функция до конца, рано ли возвращается, выдает исключение - если функция заканчивается, C ++ гарантирует, что они будут уничтожены. (Приложение, завершающее мертвую на своих треках, отличается.)

node2 объявлено внутри блока if. Он будет уничтожен, когда код выйдет из блока if - даже до завершения функции.

Это гарантированное автоматическое c уничтожение этих переменных в совершенно предсказуемое время - одна из самых сильных сторон C ++. Он называется «детерминированность c уничтожение» и является одной из причин, по которой C ++ является моим предпочтительным языком.

Dynami c длительность хранения

«Куча» переменных (то есть сущностей с «dynamici *»). 1087 * «место хранения» сложнее.

void my_leaky_function() {
  node* node5;

  new node;

  node* node6 = new node;
}

node5 по-прежнему просто локальная переменная. Тот факт, что он имеет тип «указатель на узел», а не просто «узел», не имеет значения. Это переменная с автоматическим c продолжительностью, которая использует память. Его размер - это размер указателя (вероятно, 4 или 8 байт - зависит от вашей платформы), а не размер узла. Эта переменная «уходит», и ее память восстанавливается после завершения функции.

new node; выделяет память в «свободном хранилище» (в разговорной речи называется «куча»). new возвращает указатель на выделенную память, но этот код игнорирует указатель. Здесь не задействованы локальные переменные, и узел не уничтожается при завершении функции;

node* node6 = new node; также выделяет достаточно места для объекта узла в свободном хранилище - но на этот раз указатель, возвращаемый new, хранится в локальной переменной с именем node6. NB: node6 является локальной переменной (которая хранит указатель, а не узел) и имеет автоматическую c продолжительность хранения. Переменная node6 исчезает (и несколько байтов используемой памяти освобождаются), когда функция завершается. НО узел, на который тоже указывал node6 - узел, который хранится в бесплатном хранилище - не уничтожен.

Когда эта функция заканчивается, она оставила два узла в бесплатном хранилище - и поскольку он отбросил указатели на каждый из них, никто не может удалить их. У него «утечка памяти».

Зачем использовать хранилище Dynami c?

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

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

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

tl; dr;

То есть

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

, тогда свободный магазин - подходящее место для него.

Оооочень много go касается того, кто «владеет» значением и отвечает за его удаление - и использование умных указателей, а не сырых указателей - и исключений безопасности - и и и и и - но этот ответ уже больше, чем я хотел. Итак, позвольте мне закончить с этим:

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

Как только вы поймете указатели и динамическое хранилище c - как только вы поймете, как написать свой собственный связанный список или двоичное дерево с нуля - STOP ИСПОЛЬЗУЯ ИХ. В повседневном кодировании, если вы не являетесь экспертом в написании кода для библиотеки контейнеров, никогда не используйте new или delete снова - никогда. Используйте умные указатели, когда это абсолютно необходимо, но старайтесь избегать даже их.

Положитесь на автоматическую c длительность хранения, когда сможете. Deterministi c разрушение ваш друг. Это то, что отличает C ++ от C. Это то, что отличает C ++ от мусорных языков. Именно поэтому C ++ по-прежнему является одним из королей языков программирования.

0 голосов
/ 10 апреля 2020

У вас все еще могут быть указатели, ссылающиеся на тот же объект в предыдущих вызовах методов.

...