C ++ динамически выделяемая память - PullRequest
6 голосов
/ 14 декабря 2011

Я не совсем понимаю смысл динамически выделяемой памяти, и я надеюсь, что вы, ребята, можете прояснить ситуацию для меня.

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

int * dynInt = new int;

Так в чем же разница между выполнением того, что я делал выше, и:

int someInt;
int* dynInt = &someInt;

Как я понимаю, в обоих случаях память выделяется для int, и мы получаемуказатель на эту память.

Так в чем же разница между ними?Когда один метод предпочтительнее другого.

Более того, зачем мне освобождать память с помощью

delete dynInt;

в первом случае, но не во втором случае.

Мои предположения:

  1. При динамическом выделении памяти для объекта, объект не инициализируется, в то время как если вы делаете что-то, как во втором случае, объект get инициализируется.Если это единственное отличие, есть ли какая-то мотивация за этим, кроме того факта, что динамическое выделение памяти происходит быстрее.

  2. Причина, по которой нам не нужно использовать delete для второгодело в том, что тот факт, что объект был инициализирован, создает некую процедуру автоматического уничтожения.

Это всего лишь догадки, которые будут любить его, если кто-то исправит меня и прояснит для меня.

Ответы [ 8 ]

15 голосов
/ 14 декабря 2011

Разница в длительности хранения .

  • Объекты с длительностью автоматического хранения - это ваши "нормальные" объекты, которые автоматически выходят изобласть видимости в конце блока, в котором они определены.

    Создайте их как int someInt;

    Возможно, вы слышали о них как о «объектах стека», хотя я объект к этой терминологии.

  • Объекты с динамической продолжительностью хранения имеют что-то вроде "ручного" времени жизни;вы должны уничтожить их самостоятельно с помощью delete и создать их с ключевым словом new.

    Возможно, вы слышали о них как о "кучных объектах", хотя я тоже возражаю против этого.

Использование указателей на самом деле не имеет никакого отношения ни к одному из них.У вас может быть указатель на объект с автоматической продолжительностью хранения (ваш второй пример) и у вас может быть указатель на объект с динамической памятью (ваш первый пример).

Но редко вы будетенужен указатель на автоматический объект, потому что:

  1. у вас нет одного «по умолчанию»;
  2. объект не будет длиться очень долго, поэтому нетмногое вы можете сделать с таким указателем.

Напротив, динамические объекты часто доступны через указатели просто потому, что синтаксис приближается к его принудительному исполнению.new возвращает указатель для использования, вы должны передать указатель на delete, и (кроме использования ссылок) фактически нет другого способа получить доступ к объекту.Он живет «там» в облаке динамичности, который не сидит в локальной области.

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

13 голосов
/ 14 декабря 2011

Объект, созданный следующим образом:

int foo;

имеет длительность автоматического хранения - объект живет до тех пор, пока переменная foo не выйдет из области видимости.Это означает, что в вашем первом примере dynInt будет недопустимым указателем, как только someInt выйдет из области видимости (например, в конце функции).

Объект, созданный следующим образом:

int foo* = new int;

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

Инициализация объектов - это ортогональное понятие;это напрямую не связано с тем, какой тип хранения вы используете.См. здесь для получения дополнительной информации об инициализации.

4 голосов
/ 14 декабря 2011
  1. Ваша программа получает начальный кусок памяти при запуске.Эта память называется стек .Размер обычно составляет около 2 МБ.

  2. Ваша программа может запросить у ОС дополнительную память.Это называется динамическим распределением памяти.Это выделяет память в бесплатном хранилище (терминология C ++) или heap (терминология C).Вы можете запросить столько памяти, сколько хочет система (несколько гигабайт).

Синтаксис для выделения переменной в стеке выглядит следующим образом:

{
    int a; // allocate on the stack
} // automatic cleanup on scope exit

Синтаксис для выделения переменной с использованием памяти из бесплатного хранилища выглядит следующим образом:

int * a = new int; // ask OS memory for storing an int
delete a; // user is responsible for deleting the object


Чтобы ответить на ваши вопросы:

Когда предпочтителен один методк другому.

  1. Как правило, выделение стека является предпочтительным.
  2. Динамическое выделение требуется, когда необходимо сохранить полиморфный объект, используя его базовый тип.
  3. Всегда используйте умный указатель для автоматизации удаления:
    • C ++ 03: boost::scoped_ptr, boost::shared_ptr или std::auto_ptr.
    • C ++ 11: std::unique_ptr или std::shared_ptr.

Например:

// stack allocation (safe)
Circle c; 

// heap allocation (unsafe)
Shape * shape = new Circle;
delete shape;

// heap allocation with smart pointers (safe)
std::unique_ptr<Shape> shape(new Circle);

Более подробно, почему мне нужно освободить память в первом случае, а не во второмcase.

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

4 голосов
/ 14 декабря 2011

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

Тем не менее, в целом, для динамического размещения больше возможностей. Есть много вещей, которые ваша программа не знает до выделения и зависит от ввода. Например, ваша программа должна прочитать файл изображения. Насколько большой этот файл изображения? Можно сказать, что мы храним его в массиве так:

unsigned char data[1000000];

Но это сработало бы только в том случае, если размер изображения был меньше или равен 1000000 байт, а также было бы бесполезным для небольших изображений. Вместо этого мы можем динамически распределять память:

unsigned char* data = new unsigned char[file_size];

Здесь file_size определяется во время выполнения. Вы не могли бы сказать это значение во время компиляции.

2 голосов
/ 14 декабря 2011

Узнайте больше о динамическом выделении памяти , а также сборке мусора

Вам действительно нужно прочитать хорошую книгу по программированию на C или C ++ .

Детальное объяснение заняло бы много времени.

Куча - это память, внутри которой происходит динамическое выделение (с new в C ++ или malloc в C).Существуют системные вызовы , связанные с увеличением и уменьшением размера кучи.В Linux это mmap & munmap (используется для реализации malloc и new и т.д ...).

Вы можете много раз вызывать примитив распределения.Таким образом, вы можете поместить int *p = new int; в цикл и получать новое место каждый раз при выполнении цикла!

Не забудьте освободить память (с delete в C ++ или free в C).В противном случае вы получите утечка памяти - непослушная ошибка -.В Linux valgrind помогает их поймать.

2 голосов
/ 14 декабря 2011

Всякий раз, когда вы используете new в C ++, память выделяется через malloc, который сам вызывает системный вызов sbrk (или аналогичный).Поэтому никто, кроме ОС, не знает о запрашиваемом размере.Таким образом, вам придется использовать delete (который вызывает free, который снова переходит к sbrk) для возврата памяти системе.В противном случае вы получите утечку памяти.

Теперь, когда дело доходит до вашего второго случая, компилятор знает о размере выделенной памяти.То есть в вашем случае размер один int.Установка указателя на адрес этого int ничего не меняет в знании необходимой памяти.Или другими словами: компилятор может позаботиться об освобождении памяти.В первом случае с new это невозможно.

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

Редактировать

Еще две общие фразы: первый случай также известен как статическое распределение памяти (выполняется компилятором), второй случай относится к динамическому распределению памяти (сделаносистемой исполнения).

1 голос
/ 14 декабря 2011

Короче говоря, время жизни динамически размещаемого объекта контролируется вами , а не языком.Это позволяет вам жить так долго, как это требуется (в отличие от конца области), возможно, определяется условием, которое может быть вычислено только во время выполнения.

Кроме того, динамическая память обычногораздо более «масштабируемый» - т.е. вы можете выделять больше и / или более крупные объекты по сравнению с выделением на основе стека.,Отмена выделения «снимает пометку» с этой части памяти, поэтому ее можно использовать повторно для более поздних выделений.Если вам не удастся освободить память после того, как она больше не нужна, вы получите условие, известное как «утечка памяти» - ваша программа занимает память, в которой она больше не нуждается, что может привести к невозможности выделить новую память (из-за отсутствия свободного места).память), и просто вообще создает ненужную нагрузку на систему.

1 голос
/ 14 декабря 2011

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

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