Expresión new
Crea e inicializa objetos duración de almacenamiento dinámica. Es decir, objetos cuya duración no se limita al ámbito en el que se crearon.
Contenido |
[editar]Sintaxis
:: (opcional)new ( params_de_ubicación) (opcional)( tipo) inicializador(opcional) | (1) | ||||||||
:: (opcional)new ( params_de_ubicación) (opcional)tipoinicializador(opcional) | (2) | ||||||||
tipo
, que puede ser un tipo de array, y puede incluir un especificador de tipo marcador de posición(desde C++11), o incluir una plantilla de clase cuyo argumento se deducirá mediante la deducción de argumentos de plantillas de clase(desde C++17).tipo
no puede incluir paréntesis:new int(*[10])();// ERROR: analizado como (new int) (*[10]) () new (int(*[10])());// de acuerdo: asigna un array de 10 punteros a funciones
Además, el tipo sin paréntesis es voraz: incluirá todos los símbolos que puedan ser parte de un declarador:
new int+1;// de acuerdo: analizado como (new int) + 1,// incrementa un puntero devuelto por new int new int*1;// ERROR: analizado como (new int*) (1)
El inicializador
no es opcional si:
- un marcador de posición (
auto
odecltype(auto)
(desde C++14)) se usa entipo
tipo
es un array de límite desconocido
auto p = new auto('c');// crea un solo objeto de tipo. p es un char*double* p = new double[]{1,2,3};// crea un array de tipo double[3]
[editar]Explicación
La expresión new
intenta asignar almacenamiento y luego intenta construir e inicializar un solo objeto sin nombre o un array de objetos sin nombre en el almacenamiento asignado. La expresión new
devuelve un puntero prvalue al objeto construido o, si se construyó un array de objetos, un puntero al elemento inicial del array.
Si tipo
es un tipo de array, todas las dimensiones excepto la primera deben de especificarse como una expresión constante entera(hasta C++14)expresión constante convertida de tipo std::size_t(desde C++14) positiva, pero (solamente cuando se usa la sintaxis sin paréntesis (2)) la primera dimensión puede ser cualquier expresión convertible a std::size_t. Esta es la única manera de crear directamente un array con un tamaño definido en tiempo de ejecución. Tales arrays son referidos frecuentemente como arrays dinámicos:
int n =42;double a[n][5];// ERRORauto p1 = new double[n][5];// de acuerdoauto p2 = new double[5][n];// ERROR, solamente la primera dimensión puede ser variableauto p3 = new (double[n][5]);// ERROR, sintaxis (1) no puede usarse para arrays dinámicos
En los casos siguientes la expresión que especifica la primera dimensión es errónea:
- la expresión es de un tipo de no-clase y su valor antes de la conversión a std::size_t es negativo;
- la expresión es de un tipo de clase y su valor después de la función de conversión definida por el usuario y antes de la segunda conversión estándar es negativo;
- el valor de la expresión es más grande que algún límite definido por la implementación;
- el valor es más pequeño que el número de elementos del array proporcionados en el inicializador entre llaves (incluyendo el terminador
'\0'
en un literal de cadena).
Si el valor en la primera dimensión es erróneo por cualquiera de estas razones:
- Si después de la conversión a std::size_t la primera dimensión es una expresión constante central, el programa está mal formado (se emite un error en tiempo de compilación).
| (desde C++14) |
| (desde C++11) |
La primera dimensión de cero es aceptable, y se llama a la función de asignación.
Nota: std::vector ofrece una funcionalidad similar para arrays dinámicos unidimensionales.
[editar]Asignación
La expresión new
asigna almacenamiento llamando a la función de asignación apropriada. Si tipo
es un tipo que no es array, el nombre de la función es operator new
. Si tipo
es un tipo de array, el nombre de la función es operator new[]
.
Como se describe en la función de asignación, el programa de C++ puede proporcionar reemplazos globales y específicos de clase para estas funciones. Si la expresión new
comienza con el operador opcional ::, como en ::new T o ::new T [n] , los reemplazos específicos de la clase serán ignorados (la función es buscada en el ámbito global). De lo contrario, si T
es un tipo de clase, la búsqueda comienza en el ámbito de clase de T
.
Al llamar a la función de asignación, la expresión new
pasa el número de bytes solicitados como primer argumento, de tipo std::size_t, que es exactamente sizeof(T) para una T
que no es un array.
La asignación de un array puede suplementar un valor adicional no especificado, que puede variar de una llamada a new
a la siguiente. El puntero devuelto por la expresión new
será compensado con ese valor adicional del puntero devuelto por la función de asignación. Muchas implementaciones usan ese valor adicional del array para almacenar el número de objetos en el array, que se utiliza por la expresión delete [] para llamar al número correcto de destructores. Además, si la expresión new
se usa para asignar un array de char, unsignedchar, o std::byte, puede solicitar memoria adicional de la función de asignación si es necesario para garantizar la alineación correcta de los objetos de todos los tipos que no sean mayores que el tamaño del array solicitado, si luego se coloca uno de tales objetos en el array asignado.
Se permite que las expresiones 1) La duración del objeto asignado por E1 contiene estrictamente la duración del objeto asignado por E2. 2) E1 y E2 invocarían la misma función de asignación global reemplazable. 3) Para una función de asignación que lanza, las excepciones en E1 y E2 serían primero atrapadas en el mismo controlador. Observa que esta optimización solamente se permite cuando se usan las expresiones | (desde C++14) |
Durante una evaluación de una expresión constante, una llamada a una función de asignación siempre se omite. Solamente las expresiones | (desde C++20) |
[editar]New de ubicación
Si se proporcionan params_de_ubicación
se pasan a la función de asignación como argumentos adicionales. Tales funciones de asdignación se conocen como "new
de ubicación", debido a la función de asignación estándar void*operator new(std::size_t, void*), que simplemente devuelve su segundo argumento sin cambiarlo. Esto se utiliza para construir objetos en almacenamiento asignado:
char* ptr = new char[sizeof(T)];// asignar memoria T* tptr = new(ptr) T;// construir en almacenamiento asignado ("ubicar") tptr->~T();// destruir delete[] ptr;// desasignar memoria
Nota: esta funcionalidad se encapsula por las funciones miembro de las clases Allocator.
Cuando se asigna un objeto cuyo requerimiento de alineación excede | (desde C++17) |
new T;// llama al operador new(sizeof(T))// (C++17) o al operador new(sizeof(T), std::align_val_t(alignof(T)))) new T[5];// llama al operador new[](sizeof(T)*5 + overhead)// (C++17) o al operador new(sizeof(T)*5+overhead, std::align_val_t(alignof(T)))) new(2,f) T;// llama al operador new(sizeof(T), 2, f)// (C++17) o al operador new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
Si la función de asignación devuelve un puntero nulo, lo cual es posible si se seleccionó la sobrecarga que no lanza, p. ej., con new(std::nothrow) T;, entonces la expresión new
regresa inmediatamente, no intenta inicializar un objeto o llamar a una función de desasignación. Si la función de asignación de ubicación estándar devuelve un puntero nulo, que es posible si el usuario pasa un puntero nulo como argumento, el comportamiento es indefinido.(desde C++17)
[editar]Construcción
El objeto creado por una expresión new
se inicializa de acuerdo a las siguientes reglas:
- Para un
tipo
que no es array, el objeto individual se construye en el área de memoria adquirida.
- Si inicializador está ausente, el objeto es inicializado mediante la inicialización por defecto.
- Si inicializador es una lista de argumentos entre paréntesis, el objeto es inicializado mediante la inicialización directa.
| (desde C++11) |
- Si tipo es un tipo de array, se inicializa un array de objetos.
- Si inicializador está ausente, cada elemento es inicializado mediante la inicialización por defecto
- Si inicializador es un par de paréntesis vacío, cada elemento es inicializado mediante la inicialización de un valor.
| (desde C++11) |
| (desde C++20) |
Si la inicialización termina lanzando una excepción (por ejemplo, del constructor), si expresión new
asignó algún almacenamiento, llama a la función de desasignación apropiada: operator delete cuando tipo
no es de tipo array, operator delete[] cuando tipo
es un tipo de array. La función de desasignación se busca en el ámbito global si la expresión new
utiliza la sintaxis ::new, de lo contrario, se busca en el ámbito de T
, si T
es un tipo de clase. Si la función de asignación fallida era la habitual (sin colocación), la búsqueda de la función de desasignación sigue las reglas descritas en la expresión delete
. Para new
de ubicación fallida, todos los tipos de parámetros, excepto el primero, de la función de desasignación coincidente deben ser idénticos a los parámetros de new
de ubicación. La llamada a la función de desasignación se convierte en el valor obtenido anteriormente de la función de asignación pasada como primer argumento, la alineación pasada como el argumento de alineación opcional(desde C++17), y params_de_ubicación
, si hay alguno, pasados como argumentos de ubicación adicionales. Si no se encuentra ninguna función de desasignación, la memoria no se desasigna.
[editar]Fugas de memoria
Los objetos creados por las expresiones new
(objetos con duración de almacenamiento dinámica) persisten hasta que el puntero devuelto por la expresión new
se usa en una expresión delete
coincidente. Si el valor original del puntero se pierde, el objeto se vuelve inalcanzable y no puede desasignarse: ocurre una fuga de memoria.
Esto puede suceder si se asigna al puntero:
int* p = new int(7);// int asignado dinámicamente con el valor 7 p = nullptr;// fuga de memoria
o si el puntero sale de ámbito:
void f(){int* p = new int(7);}// fuga de memoria
o debido a una excepción
void f(){int* p = new int(7); g();// puede lanzar delete p;// de acuerdo si no hay excepción}// fuga de memoria si g() lanza
Para simplificar la administración de objetos asignados dinámicamente, el resultado de una expresión new
frecuentemente se almacena en un puntero inteligente: std::auto_ptr(hasta C++17)std::unique_ptr, o std::shared_ptr(desde C++11). Estos punteros garantizan que la expresión delete
se ejecuta en las situaciones mostradas anteriormente.
[editar]Palabras clave
[editar]Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 1992 | C++14 | new (std::nothrow) int[N] podía lanzar bad_array_new_length . | Se cambió para que devolviera un puntero nulo. |
P1009R2 | C++11 | El límite del array no se puede deducir en una expresión new . | Se permite la deducción. |