Я изучаю динамическое распределение памяти в C ++ и ключевые слова new
а также new[]
упоминаются.
Говорят, что он позволяет пользователям указывать размер выделения памяти во время выполнения, в отличие от простого объявления переменной или массива с фиксированным размером в исходном коде.
Я не понимаю эту концепцию. Как это работает? Мне просто нужно уточнить идею, и пример будет полезен!
Итак, если вам нужен массив из 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] Если вы не хотите пропускать память, а утечка памяти — это «плохой стиль». Не делай никого счастливым, если ты делаешь это.Я видел много сообщений о распределении памяти в C ++, вопросы о «новый оператор» против «оператор новый», вопросы о new int(100)
против new int[100]
вопросы об инициализации памяти … Я думаю, что должен быть ответ, в котором все ясно и ясно все раз и навсегда, и я выбираю этот вопрос, чтобы написать это резюме. Речь идет о динамическом распределении памяти, т.е. выделения в куче во время выполнения. Я также предоставляю итоговая реализация (всеобщее достояние).
Основные функции для динамического распределения памяти:
<cstdlib>
) у нас есть в основном malloc
а также calloc
а также free
, Я не буду говорить о realloc
,<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)
байт).