Как работает delete c
Перейти к содержимому

Как работает delete c

  • автор:

Оператор delete (C++)

Аргумент cast-expression должен быть указателем на блок памяти, ранее выделенный для объекта, созданного с помощью нового оператора. Оператор delete имеет результат типа void и поэтому не возвращает значение. Например:

CDialog* MyDialog = new CDialog; // use MyDialog delete MyDialog; 

Использование delete указателя на объект, не выделенный с new непредсказуемыми результатами. Однако можно использовать delete указатель с значением 0. Эта подготовка означает, что при new возврате 0 при сбое удаление результата неудачной new операции является безвредным. Дополнительные сведения см. в разделе «Новые и удаленные операторы».

delete Операторы new также можно использовать для встроенных типов, включая массивы. Если pointer ссылается на массив, поместите пустые квадратные скобки ( [] ) перед pointer :

int* set = new int[100]; //use set[] delete [] set; 

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

Если delete используется для освобождения памяти для объекта класса C++, деструктор объекта вызывается до освобождения памяти объекта (если объект имеет деструктор).

Если операнду оператору delete является изменяемым l-значением, его значение не определено после удаления объекта.

Если указан параметр компилятора /sdl (включение дополнительных проверка безопасности), операнду delete оператору присваивается недопустимое значение после удаления объекта.

Использование оператора delete

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

// expre_Using_delete.cpp struct UDType < >; int main() < // Allocate a user-defined object, UDObject, and an object // of type double on the free store using the // new operator. UDType *UDObject = new UDType; double *dObject = new double; // Delete the two objects. delete UDObject; delete dObject; // Allocate an array of user-defined objects on the // free store using the new operator. UDType (*UDArr)[7] = new UDType[5][7]; // Use the array syntax to delete the array of objects. delete [] UDArr; >

В следующих двух случаях возникают неопределенные результаты: использование формы удаления массива ( delete [] ) в объекте и использование неаррейской формы удаления в массиве.

Пример

Примеры использования delete см . в разделе «Новый оператор».

Принцип работы delete

Оператор удаления вызывает удаление оператора функции.

Для объектов не типа класса (класса, структуры или объединения) вызывается глобальный оператор удаления. Для объектов типа класса имя функции deallocation разрешается в глобальных область если выражение удаления начинается с унарного оператора разрешения область ( :: ). В противном случае перед освобождением памяти оператор удаления вызывает деструктор объекта (если указатель не имеет значения null). Оператор удаления можно определять отдельно для каждого класса; если для некоторого класса такое определение отсутствует, вызывается глобальный оператор удаления. Если выражение удаления используется для освобождения объекта класса, статический тип которого имеет виртуальный деструктор, функция освобождение разрешается через виртуальный деструктор динамического типа объекта.

Операторы new и delete

C++ поддерживает динамическое выделение и размещение объектов с помощью new операторов. delete Эти операторы выделяют память для объектов из пула, называемого бесплатным хранилищем (также известное как куча ). Оператор new вызывает специальную функцию operator new , а delete оператор вызывает специальную функцию operator delete .

Список файлов библиотеки в библиотеке среды выполнения C и стандартной библиотеке C++ см. в разделе «Функции библиотеки CRT».

Оператор new

Компилятор преобразует оператор, такой как этот, в вызов функции operator new :

char *pch = new char[BUFFER_SIZE]; 

Если запрос равен нулю байтов хранилища, operator new возвращает указатель на отдельный объект. То есть повторяющиеся вызовы для operator new возврата разных указателей.

Если для запроса на выделение недостаточно памяти, operator new создается std::bad_alloc исключение. Кроме того, он возвращается nullptr , если вы использовали форму new(std::nothrow) размещения или если вы связали в неисключаемой поддержке operator new . Дополнительные сведения см. в разделе «Поведение сбоя выделения».

Два область для 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 используется для выделения и задания памяти заданному значению:

#include #include class Blanks < public: Blanks()<>void *operator new( size_t stAllocateBlock, char chInit ); >; void *Blanks::operator new( size_t stAllocateBlock, char chInit ) < void *pvTemp = malloc( stAllocateBlock ); if( pvTemp != 0 ) memset( pvTemp, chInit, stAllocateBlock ); return pvTemp; >// For discrete objects of type Blanks, the global operator new function // is hidden. Therefore, the following code allocates an object of type // Blanks and initializes it to 0xa5 int main()

Аргумент, предоставленный в скобках, new передается Blanks::operator new в качестве аргумента chInit . Однако глобальная operator new функция скрыта, что приводит к возникновению ошибки, например следующего кода:

Blanks *SomeBlanks = new Blanks; 

Компилятор поддерживает массив new элементов и delete операторы в объявлении класса. Например:

class MyClass < public: void * operator new[] (size_t) < return 0; >void operator delete[] (void*) < >>; int main()

Поведение сбоя выделения

Функция new в стандартной библиотеке C++ поддерживает поведение, указанное в стандарте C++ с C++98. Если для запроса на выделение недостаточно памяти, operator new создается std::bad_alloc исключение.

Старый код C++ вернул указатель null для неудавшихся выделений. Если у вас есть код, который ожидает неисключаемую версию new , свяжите программу с nothrownew.obj . Файл nothrownew.obj заменяет глобальную operator new версию, которая возвращается nullptr при сбое выделения. operator new больше не бросает std::bad_alloc . Дополнительные сведения о nothrownew.obj файлах параметров компоновщика см. в разделе «Параметры ссылки».

Нельзя смешивать код, который проверка для исключений из глобального operator new кода, который проверка для указателей NULL в одном приложении. Однако вы по-прежнему можете создавать локальные operator new классы, которые ведут себя по-разному. Эта возможность означает, что компилятор должен выполнять оборонительные действия по умолчанию и включать проверка для возвращаемых указателей NULL в new вызовах. Дополнительные сведения об оптимизации этих проверка компилятора см. в статье /Zc:throwingnew .

Обработка нехватки памяти

Способ тестирования для неудавшихся выделений из new выражения зависит от того, используется ли стандартный механизм исключений nullptr или используется возврат. Стандартный C++ ожидает, что std::bad_alloc выделяющий объект или класс, производный от std::bad_alloc . Вы можете обработать такое исключение, как показано в этом примере:

#include #include using namespace std; #define BIG_NUMBER 10000000000LL int main() < try < int *pI = new int[BIG_NUMBER]; >catch (bad_alloc& ex) < cout > 

При использовании формы new можно проверить сбой nothrow выделения, как показано в этом примере:

#include #include using namespace std; #define BIG_NUMBER 10000000000LL int main() < int *pI = new(nothrow) int[BIG_NUMBER]; if ( pI == nullptr ) < cout > 

Если вы использовали nothrownew.obj файл для замены глобального operator new файла, можно протестировать выделение памяти сбоем, как показано ниже.

#include #include using namespace std; #define BIG_NUMBER 10000000000LL int main() < int *pI = new int[BIG_NUMBER]; if ( !pI ) < cout > 

Вы можете предоставить обработчик для неудачных запросов на выделение памяти. Можно написать настраиваемую процедуру восстановления для обработки такого сбоя. Например, он может освободить зарезервированную память, а затем разрешить повторное выполнение выделения. Дополнительные сведения см. в разделе _set_new_handler .

Оператор delete

Память, которая динамически выделяется с помощью new оператора, можно освободить с помощью delete оператора. Оператор удаления вызывает operator delete функцию, которая освобождает память обратно в доступный пул. delete Использование оператора также приводит к вызову деструктора класса (если он существует).

Существуют глобальные функции и функции область класса operator delete . Для данного класса можно определить только одну operator delete функцию. Если она определена, она скрывает глобальную operator delete функцию. Глобальная operator delete функция всегда вызывается для массивов любого типа.

Глобальная operator delete функция. Для глобальных operator delete и членных operator delete функций существуют две формы:

void operator delete( void * ); void operator delete( void *, size_t ); 

Для данного класса может присутствовать только одна из предыдущих двух форм. Первая форма принимает один аргумент типа void * , который содержит указатель на объект для освобождения. Вторая форма, размер сделки, принимает два аргумента: первый — указатель на блок памяти для освобождения, а второй — число байтов для освобождения. Возвращаемый тип обеих форм — ( void operator delete не может возвращать значение).

