@Noortvel

Управление памятью в С++?

Изучаю С++.Решил "быстро" перескочить с Java на С++, но никак не могу понять как управлять памятью.Я знаю, что в С++ нету сборщика мусора, но что такое мусор?(Гугл выдает, что управление памятью это указатели, но если я сделаю так (см ниже), то удалится только указатель, но не сама переменная?То как тогда удалить переменную?)
int a = 3;
int *p = 0;
p = &a;
delete p;
  • Вопрос задан
  • 1499 просмотров
Решения вопроса 4
petermzg
@petermzg
Самый лучший программист
Понятия "мусор" в С++ нет, так как не должно быть безхозных обьектов, если такие появляются, это называется "утечкой памяти".

В С++ бывает 2 типа использования памяти.
1. На стеке. Такую память по особому выделять не нужно и освобождать тоже.
Пример:
int a = 3;
2. В "куче". В этом случае уже вы отвечаете за выделение и освобождение памяти.
Работает через оператор new. Или функции malloc, calloc и т.п.
int* p = new int;
delete p;
Ответ написан
Комментировать
@MiiNiPaa
Быстро перескочить не получится. Сначала придётся разобраться с моделью памяти в С++, разницу между автоматической, статической и динамической памятью...

Конкретно по коду: удалять ничего не надо, переменная a будет уничтожена автоматически, как только выйдет из области видимости.
Ответ написан
Комментировать
@isotoxin
Высокоуровневые языки со сборщиками мусора, конечно, просты в использовании, но не дают понять сути происходящего. В этом их минус. А суть в работе с памятью такова:
Любой объект ненулевой длины, любая переменная - всё требует место в памяти. Это в любом языке. Это фундамент мироздания.
Вопрос только в том, как язык эту память выделяет или дает выделять программисту.
Когда вы объявляете глобальную переменную, память под нее выделяется при старте программы. Это делается операционной системой в процессе загрузки программы. Управлять этим процессом можно, но новичку лучше пока голову не забивать. Далее. Переменная внутри функции. Память под такую переменную выделяется на стеке в момент вызова функции и освобождается по завершении функции. Тут тоже программист особо на этот процесс не влияет, хотя и может. Есть понимание, что такое стек? На всякий случай вкратце - это специальный участок памяти приложения (обычно 4 мегабайта на поток) как раз для переменных внутри функций. Т.о. если рекурсивная функция вошла в бесконечную рекурсию, она очень быстро выжирает всю память стека и приложение падает.

Все что было до этого, характерно для большинства языков, в том числе и для Java.

Наконец, самое сложное - переменные и данные, под которые программист явно запрашивает память.
В java есть оператор new. В C++ он тоже есть. Они делают одно и тоже - выделяют где-то там (пока что не важно где) место под объект и инициализируют этот объект. А теперь о различии. Это основное отличие языка C++ от языков со сборщиком мусора. В C++ вы должны созданный таким образом объект явно удалить оператором delete. Если вы этого не сделаете, никто за вас это не сделает. Если этого не делать вообще, то приложение просто выжрет всю память и в итоге упадет. В Java вы просто оставляете объект на произвол судьбы и сборщик мусора сам удалит его, когда убедится, что объект больше нигде не используется.
Вот такое управление памятью.
Всякие malloc/free и т.п. оставлю без внимания, потому что это больше к C относится. Лучше пока их не трогать.
Ответ написан
Комментировать
Ckpyt
@Ckpyt
Тут надо понимать, что такое "мусор" в jave. Как правило, это объекты, на которых не осталось указателей. Или все указатели друг на друга замкнуты. Такой "мусор" вызывает рост использованной памяти, при том, что никто ей и не пользуется. Это называется "утечка памяти". Естественно, такое положение дел никого не устраивает, и время от времени запускается сборщик мусора, который и грохает все утечки.
Теперь, переходим к С++. Тут все управление памятью ложиться на пользователя.
Выделение памяти бывает двух видов: на стеке и в куче. На стеке - самое простое, но не самое безопасное, когда значение должно где-то храниться.
Самый простой пример int x[10]; - выделяет 10*sizeof(int) байт на стеке.
Сразу покажу чем это плохо: предположим, у вас есть два потока, и надо заполнить и передать задание второму потоку. Мы создаем внутри функции массив, заполняем его, передаем второму потоку и выходим из функции. Память при этом освободилась и уже какой-то другой поток в нее пишет.
Второй поток при этом наконец-то добрался до задания и начинает работать с переданным массивом.
В итоге, он сам получает бред, да еще и если пишет в массив выходные значения, то и портит память другому потоку. Тоже самое будет, если вы сохраните указатель на массив во внешнее поле и попробуете получить к нему доступ из другой функции.
Так мы приходим к тому, что нам нужна независимая память. Или память на куче.
int* x = new int[10];
С такой памятью легче все работать как с передаваемым массивом. Эта память нигде не испортится и в общем и целом, с ней все будет хорошо. Есть только одна проблема: ее надо прибрать. Т.е. где-нибудь должен быть delete.
Пока один поток и четкая и понятная структура приложения, все легко и просто. Когда у нас начинается многопоточность и самописные библиотеки, то все резко усложняется. И тут на помощь приходят умные указатели - shared_ptr, к примеру, делает следующее: увеличивает счетчик владений объектом на единицу каждый раз, когда указатель присваивается. И уменьшает на ту же единицу, когда вызывается деструктор. Если счетчик хранения равен нулю, то умный указатель корректно удаляет объект и утечки памяти не происходит.
В общем и целом, советую почитать про умные указатели.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
Давай попробуем разобраться, что у тебя написано:

int a = 3; // объявляем переменную типа int
int *p = 0; // объявляем указатель
p = &a; // записываем в указатель адрес переменной а

А теперь я предлагаю тебе прописать вот такие простые строчки, чтобы посмотреть что у тебя там всё таки хранится:

cout << "Значение a=" << a << endl; //значение a
cout << "Адрес &a=" << &a << endl; //здесь адрес а
cout << "Значение p=" << p << endl; //здесь адрес а

В твоем примере удалять ничего не нужно, это даже вызовет ошибку при компиляции, так как оператор delete используется в том случае, если память выделяешь ты динамически, используя ключевое слово new.

в твоем случае

int a = 3 // ты в памяти выделил 4 байта;
int *p = ты выделил (4 или 8 байта, на хранения адреса a, в зависимости от того, какая у тебя система 32-х или 64-х разрядная.

если ты объявляешь
int *p = new int //ты в динамической памяти выделяешь размер для хранения int
delete p //освобождаешь занятую ячейку в памяти.

Я сам новичок, и то что я тут написал, это моё понимание, очень хотел бы чтобы меня поправили.
Ответ написан
Комментировать
@vilgeforce
Раздолбай и программист
В общем, если нет специального указания выделять памяти (хотя формулировка хромает, да) - специально освобождать ее не нужно. Под специальным указанием понимать следует встроенный в язык new. И читать документацию на используемые функции типа malloc/VierualAlloc и т.д. Вы не используете никаких функций и new, значит, ничего не надо специально удалять.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы