Operadores new
e delete
A C++ oferece suporte a alocação e desalocação dinâmica de objetos usando os operadores new
e delete
. Esses operadores alocam memória para objetos de um pool chamado de free store (também conhecido como heap). O operador new
chama a função especial operator new
e o operador delete
chama a função especial operator delete
.
Para ver uma lista dos arquivos da Biblioteca de Runtime C e a Biblioteca Padrão C++, consulte Recursos da Biblioteca de CRT.
O operador new
O compilador converte uma instrução como esta em uma chamada para a função operator new
:
char *pch = new char[BUFFER_SIZE];
Se a solicitação for para zero bytes de armazenamento, operator new
retorna um ponteiro para um objeto distinto. Ou seja, chamadas repetidas para operator new
retornam ponteiros diferentes.
Se não houver memória suficiente para a solicitação de alocação, operator new
gerará uma exceção std::bad_alloc
. Ou ele retornará nullptr
se você tiver usado a forma placement new(std::nothrow)
ou se estiver vinculado no suporte de não lançamento operator new
. Para obter mais informações, consulte Comportamento de falha de alocação.
Os dois escopos para funções operator new
são descritos na tabela a seguir.
Escopo para funções operator new
Operador | Escopo |
---|---|
::operator new |
Global |
class-name ::operator new |
Classe |
O primeiro argumento de operator new
deve ser do tipo size_t
e o tipo de retorno é sempre void*
.
A função operator new
global é chamada quando o operador new
é usado para alocar objetos de tipos internos, objetos de tipo de classe que não contêm funções operator new
definidas pelo usuário e matrizes de qualquer tipo. Quando o operador new
é usado para alocar objetos de um tipo de classe um operator new
é definido, o operator new
dessa classe é chamado.
Uma função operator new
definida para uma classe é uma função membro estática (que não pode ser virtual), a qual oculta a função operator new
global para objetos desse tipo de classe. Considere o caso em que new
é usado para alocar e definir memória para um valor específico:
#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;
}
O argumento fornecido entre parênteses para new
é passado para Blanks::operator new
como o argumento chInit
. No entanto, a função operator new
global é ocultada, fazendo com que o código como o seguinte gere um erro:
Blanks *SomeBlanks = new Blanks;
O compilador oferece suporte aos operadores new
e delete
da matriz de membros em uma declaração de classe. Por exemplo:
class MyClass
{
public:
void * operator new[] (size_t)
{
return 0;
}
void operator delete[] (void*)
{
}
};
int main()
{
MyClass *pMyClass = new MyClass[5];
delete [] pMyClass;
}
Comportamento de falha de alocação
A função new
na Biblioteca Padrão C++ dá suporte ao comportamento especificado no padrão C++ desde C++98. Quando não houver memória suficiente para uma solicitação de alocação, operator new
gerará uma exceção std::bad_alloc
.
O código C++ mais antigo retornou um ponteiro nulo para uma alocação com falha. Se você tiver um código que espera a versão de não lançamento de new
, vincule o programa com nothrownew.obj
. O arquivo nothrownew.obj
substitui operator new
global por uma versão que retorna nullptr
se uma alocação falhar. operator new
não lança mais std::bad_alloc
. Para obter mais informações sobre nothrownew.obj
e outros arquivos de opção do vinculador, consulte Opções do Link.
Você não pode misturar código que verifica exceções de operator new
global com código que verifica ponteiros nulos no mesmo aplicativo. No entanto, você ainda pode criar operator new
de classe local que se comporte de forma diferente. Essa possibilidade significa que o compilador deve agir defensivamente por padrão e incluir verificações de retorno de ponteiro nulo em chamadas new
. Para obter mais informações sobre uma maneira de otimizar essas verificações do compilador, consulte /Zc:throwingnew
.
Tratar condições de memória insuficiente
A maneira como você testa uma alocação com falha de uma new
expressão depende se você usa o mecanismo de exceção padrão ou usa um retorno nullptr
. O C++ Padrão espera que um alocador gere std::bad_alloc
ou uma classe derivada de std::bad_alloc
. Você pode tratar essa exceção, conforme mostrado neste exemplo:
#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;
}
}
Ao usar a forma nothrow
de new
, é possível testar uma falha de alocação, conforme mostrado neste exemplo:
#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;
}
}
Você pode testar uma alocação de memória com falha se usou o arquivo nothrownew.obj
para substituir operator new
global, conforme mostrado aqui:
#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;
}
}
Você pode fornecer um manipulador para solicitações de alocação de memória com falha. É possível gravar uma rotina de recuperação personalizada para tratar essa falha. Ela pode, por exemplo, liberar alguma memória reservada e permitir que a alocação seja executada novamente. Para obter mais informações, consulte _set_new_handler
.
O operador delete
A memória que é atribuída dinamicamente usando o operador new
pode ser liberada usando o operador delete
. O operador delete chama a função operator delete
, que libera memória no pool disponível. Usar o operador delete
também faz com que o destruidor da classe (se houver) seja chamado.
Há funções operator delete
de escopo global e de classe. Apenas uma função operator delete
pode ser definida para uma classe específica e, se definida, ela oculta a função operator delete
global. A função operator delete
global sempre é chamada para matrizes de qualquer tipo.
A função operator delete
global. Existem duas formas para as funções operator delete
global e operator delete
de membro de classe:
void operator delete( void * );
void operator delete( void *, size_t );
Somente uma das duas variantes precedentes pode estar presente para uma determinada classe. A primeira forma usa um argumento do tipo void *
, que contém um ponteiro para o objeto a ser desalocado. A segunda forma, desalocação dimensionada, usa dois argumentos: o primeiro é um ponteiro para o bloco de memória a ser desalocado e o segundo é o número de bytes para desalocar. O tipo de retorno de ambos os formulários é void
(operator delete
não é possível retornar um valor).
A intenção da segunda forma é acelerar a pesquisa pela categoria de tamanho correta do objeto a ser excluído. Essas informações geralmente não são armazenadas perto da alocação em si e provavelmente não são armazenadas em cache. A segunda forma é particularmente útil quando uma função operator delete
de uma classe base é usada para excluir um objeto de uma classe derivada.
A função operator delete
é estática e, portanto, não pode ser virtual. A função operator delete
obedece o controle de acesso, conforme descrito em Controle de Acesso de Membros.
O exemplo a seguir mostra as funções operator new
e operator delete
definidas pelo usuário criadas para registrar em log as alocações e desalocações de memória:
#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;
}
O código anterior pode ser usado para detectar "vazamento de memória", ou seja, a memória atribuída no repositório livre, mas nunca liberada. Para executar essa detecção, os operadores new
e delete
globais são redefinidos para contar a alocação e a desalocação de memória.
O compilador oferece suporte aos operadores new
e delete
da matriz de membros em uma declaração de classe. Por exemplo:
// 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;
}