En el lenguaje de programación C++ , new y delete son un par de construcciones del lenguaje que realizan asignación dinámica de memoria , construcción de objetos y destrucción de objetos . [ 1 ]
Descripción general
Excepto por una forma denominada "placement new" , el newoperador denota una solicitud de asignación de memoria en el montón de un proceso . Si hay suficiente memoria disponible, newinicializa la memoria, llamando a los constructores de objetos si es necesario, y devuelve la dirección de la memoria recién asignada e inicializada. [ 2 ] [ 3 ] Una nueva solicitud, en su forma más simple, se ve así:
T * p = nuevo T ; T * p = nuevo T (); // inicializa a cero los elementos primitivosdonde pes un puntero de tipo T(o algún otro tipo al que Tse pueda asignar un puntero, como una superclase de T). El constructor predeterminado para T, si existe, se llama para construir una Tinstancia en el búfer de memoria asignado.
Si no hay suficiente memoria disponible en el almacenamiento libre para un objeto de tipo T, la newsolicitud indica un fallo lanzando una excepción de tipo std::bad_alloc. Esto elimina la necesidad de comprobar explícitamente el resultado de una asignación.
La contraparte de desasignación de newes delete, que primero llama al destructor (si existe) en su argumento y luego devuelve la memoria asignada por de newvuelta al almacenamiento libre. Cada llamada a newdebe ir acompañada de una llamada a delete; de lo contrario, se produce una fuga de memoria . [ 1 ]
La nueva sintaxis tiene varias variantes que permiten un control más preciso sobre la asignación de memoria y la construcción de objetos. Se utiliza una sintaxis similar a la de una llamada a función para llamar a un constructor diferente al predeterminado y pasarle argumentos, por ejemplo:
T * p = nuevo T ( argumento );Al inicializar el búfer recién asignado, se llama a un constructor de un solo argumento Ten lugar del constructor predeterminado.
Otra variante asigna e inicializa matrices de objetos en lugar de objetos individuales:
T * p = nuevo T [ N ];Esto solicita un búfer de memoria del almacenamiento libre que sea lo suficientemente grande como para contener una matriz contigua de Nobjetos de tipo T, y llama al constructor predeterminado en cada elemento de la matriz.
La memoria asignada con new[]debe liberarse con el delete[]operador, en lugar de delete. Usar la forma inapropiada produce un comportamiento indefinido . Los compiladores de C++ no están obligados a generar un mensaje de diagnóstico por usar la forma incorrecta.
El estándar C++11 especifica una sintaxis adicional,
constexpr size_t N = /* alguna longitud */ ;T * p = nuevo T [ N ] { inicializador1 , ..., inicializadorN };que inicializa cada p[ i ] al inicializador i +1 .
Java y C# tienen el newoperador (que llama al constructor de la clase ), pero no el deleteoperador. Java no tiene destructores . C# tiene destructores, pero se usan raramente y no se pueden llamar a través del deleteoperador.
clase pública Persona { cadena privada nombre ; entero privado edad ;public Persona ( String nombre , int edad ) { this . nombre = nombre ; this . edad = edad ; } }Persona p = nueva Persona ( "Alicia" , 30 );Aunque en Rust no hay constructores de estilo orientado a objetos, la convención es nombrar un método de fábrica para instanciar una estructura new(generalmente devolviendo Self), mientras que la eliminación manual se realiza implementando std::ops::Drop. [ 4 ]
JavaScript y TypeScript cuentan con los operadores ` newand` y `off` delete. Sin embargo, mientras que `and` newse utiliza para llamar al constructor de la clase, `off` deleteno llama a un destructor (ya que los destructores no existen en JavaScript ni en TypeScript). En cambio, ` deleteoff` elimina una propiedad de un objeto.
clase Usuario { nombre : cadena ; edad ?: número ; // marcado como opcional para permitir la eliminaciónconstructor ( nombre : cadena , edad? : número ) { this . nombre = nombre ; this . edad = edad ; } }let user : User = new User ( "Alice" , 30 );eliminar usuario.edad ; // Sin error de TypeScript// Salida: { nombre: "Alice" } console . log ( usuario );En C++, deletetambién se utiliza para deshabilitar métodos generados por el compilador, como los constructores predeterminados. En C++26 , es posible proporcionar una razón para su eliminación.
clase NonCopyable { public : NonCopyable () = default ; NonCopyable ( const NonCopyable & ) = delete ( "No se permite la copia" ); NonCopyable & operator = ( const NonCopyable & ) = delete ( "No se permite la copia" ); };Manejo de errores
Si newno puede encontrar suficiente memoria para atender una solicitud de asignación, puede informar de su error de tres maneras distintas. En primer lugar, el estándar ISO C++ permite que los programas registren una función personalizada llamada con el entorno de ejecuciónnew_handler de C++ ; si lo hace, esta función se llama cada vez que encuentra un error. El puede intentar liberar más memoria o terminar el programa si no puede.newnew_handler
Si no new_handlerestá instalado, newen su lugar lanza una excepción de tipo std::bad_alloc. Por lo tanto, el programa no necesita comprobar el valor del puntero devuelto, como es costumbre en C ; si no se lanzó ninguna excepción, la asignación tuvo éxito.
El tercer método de manejo de errores lo proporciona la forma variante new(std::nothrow), que especifica que no se debe lanzar ninguna excepción; en su lugar, se devuelve un puntero nulo para señalar un error de asignación.
Sobrecarga
El newoperador puede sobrecargarse para que tipos específicos (clases) utilicen algoritmos de asignación de memoria personalizados para sus instancias. Por ejemplo, la siguiente es una variante del patrón Singleton donde la primera new Singleton()llamada asigna una instancia y todas las llamadas posteriores devuelven esa misma instancia:
importar std ;clase Singleton { privado : static void * instancia = nullptr ; static size_t refcount = 0 ; público : static void * operador nuevo ( size_t tamaño ) { if ( ! instancia ) { instancia = std :: malloc ( tamaño ); } ++ refcount ; return instancia ; }static void operator delete ([[ maybe_unused ]] void * p ) noexcept { if ( -- refcount == 0 ) { std :: free ( instance ); instance = nullptr ; } } };Esta característica estuvo disponible desde los inicios de la historia de C++, aunque el mecanismo de sobrecarga específico cambió. Se añadió al lenguaje porque los programas C++ orientados a objetosnew tendían a asignar muchos objetos pequeños con , que internamente utilizaba el asignador de C (véase § Relación con malloc y free ); sin embargo, este último estaba optimizado para las asignaciones más grandes y menos numerosas que realizaban los programas C típicos. Stroustrup informó que en las primeras aplicaciones, la función de C malloc()era "el cuello de botella de rendimiento más común en los sistemas reales", y los programas dedicaban hasta el 50% de su tiempo a esta función. [ 5 ]
Relación con malloc y free
Dado que el estándar C++ engloba la biblioteca estándar de C , las rutinas de asignación dinámica de memoria de Cmalloc() , calloc()y también están disponibles para los programadores de C++. Se desaconseja el uso de estas rutinas en la mayoría de los casos, ya que no realizan la inicialización ni la destrucción de objetos. [ 6 ] y , de hecho, se introdujeron en la primera versión de C++ (entonces llamada " C con Clases ") para evitar la necesidad de inicializar objetos manualmente. [ 5 ]realloc()free()newdelete
A diferencia de las rutinas de C, que permiten aumentar o disminuir el tamaño de un array asignado con realloc(), no es posible cambiar el tamaño de un búfer de memoria asignado por new[]. La biblioteca estándar de C++ proporciona en su lugar un array dinámico (colección) que se puede ampliar o reducir en su clase de plantilla std::vector .
El estándar C++ no especifica ninguna relación entre new/ deletey las rutinas de asignación de memoria de C, pero newy deletese implementan típicamente como envolturas alrededor de malloc()y free(). [ 7 ] Mezclar las dos familias de operaciones, por ejemplo, free()-ing newmemoria asignada -ly o delete-ing malloc()memoria -ed, causa un comportamiento indefinido y en la práctica puede conducir a varios resultados catastróficos, como la falla para liberar bloqueos y, por lo tanto, un interbloqueo . [ 8 ]
Véase también
Referencias
- 1 2 Savitch, Walter (2013). Absolute C++ . Pearson. pp. 420– 445. ISBN 978-0132846813.
- ↑ "Documentación de IBM que describe el operador new de C++" . Consultado el 6 de noviembre de 2013 .
{{cite web}}: CS1 maint: servicio de archivado obsoleto ( enlace ) - ↑ "Nueva documentación del operador de Microsoft Visual Studio" . Consultado el 6 de noviembre de 2013 .
- ↑ "Destructores - la referencia de Rust" .
- 1 2 Stroustrup, Bjarne (1993). Una historia de C++: 1979–1991 (PDF) . Actas de la Conferencia ACM sobre la historia de los lenguajes de programación.
- ↑ Meyers, Scott (1998). Effective C++ . Addison-Wesley. p . 21. ISBN 9780201924886.
- ↑ Alexandrescu, Andrei (2001). Diseño moderno de C++: Programación genérica y patrones de diseño aplicados . Addison-Wesley. pág. 68 .
- ↑ Seacord, Robert C. (2013). Codificación segura en C y C++ . Addison-Wesley.Sección 4.4, Errores comunes de gestión de memoria en C++.
- C++