Как удалить из памяти массив с
Аргумент приведения выражения должен быть указателем на блок памяти, ранее выделенный для объекта, созданного с помощью нового оператора. Оператор delete имеет результат типа void и, следовательно, не возвращает значение. Пример:
Использование delete указателя на объект, не выделенный с непредсказуемыми new результатами. Однако можно использовать delete указатель со значением 0. Эта подготовка означает, что при new возвращении 0 при сбое удаление результата неудачной new операции безвредно. Дополнительные сведения см . в разделе "Новые и удаленные операторы".
Операторы new и delete операторы также можно использовать для встроенных типов, включая массивы. Если pointer ссылается на массив, поместите пустые скобки ( [] ) перед pointer :
delete Использование оператора для объекта освобождает память. Программа, которая разыменовывает указатель после удаления объекта, может создать непрогнозируемый результат или вызвать сбой.
Когда delete используется для освобождения памяти для объекта класса C++, деструктор объекта вызывается до освобождения памяти объекта (если объект имеет деструктор).
Если операнд delete оператору является изменяемым l-значением, его значение не определено после удаления объекта.
Если указан параметр компилятора /sdl (включение дополнительных проверок безопасности), операнд delete оператору присваивается недопустимое значение после удаления объекта.
Использование оператора delete
Существует два синтаксических варианта для оператора delete: один для отдельных объектов, а другой для массивов объектов. В следующем фрагменте кода показано, как они отличаются:
В следующих двух случаях возникают неопределенные результаты: использование формы массива удаления ( delete [] ) для объекта и использование неаррейской формы удаления в массиве.
Пример
Примеры использования delete см. в разделе "Новый оператор".
Принцип работы delete
Оператор delete вызывает удаление оператора функции.
Для объектов, не относится к типу класса (классу, структуре или объединению), вызывается глобальный оператор удаления. Для объектов типа класса имя функции освобождения разрешается в глобальной области, если выражение delete начинается с оператора разрешения унарной области ( :: ). В противном случае перед освобождением памяти оператор удаления вызывает деструктор объекта (если указатель не имеет значения null). Оператор удаления можно определять отдельно для каждого класса; если для некоторого класса такое определение отсутствует, вызывается глобальный оператор удаления. Если выражение удаления используется для освобождения объекта класса, статический тип которого имеет виртуальный деструктор, функция освобождение разрешается через виртуальный деструктор динамического типа объекта.
В C++ можно использовать различные типы объектов, которые различаются по использованию памяти. Так, глобальные объекты создаются при запуске программы и освобождаются при ее завершении. Локальные автоматические объекты создаются в блоке кода и удаляются, когда этот блок кода завершает работу. Локальные статические объекты создаются перед их первым использованием и освобождаются при завершении программы.
Глобальные, а также статические локальные объекты помещаются в статической памяти, а локальные автоматические объекты размещаются в стеке. Объекты в статической памяти и стеке создаются и удаляются компилятором. Статическая память очищается при завершении программы, а объекты из стека существуют, пока выполняется блок, в котором они определены.
В дополнение к этим типам в C++ можно создавать динамические объекты . Продолжительность их жизни не зависит от того, где они созданы. Динамические объекты существуют, пока не будут удалены явным образом. Динамические объекты размещаются в динамической памяти (free store).
Для управления динамическими объектами применяются операторы new и delete .
Оператор new выделяет место в динамической памяти для объекта и возвращает указатель на этот объект.
Оператор delete получает указатель на динамический объект и удаляет его из памяти.
Выделение памяти
Создание динамического объекта:
Оператор new создает новый объект типа int в динамической памяти и возвращает указатель на него. Значение такого объекта неопределено.
Также можно инициализировать объект при создании:
Освобождение памяти
Динамические объекты будут существовать пока не будут явным образом удалены. И после завершения использования динамических объектов следует освободить их память с помощью оператора delete :
Особенно это надо учитывать, если динамический объект создается в одной части кода, а используется в другой. Например:
В функции usePtr получаем из функции createPtr указатель на динамический объект. Однако после выполнения функции usePtr этот объект автоматически не удаляется из памяти (как это происходит в случае с локальными автоматическими объектами). Поэтому его надо явным образом удалить, использовав оператор delete.
Использование объекта по указателю после его удаления или повторное применение оператора delete к указателю могут привести к непредсказуемым результатам:
Поэтому следует удалять объект только один раз.
Также нередко имеет место ситуация, когда на один и тот же динамический объект указывают сразу несколько указателей. Если оператор delete применен к одному из указателей, то память объекта освобождается, и по второму указателю этот объект мы использовать уже не сможем. Если же после этого ко второму указателю применить оператор delete, то динамическая память может быть нарушена.
В то же время недопустимость указателей после применения к ним оператора delete не означает, что эти указатели мы в принципе не сможем использовать. Мы сможем их использовать, если присвоим им адрес другого объекта:
Здесь после удаления объекта, на который указывает p1, этому указателю передается адрес другого объекта в динамической памяти. Соответственно мы также можем использовать указатель p1. В то же время адрес в указателе p2 по прежнему будет недействительным.
При создании массива с фиксированными размерами под него выделяется определенная память. Например, пусть у нас будет массив с пятью элементами:
Для такого массива выделяется память 5 * 8 (размер типа double) = 40 байт. Таким образом, мы точно знаем, сколько в массиве элементов и сколько он занимает памяти. Однако это не всегда удобно. Иногда бывает необходимо, чтобы количество элементов и соответственно размер выделяемой памяти для массива определялись динамически в зависимости от некоторых условий. Например, пользователь сам может вводить размер массива. И в этом случае для создания массива мы можем использовать динамическое выделение памяти.
Для управления динамическим выделением памяти используется ряд функций, которые определены в заголовочном файле stdlib.h :
malloc() . Имеет прототип
Выделяет память длиной в s байт и возвращает указатель на начало выделенной памяти. В случае неудачного выполнения возвращает NULL
calloc() . Имеет прототип
Выделяет память для n элементов по m байт каждый и возвращает указатель на начало выделенной памяти. В случае неудачного выполнения возвращает NULL
realloc() . Имеет прототип
Изменяет размер ранее выделенного блока памяти, на начало которого указывает указатель bl, до размера в ns байт. Если указатель bl имеет значение NULL , то есть память не выделялась, то действие функции аналогично действию malloc
free() . Имеет прототип
Освобождает ранее выделенный блок памяти, на начало которого указывает указатель bl.
Если мы не используем эту функцию, то динамическая память все равно освободится автоматически при завершении работы программы. Однако все же хорошей практикой является вызов функции free() , который позволяет как можно раньше освободить память.
Рассмотрим применение функций на простой задаче. Длина массива неизвестна и вводится во время выполнения программы пользователем, и также значения всех элементов вводятся пользователем:
Консольный вывод программы:
Здесь для управления памятью для массива определен указатель block типа int . Количество элементов массива заранее неизвестно, оно представлено переменной n.
Вначале пользователь вводит количество элементов, которое попадает в переменную n. После этого необходимо выделить память для данного количества элементов. Для выделения памяти здесь мы могли бы воспользоваться любой из трех вышеописанных функций: malloc, calloc, realloc. Но конкретно в данной ситуации воспользуемся функцией malloc :
Прежде всего надо отметить, что все три выше упомянутые функции для универсальности возвращаемого значения в качестве результата возвращают указатель типа void * . Но в нашем случае создается массив типа int, для управления которым используется указатель типа int * , поэтому выполняется неявное приведение результата функции malloc к типу int * .
В саму функцию malloc передается количество байтов для выделяемого блока. Это количество подсчитать довольно просто: достаточно умножить количество элементов на размер одного элемента n * sizeof(int) .
После выполнения всех действий память освобождается с помощью функции free() :
Важно, что после выполнения этой функции мы уже не сможем использовать массив, например, вывести его значения на консоль:
И если мы попытаемся это сделать, то получим неопределенные значения.
Вместо функции malloc аналогичным образом мы могли бы использовать функцию calloc() , которая принимает количество элементов и размер одного элемента:
Либо также можно было бы использовать функцию realloc() :
При использовании realloc желательно (в некоторых средах, например, в Visual Studio, обязательно) инициализировать указатель хотя бы значением NULL.
Но в целом все три вызова в данном случае имели бы аналогичное действие:
Теперь рассмотрим более сложную задачу - динамическое выделение памяти для двухмерного массива:
Переменная table представляет указатель на массив указателей типа int* . Каждый указатель table[i] в этом массиве представляет указатель на подмассив элементов типа int , то есть отдельные строки таблицы. А переменная table фактически представляет указатель на массив указателей на строки таблицы.
Для хранения количества элементов в каждом подмассиве определяется указатель rows типа int . Фактически он хранит количество столбцов для каждой строки таблицы.
Сначала вводится количество строк в переменную rowscount . Количество строк - это количество указателей в массиве, на который указывает указатель table . И кроме того, количество строк - это количество элементов в динамическом массиве, на который указывает указатель rows . Поэтому вначале необходимо для всех этих массивов выделить память:
Далее в цикле осуществляется ввод количества столбцов для каждый строки. Введенное значение попадает в массив rows. И в соответствии с введенным значением для каждой строки выделяется необходимый размер памяти:
Затем производится ввод элементов для каждой строки.
В конце работы программы при выводе происходит освобождение памяти. В программе память выделяется для строк таблицы, поэтому эту память надо освободить:
И кроме того, освобождается память, выделенная для указателей table и rows:
C++ поддерживает динамическое выделение и освобождение объектов с помощью new операторов и delete операторов. Эти операторы выделяют память для объектов из пула, называемого свободным хранилищем. Оператор new вызывает специальную функцию operator new , а delete оператор вызывает специальную функцию operator delete .
Функция new в стандартной библиотеке C++ поддерживает поведение, указанное в стандарте C++, которое вызывает std::bad_alloc исключение при сбое выделения памяти. Если вы по-прежнему хотите использовать неисключающую версию new , свяжите программу с nothrownew.obj . Однако при связывании со nothrownew.obj стандартной библиотекой C++ значение по умолчанию operator new больше не работает.
Список файлов библиотеки в библиотеке среды выполнения C и стандартной библиотеке C++ см. в разделе "Функции библиотеки CRT".
Оператор new
Компилятор преобразует оператор, например этот, в вызов функции operator new :
Если запрос равен нулю байтов хранилища, operator new возвращает указатель на отдельный объект. То есть повторяющиеся вызовы для operator new возврата разных указателей. Если для запроса на выделение недостаточно памяти, operator new возникает std::bad_alloc исключение. Или возвращается nullptr , если вы связали в поддержке, отличной от создания operator new .
Вы можете написать подпрограмму, которая пытается освободить память и повторить выделение. Для получения дополнительной информации см. _set_new_handler . Дополнительные сведения о схеме восстановления см. в разделе "Обработка нехватки памяти ".
Две области для operator new функций описаны в следующей таблице.
Область для operator new функций
Оператор | Область |
---|---|
::operator new | Глобальный |
имя класса ::operator new | Класс |
Первый аргумент operator new должен иметь тип size_t , определенный в , и возвращаемый тип всегда void* .
Глобальная operator new функция вызывается, когда new оператор используется для выделения объектов встроенных типов, объектов типа класса, не содержащих определяемых operator new пользователем функций, и массивов любого типа. new Если оператор используется для выделения объектов определенного типа operator new класса, вызывается этот класс operator new .
Функция, определенная operator new для класса, является статической функцией-членом (которая не может быть виртуальной), которая скрывает глобальную operator new функцию для объектов этого типа класса. Рассмотрим случай, когда new используется для выделения и задания памяти заданному значению:
Аргумент, указанный в скобках, new передается Blanks::operator new в качестве аргумента chInit . Однако глобальная operator new функция скрыта, что приводит к возникновению ошибки, например следующего:
Компилятор поддерживает массив new элементов и delete операторы в объявлении класса. Пример:
Обработка нехватки памяти
Тестирование на выделение памяти сбоем можно выполнить, как показано ниже.
Существует еще один способ обработки неудачных запросов на выделение памяти. Напишите пользовательскую подпрограмму восстановления для обработки такого сбоя, а затем зарегистрируйте функцию, вызвав _set_new_handler функцию времени выполнения.
Оператор delete
Память, которая динамически выделяется с помощью new оператора, можно освободить с помощью delete оператора. Оператор удаления вызывает функцию operator delete , которая освобождает память обратно в доступный пул. delete Использование оператора также приводит к вызову деструктора класса (если таковой существует).
Существуют глобальные и классовые operator delete функции. Для заданного класса можно определить только одну operator delete функцию. Если она определена, она скрывает глобальную operator delete функцию. Глобальная operator delete функция всегда вызывается для массивов любого типа.
Глобальная operator delete функция. Для глобальных operator delete и членных operator delete функций существуют две формы:
Для данного класса может присутствовать только одна из предыдущих двух форм. Первая форма принимает один аргумент типа void * , который содержит указатель на объект для освобождения. Вторая форма, освобождение размера, принимает два аргумента: первая — указатель на блок памяти для освобождения, а второй — количество байтов для освобождения. Тип возвращаемого значения обеих форм ( void operator delete не может возвращать значение).
Цель второй формы — ускорить поиск правильной категории размера удаляемого объекта. Эта информация часто не хранится рядом с самим выделением и, скорее всего, не качается. Вторая форма полезна, если operator delete функция из базового класса используется для удаления объекта производного класса.
Функция operator delete является статической, поэтому она не может быть виртуальной. Функция operator delete подчиняется управлению доступом, как описано в разделе "Член-контроль доступа".
В следующем примере показаны определяемые operator new пользователем функции и operator delete функции, предназначенные для записи выделения и освобождения памяти:
Приведенный выше код можно использовать для обнаружения утечки памяти, то есть памяти, выделенной в свободном хранилище, но никогда не освобождаемой. Для обнаружения утечек глобальные new и delete операторы переопределяются для подсчета выделения и освобождения памяти.
Компилятор поддерживает массив new элементов и delete операторы в объявлении класса. Пример:
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Перегрузки
Очищает содержимое массива.
Задает для диапазона элементов в массиве значение, предусмотренное по умолчанию для каждого типа элементов.
Clear(Array)
Очищает содержимое массива.
Параметры
Массив, из которого необходимо удалить элементы.
Исключения
Параметр array равен null .
Применяется к
Clear(Array, Int32, Int32)
Задает для диапазона элементов в массиве значение, предусмотренное по умолчанию для каждого типа элементов.
Параметры
Массив, элементы которого необходимо очистить.
Начальный индекс диапазона элементов, которые необходимо очистить.
Число элементов, подлежащих очистке.
Исключения
array имеет значение null .
index меньше нижней границы массива array .
Значение параметра length меньше нуля.
Сумма index и length больше, чем размер array .
Примеры
В следующем примере метод используется для сброса Clear целых значений в одномерном, двумерном и трехмерном массиве.
В следующем примере определяется TimeZoneTime структура, включающая TimeZoneInfo поле и DateTimeOffset поле. Затем он вызывает метод для очистки Clear одного элемента в массиве значений с двумя элементами TimeZoneTime . Метод задает значение очищенного элемента значением TimeZoneInfo по умолчанию объекта, т null . е. значением по умолчанию объекта, т. е. значением DateTimeOffset по умолчанию объекта.DateTimeOffset.MinValue
Комментарии
Этот метод сбрасывает каждый элемент массива в значение по умолчанию типа элемента. Он задает для элементов ссылочных типов (включая String элементы) null значение и задает элементы типов значений по умолчанию, показанные в следующей таблице.
Тип | Значение |
---|---|
Boolean | false |
Все целочисленные и числовые типы с плавающей запятой | 0 (ноль) |
DateTime | DateTime.MinValue |
Другие типы значений | Значение по умолчанию полей типа |
Диапазон очищенных элементов из строки в строку в многомерном массиве.
Этот метод очищает только значения элементов; он не удаляет сами элементы. Массив имеет фиксированный размер; таким образом, элементы нельзя добавлять или удалять.
Читайте также: