Операторы 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 Глобальный
class-name::operator new Класс

Первый аргумент operator new должен иметь тип size_t, а тип возвращаемого значения всегда void*имеет тип.

Глобальная operator new функция вызывается, когда new оператор используется для выделения объектов встроенных типов, объектов типа класса, не содержащих определяемых operator new пользователем функций, и массивов любого типа. new Если оператор используется для выделения объектов определенного типа operator new класса, вызывается этот классoperator new.

Функция, определенная operator new для класса, является статической функцией-членом (которая не может быть виртуальной), которая скрывает глобальную operator new функцию для объектов этого типа класса. Рассмотрим случай, когда new используется для выделения и задания памяти заданному значению:

#include <malloc.h>
#include <memory.h>

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()
{
   Blanks *a5 = new(0xa5) Blanks;
   return a5 != 0;
}

Аргумент, указанный в скобках 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()
{
   MyClass *pMyClass = new MyClass[5];
   delete [] pMyClass;
}

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

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

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

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

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

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

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   try {
      int *pI = new int[BIG_NUMBER];
   }
   catch (bad_alloc& ex) {
      cout << "Caught bad_alloc: " << ex.what() << endl;
      return -1;
   }
}

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

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   int *pI = new(nothrow) int[BIG_NUMBER];
   if ( pI == nullptr ) {
      cout << "Insufficient memory" << endl;
      return -1;
   }
}

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

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   int *pI = new int[BIG_NUMBER];
   if ( !pI ) {
      cout << "Insufficient memory" << endl;
      return -1;
   }
}

Вы можете предоставить обработчик для неудачных запросов на выделение памяти. Для обработки такого сбоя можно написать пользовательскую подпрограмму восстановления. Например, можно освободить зарезервированную память, а затем снова разрешить выделение. Для получения дополнительной информации см. _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 *, который содержит указатель на объект для освобождения. Вторая форма, освобождение размера, принимает два аргумента: первый — указатель на блок памяти для освобождения, а второй — количество байтов для освобождения. Тип возвращаемого значения обеих форм ( voidoperator delete не может возвращать значение).

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

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

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

#include <iostream>
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 << "Memory block " << ++cBlocksAllocated
          << " allocated for " << stAllocateBlock
          << " bytes\n";
      fInOpNew = 0;
   }
   return malloc( stAllocateBlock );
}

// User-defined operator delete.
void operator delete( void *pvMem ) {
   static int fInOpDelete = 0;   // Guard flag.
   if ( fLogMemory && !fInOpDelete ) {
      fInOpDelete = 1;
      clog << "Memory block " << cBlocksAllocated--
          << " deallocated\n";
      fInOpDelete = 0;
   }

   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() {
   X *pX = new X[5];
   delete [] pX;
}