Operadores new
y delete
C++ admite la asignación dinámica y la desasignación de objetos mediante los operadores new
y delete
. Estos operadores asignan memoria para los objetos de un grupo denominado almacén libre (también conocido como montón). El operador new
llama a la función especial operator new
y el operador delete
llama a la función especial operator delete
.
Para obtener una lista de los archivos de biblioteca que componen la biblioteca en tiempo de ejecución de C y la biblioteca estándar de C++, consulte Características de la biblioteca CTR.
El operador new
El compilador traduce una instrucción como esta en una llamada a la función operator new
:
char *pch = new char[BUFFER_SIZE];
Si la solicitud es para cero bytes de almacenamiento, operator new
devuelve un puntero a un objeto distinto. Es decir, al llamar repetidamente a operator new
se devuelven punteros diferentes.
Si no hay memoria suficiente para la solicitud de asignación, operator new
produce una excepción std::bad_alloc
. O bien, devuelve nullptr
si ha usado el formulario de colocación de new(std::nothrow)
o si ha vinculado en compatibilidad con operator new
sin inicio. Para obtener más información, consulte Comportamiento de los errores de asignación.
En la tabla siguiente se describen los dos ámbitos de las funciones operator new
.
Ámbito de las funciones operator new
Operador | Ámbito |
---|---|
::operator new |
Global |
class-name::operator new |
Clase |
El primer argumento de operator new
debe ser de tipo size_t
, y el tipo de valor devuelto siempre será void*
.
Se llama a la función global operator new
cuando el operador new
se usa para asignar objetos de tipos integrados, objetos de tipo de clase que no contienen funciones operator new
definidas por el usuario y matrices de cualquier tipo. Cuando el operador new
se usa para asignar objetos de un tipo de clase en la que se ha definido operator new
, se llama a la función operator new
de esa clase.
Una función operator new
definida para una clase es una función miembro estática (que no puede ser virtual) que oculta la función global operator new
para los objetos de ese tipo de clase. Imagine que se usa new
para asignar y establecer la memoria en un valor determinado:
#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;
}
El argumento proporcionado entre paréntesis a new
se pasa a Blanks::operator new
como el argumento chInit
. Sin embargo, se oculta la función global operator new
, haciendo que código como el siguiente genere un error:
Blanks *SomeBlanks = new Blanks;
El compilador admite los operadores new
y delete
de la matriz de miembro en una declaración de clase. Por ejemplo:
class MyClass
{
public:
void * operator new[] (size_t)
{
return 0;
}
void operator delete[] (void*)
{
}
};
int main()
{
MyClass *pMyClass = new MyClass[5];
delete [] pMyClass;
}
Comportamiento de los errores de asignación
La función new
de la biblioteca estándar de C++ admite el comportamiento especificado en el estándar de C++ desde C++98. Cuando no hay memoria suficiente para una solicitud de asignación, operator new
produce una excepción std::bad_alloc
.
El código de C++ anterior devolvió un puntero nulo para una asignación con errores. Si tiene código que espera la versión no iniciada de new
, vincule el programa con nothrownew.obj
. El archivo nothrownew.obj
reemplaza al operator new
global con una versión que devuelve nullptr
si se produce un error en una asignación. operator new
ya no produce std::bad_alloc
. Para obtener más información sobre nothrownew.obj
y otros archivos de opciones del enlazador, consulte Opciones de vínculo.
No se puede mezclar código que comprueba si hay excepciones del operator new
global con código que comprueba si hay punteros nulos en la misma aplicación. Sin embargo, sigue pudiendo crear un elemento operator new
local de clase que se comporte de forma diferente. Esta posibilidad significa que el compilador debe actuar defensivamente de manera predeterminada e incluir comprobaciones para devoluciones de punteros nulos en las llamadas a new
. Para obtener más información sobre cómo optimizar estas comprobaciones del compilador, consulte /Zc:throwingnew
.
Controlar la memoria insuficiente
La forma en que se prueba una asignación con errores de una expresión new
depende de si se usa el mecanismo de excepción estándar o se usa una devolución nullptr
. C++ estándar espera que un asignador produzca std::bad_alloc
o una clase derivada de std::bad_alloc
. Puede controlar una excepción como esta, como se muestra en este ejemplo:
#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;
}
}
Cuando se usa la forma nothrow
de new
, puede probar si se produce un error de asignación, como se muestra en este ejemplo:
#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;
}
}
Puede probar si se produce una asignación de memoria con errores cuando haya usado el archivo nothrownew.obj
para reemplazar el operator new
global, como se muestra aquí:
#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;
}
}
Puede proporcionar un controlador para las solicitudes de asignación de memoria con errores. Es posible escribir una rutina de recuperación personalizada para controlar este tipo de errores. Por ejemplo, podría liberar memoria reservada y, a continuación, permitir que la asignación se ejecute de nuevo. Para obtener más información, vea _set_new_handler
.
El operador delete
La memoria que se asigna dinámicamente mediante el operador new
se puede liberar con el operador delete
. El operador delete llama a la función operator delete
, que libera memoria para que la use el grupo disponible. El uso del operador delete
también ocasiona una llamada al destructor de clase (si existe alguno).
Hay funciones operator delete
globales y de ámbito de clase. Solo se puede definir una función operator delete
para una clase dada; si se define, oculta la función operator delete
global. La función operator delete
global siempre se llama para las matrices de cualquier tipo.
La función operator delete
global. Existen dos formularios para las funciones operator delete
global y operator delete
de miembro de clase:
void operator delete( void * );
void operator delete( void *, size_t );
Solo uno de los dos formularios anteriores puede estar presente para una clase determinada. El primer formulario toma un único argumento del tipo void *
, que contiene un puntero al objeto que se debe desasignar. El segundo formulario, una desasignación con tamaño, toma dos argumentos: el primero es un puntero al bloque de memoria que se va a desasignar y el segundo es el número de bytes que se van a desasignar. El tipo de valor devuelto de ambos formularios es void
(operator delete
no puede devolver un valor).
La intención del segundo formulario es acelerar la búsqueda de la categoría de tamaño correcta del objeto que se va a eliminar. Esta información a menudo no se almacena cerca de la propia asignación y es probable que no se almacene en caché. El segundo formulario resulta especialmente útil cuando se usa una función operator delete
de una clase base para eliminar un objeto de una clase derivada.
La función operator delete
es estática, por lo que no puede ser virtual. La función operator delete
obedece al control de acceso, tal como se describe en Control de acceso a miembros.
En el ejemplo siguiente se muestran las funciones operator new
y operator delete
definidas por el usuario, diseñadas para registrar asignaciones y desasignaciones de memoria:
#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;
}
El código anterior se puede utilizar para detectar "fugas de memoria", es decir, la memoria que se asigna en el almacén libre pero que nunca se libera. Para detectar fugas, los operadores new
y delete
globales se redefinen para determinar la asignación y desasignación de memoria.
El compilador admite los operadores new
y delete
de la matriz de miembro en una declaración de clase. Por ejemplo:
// 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;
}
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente las Cuestiones de GitHub como mecanismo de retroalimentación para el contenido y lo sustituiremos por un nuevo sistema de retroalimentación. Para más información, consulta:Enviar y ver comentarios de