Цель второй формы — ускорить поиск правильной категории размера объекта для удаления. Эта информация часто не хранится рядом с самим выделением и, скорее всего, не качается. Вторая форма полезна, если operator delete функция из базового класса используется для удаления объекта производного класса.

Функция operator delete является статической, поэтому она не может быть виртуальной. Функция operator delete подчиняется управлению доступом, как описано в разделе «Член-контроль доступа».

В следующем примере показаны определяемые operator new пользователем функции и operator delete функции, предназначенные для выделения журналов и размещения памяти:

#include using namespace std; int fLogMemory = 0; // Perform logging (0=no; nonzero=yes)? int cBlocksAllocated = 0; // Count of blocks allocated. // User-defined operator new. void *operator new( size_t stAllocateBlock ) < static int fInOpNew = 0; // Guard flag. if ( fLogMemory && !fInOpNew ) < fInOpNew = 1; clog return malloc( stAllocateBlock ); > // User-defined operator delete. void operator delete( void *pvMem ) < static int fInOpDelete = 0; // Guard flag. if ( fLogMemory && !fInOpDelete ) < fInOpDelete = 1; clog free( pvMem ); > int main( int argc, char *argv[] ) < fLogMemory = 1; // Turn logging on if( argc >1 ) for( int i = 0; i < atoi( argv[1] ); ++i ) < char *pMem = new char[10]; delete[] pMem; >fLogMemory = 0; // Turn logging off. return cBlocksAllocated; > 

Предыдущий код можно использовать для обнаружения утечки памяти, то есть памяти, выделенной в свободном хранилище, но никогда не освобождаемой. Чтобы обнаружить утечки, глобальные new и delete операторы переопределяются для подсчета распределения и распределения памяти.

Компилятор поддерживает массив new элементов и delete операторы в объявлении класса. Например:

// spec1_the_operator_delete_function2.cpp // compile with: /c class X < public: void * operator new[] (size_t) < return 0; >void operator delete[] (void*) <> >; void f()

Что такое delete в C++?

Оператор delete используется для освобождения памяти, ранее выделенной с использованием оператора new . Правильное использование delete важно для предотвращения утечек памяти и некорректной работы программы.

Основные моменты:

  1. Использование после new : delete применяется к указателям, полученным из оператора new . Это обеспечивает правильное освобождение памяти.
  2. delete[] для массивов: Если память была выделена для массива с использованием new[] , следует использовать delete[] для ее корректного освобождения.
  3. Осторожность с двойным delete : Двойное освобождение памяти ( delete дважды для одного указателя) приведет к неопределенному поведению программы.
int main() < // Выделение памяти для одного целого числа int* singleNumber = new int(42); // Освобождение памяти после использования delete singleNumber; // Выделение памяти для массива целых чисел int* numberArray = new int[5]; // Использование выделенной памяти (пример) for (int i = 0; i < 5; ++i) < std::cout // Освобождение памяти после использования массива delete[] numberArray; return 0; >

Как устроены операторы delete и delete[]?

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

Отслеживать
задан 17 апр 2020 в 13:05
4,128 1 1 золотой знак 9 9 серебряных знаков 24 24 бронзовых знака

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

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

Если в delete приходит указатель «неверного типа», то будет вызван и «неверный деструктор». В некоторых случаях это может закончится плачевно. У void деструктор фактически отсутствует, значит вызывать нечего и соответственно ничего не вызовется. Если деструктор выполняет нетривиальную работу — будет не очень хорошо.

delete[] получив указатель, «магическим способом» получает размер массива (обычно, он хранится рядом с указателем, но это личное дело компилятора, поэтому я и написал «магическим способом»). А дальше просто в цикле вызываем delete . Ничего необычного. Поэтому, если попутать delete и delete[] могут быть разные непонятные баги ( delete[] будет искать размер массива там, где его нет, а потом пойдет в разнос).

Некоторые компиляторы могут добавлять немножко больше кода для проверки себя и удалять корректно. К примеру, так может сделать visual studio в дебажном режиме. Но я бы на это не закладывался.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *