Что такое динамическое распределение памяти в C ++?

Я изучаю динамическое распределение памяти в C ++ и ключевые слова new а также new[] упоминаются.
Говорят, что он позволяет пользователям указывать размер выделения памяти во время выполнения, в отличие от простого объявления переменной или массива с фиксированным размером в исходном коде.

Я не понимаю эту концепцию. Как это работает? Мне просто нужно уточнить идею, и пример будет полезен!

4

Решение

Итак, если вам нужен массив из 10 целых чисел, вы должны написать:

int arr[10];

Но что, если вы хотите сделать что-то подобное;

cout << "How many?";
cin >> num;

int arr[num];

Ну, язык C ++ не позволяет этого. Вместо этого вы должны сделать:

int *arr = new int[num];

создать свой массив. И позже вы ДОЛЖНЫ [1] использовать:

delete [] arr;

освободить память.

Так как же это работает? Когда вы вызываете new, библиотека времени выполнения C ++ (код, который вам не нужно было писать, составляющий основы C ++) выяснит, сколько места num целые числа занимают и находят место в памяти для этого. Я не буду вдаваться в подробности того, «как ты находишь память». А пока, просто поверьте мне, где-то есть память, которую можно использовать для хранения целых чисел.

Когда вы позже позвоните deleteта же самая память возвращается «пулу» или «куче» памяти, из которой она получена.

Конечно, если у вас есть машина, скажем, с 256 МБ памяти, и вы пытаетесь запросить место для хранения 250 миллионов целых чисел, учитывая, что целое число занимает более одного байта, это не сработает — здесь нет «магии» — память все еще ограничена тем, сколько доступно в машине …. Вы просто имеете право определить в программе, когда она работает, сколько памяти вам нужно, вместо того, чтобы решить при написании программы.

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

 std::vector<int> arr;

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

 std::shared_ptr<int> arr = new int[num];

это другой случай, когда «shared_ptr» больше не используется [он отслеживает это внутри класса общего указателя, поэтому вам никогда не нужно заботиться об освобождении памяти].

[1] Если вы не хотите пропускать память, а утечка памяти — это «плохой стиль». Не делай никого счастливым, если ты делаешь это.

9

Другие решения

Я видел много сообщений о распределении памяти в C ++, вопросы о «новый оператор» против «оператор новый», вопросы о new int(100) против new int[100]вопросы об инициализации памяти … Я думаю, что должен быть ответ, в котором все ясно и ясно все раз и навсегда, и я выбираю этот вопрос, чтобы написать это резюме. Речь идет о динамическом распределении памяти, т.е. выделения в куче во время выполнения. Я также предоставляю итоговая реализация (всеобщее достояние).


Основные функции для динамического распределения памяти:

  • В С (заголовок <cstdlib>) у нас есть в основном malloc а также calloc а также free, Я не буду говорить о realloc,
  • в C ++ (заголовок <new>), у нас есть:
    • Шаблон размещения одного объекта с аргументами инициализации:
      • new T( args )
      • new (std::nothrow) T( args )
      • delete ( T* )
    • Распределение нескольких объектов шаблона с инициализацией по умолчанию:
      • new T[ size_t ]
      • new (std::nothrow) T[ size_t ]
      • delete[] ( T* )
    • Инициализация памяти шаблона без выделения для одного или нескольких объектов:
      • new (void*) T( args )
      • new (void*) T[ size_t ]
    • внутренний новое-выражение за:
      • Необработанное выделение памяти ::operator new( size_t );
      • Необработанное выделение памяти без исключения ::operator new( size_t, std::nothrow );
      • Необработанная инициализация памяти без выделения ::operator new( size_t, ptr ),

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


Основные моменты: полное удаление типа (void* указатели), и, следовательно, нет строительства / разрушения, размер указан в байтах (обычно используется sizeof).

malloc( size_t ) не инициализирует память вообще (необработанная память содержит мусор, всегда инициализируйте вручную перед использованием). calloc( size_t, size_t ) инициализирует все биты в 0 (небольшие издержки, но полезно для числовых типов POD). Любая выделенная память должна быть освобождена с помощью free ТОЛЬКО.

Строительство / уничтожение экземпляров класса должно быть сделано вручную до использовать / до освобождение памяти.


Основные моменты: сбивает с толку из-за сходных синтаксисов, делающих разные вещи, все delete-ответы называют деструктором, все delete— заявления принимают полностью типизированные указатели, немного new-отчеты возвращают полностью набранные указатели, немного newЗаявления немного конструктор.

Предупреждение: как вы увидите ниже, new может быть ключевое слово ИЛИ ЖЕ функция. Лучше не говорить о «новом операторе» и / или «новом операторе», чтобы избегать путаницы. Я звоню «newзаявления «любые действительные заявления, которые содержат new либо как функция, либо как ключевое слово. Люди тоже говорят оnew-выражения «, где new это ключевое слово, а не функция.

Необработанное выделение памяти (без инициализации)

Не используйте это самостоятельно. Это используется внутри новое-выражение (увидеть ниже).

  • ::operator new( size_t ) а также ::operator new( size_t, std::nothrow ) взять размер в байтах и ​​вернуть void* в случае успеха.
  • В случае неудачи, первый бросает исключение std::bad_alloc, последний возвращается NULL,
  • использование ::operator new( sizeof(T) ) для не замужем тип объекта T (а также delete для выпуска), и ::operator new( n*sizeof(T) ) за множественный объекты (и delete[] для выпуска).

Эти распределения не делайте инициализировать память, и, в частности, они не делайте вызовите конструктор по умолчанию для выделенных объектов. Поэтому ты ДОЛЖЕН инициализировать ВСЕ элементы вручную прежде чем освободить распределение с помощью либо delete или же delete[],

ЗаметкаЯ не мог подчеркнуть, что вы НЕ должны использовать это самостоятельно. Если вы должны использовать его, однако, убедитесь, что вы передаете указатель на void вместо типизированного указателя при вызове delete или же delete[] на такие распределения (всегда после инициализации вручную). Я лично испытывал ошибки во время выполнения с не POD-типами с некоторыми компиляторами (возможно, моя ошибка).

Необработанная инициализация памяти (без выделения)

Не используйте это самостоятельно. Это используется внутри новое-выражение (увидеть ниже).
В дальнейшем я предполагаю void *ptr = ::operator new( n*sizeof(T) ) для какого-то типа T и размер n,

затем ::operator new( n*sizeof(T), (T*) ptr ) инициализирует n элементы типа T начиная с ptr используя конструктор по умолчанию T::T(), Есть без распределения здесь только инициализация с использованием конструктора default.

Распределение одного объекта & инициализация

  • new T( args ) выделяет а также инициализирует память для одного объекта типа T используя конструктор T::T( args ), Конструктор по умолчанию не будет вызван если аргументы опущены (т.е. new T() или даже new T). Бросает исключение std::bad_alloc на провал.
  • То же самое для new (std::nothrow) T( args ) кроме того, что это возвращает NULL в случае неудачи.
  • использование delete вызвать деструктор T::~T() и освободить соответствующую память.

Распределение нескольких объектов & инициализация

  • new T[n] выделяет а также инициализирует память для n объекты типа T используя конструктор по умолчанию. Бросает исключение std::bad_alloc на провал.
  • То же самое для new (std::nothrow) T[n] кроме того, что это возвращает NULL в случае неудачи.
  • использование delete[] вызвать деструктор T::~T() для каждого элемента и освободить соответствующую память.

Инициализация памяти (так называемое «размещение нового»)

Нет распределения здесь. Независимо от того, как было сделано распределение:

  • new (ptr) T(args) вызывает конструктор T::T(args) в памяти, хранящейся в ptr, Конструктор по умолчанию не вызывается, если аргументы не опущены.
  • new (ptr) T[n] вызывает конструктор по умолчанию T::T() на n объекты типа T хранится из ptr в ptr+n (То есть, n*sizeof(T) байт).

4

По вопросам рекламы [email protected]