Default-initialization
Esta es la inicialización realizada cuando un objeto se construye sin inicializador.
Contenidos |
Sintaxis
T objeto
;
|
(1) | ||||||||
new
T
|
(2) | ||||||||
Explicación
La inicialización por defecto se realiza en tres situaciones:
Los efectos de la inicialización por defecto son:
-
si
Tes un tipo clase (posiblemente calificado cv) non-POD (until C++11) , se consideran los constructores y se someten a overload resolution contra la lista de argumentos vacía. El constructor seleccionado (que es uno de los default constructors ) se llama para proporcionar el valor inicial para el nuevo objeto; -
si
Tes un tipo array, cada elemento del array es default-initialized; - de lo contrario, no se realiza ninguna inicialización (ver notes ).
|
Solo los tipos de clase no-POD (posiblemente calificados cv) (o arreglos de estos) con duración de almacenamiento automático se consideraban inicializados por defecto cuando no se usaba un inicializador. Los tipos escalares y POD con duración de almacenamiento dinámico se consideraban no inicializados (desde C++11, esta situación se reclasificó como una forma de inicialización por defecto). |
(until C++11) |
|
(hasta C++11) |
|
(desde C++11) |
cada
subobjeto potencialmente construido
de la clase base de
T
es const-default-constructible.
Valores indeterminados y erróneos
|
Cuando se obtiene almacenamiento para un objeto con duración de almacenamiento automática o dinámica, el objeto tiene un valor indeterminado . Si no se realiza inicialización para un objeto, ese objeto retiene un valor indeterminado hasta que ese valor es reemplazado. |
(until C++26) |
|
Cuando se obtiene almacenamiento para un objeto con duración de almacenamiento automática o dinámica, los bytes que componen el almacenamiento para el objeto tienen el siguiente valor inicial:
Si no se realiza inicialización para un objeto (incluyendo subobjetos ), dicho byte retiene su valor inicial hasta que ese valor es reemplazado.
|
(since C++26) |
Si una evaluación produce un valor indeterminado, el comportamiento es undefined .
|
Si una evaluación produce un valor erróneo, el comportamiento es erróneo . |
(since C++26) |
Casos especiales
Los siguientes tipos son uninitialized-friendly :
| (desde C++17) |
- unsigned char
- char , si su tipo subyacente es unsigned char
Dado un valor indeterminado o erróneo (desde C++26) , value , el valor de resultado no inicializado de value es:
- Un valor indeterminado, si value también es un valor indeterminado.
|
(desde C++26) |
Si una evaluación eval produce un valor indeterminado o erróneo (desde C++26) de un tipo uninitialized-friendly, el comportamiento está bien definido en los siguientes casos:
- eval es la evaluación de una de las siguientes expresiones y operandos:
-
- El segundo o tercer operando de una expresión condicional .
- El operando derecho de una expresión coma .
-
El operando de una
conversión integral
,
conversión explícita
o
static_casta un tipo compatible con no inicialización. - Una expresión de valor descartado .
- En este caso, el resultado de la operación es el valor de resultado no inicializado de value .
- eval es una evaluación del operando derecho de un operador de asignación simple cuyo operando izquierdo es un lvalue de un tipo uninitialized-friendly.
- En este caso, el valor del objeto referido por el operando izquierdo es reemplazado por el valor resultante no inicializado de value .
- eval es la evaluación de la expresión de inicialización al inicializar un objeto de un tipo amigable con la no inicialización.
| (desde C++17) |
- En este caso, ese objeto se inicializa al valor de resultado no inicializado de value .
Convertir un valor indeterminado de un tipo amigable a la no inicialización produce un valor indeterminado.
|
Convertir un valor erróneo de un tipo amigable a la no inicialización produce un valor erróneo, el resultado de la conversión es el valor del operando convertido. |
(since C++26) |
// Caso 1: Objetos no inicializados con duración de almacenamiento dinámico // Todas las versiones de C++: valor indeterminado + comportamiento indefinido int f(bool b) { unsigned char* c = new unsigned char; unsigned char d = *c; // OK, "d" tiene un valor indeterminado int e = d; // comportamiento indefinido return b ? d : 0; // comportamiento indefinido si "b" es verdadero } // Caso 2: Objetos no inicializados con duración de almacenamiento automático // hasta C++26: valor indeterminado + comportamiento indefinido // desde C++26: valor erróneo + comportamiento erróneo int g(bool b) { unsigned char c; // "c" tiene un valor indeterminado/erróneo unsigned char d = c; // sin comportamiento indefinido/erróneo, // pero "d" tiene un valor indeterminado/erróneo assert(c == d); // se cumple, pero ambas promociones integrales tienen // comportamiento indefinido/erróneo int e = d; // comportamiento indefinido/erróneo return b ? d : 0; // comportamiento indefinido/erróneo si "b" es verdadero } // Igual que el caso 2 void h() { int d1, d2; // "d1" y "d2" tienen valores indeterminados/erróneos int e1 = d1; // comportamiento indefinido/erróneo int e2 = d1; // comportamiento indefinido/erróneo assert(e1 == e2); // se cumple assert(e1 == d1); // se cumple, comportamiento indefinido/erróneo assert(e2 == d1); // se cumple, comportamiento indefinido/erróneo // sin comportamiento indefinido/erróneo, // pero "d2" tiene un valor indeterminado/erróneo std::memcpy(&d2, &d1, sizeof(int)); assert(e1 == d2); // se cumple, comportamiento indefinido/erróneo assert(e2 == d2); // se cumple, comportamiento indefinido/erróneo }
Notas
Las referencias y los objetos escalares const no pueden inicializarse por defecto.
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_constexpr
|
201907L
|
(C++20) | Inicialización por defecto trivial y declaración asm en funciones constexpr |
Ejemplo
#include <string> struct T1 { int mem; }; struct T2 { int mem; T2() {} // “mem” no está en la lista de inicialización }; int n; // estático no-clase, se realiza una inicialización en dos fases: // 1) inicialización a cero inicializa n a cero // 2) inicialización por defecto no hace nada, dejando n en cero int main() { [[maybe_unused]] int n; // no-clase, el valor es indeterminado std::string s; // clase, llama al constructor por defecto, el valor es "" std::string a[2]; // array, inicializa por defecto los elementos, el valor es {"", ""} // int& r; // Error: una referencia // const int n; // Error: un const no-clase // const T1 t1; // Error: clase const con constructor por defecto implícito [[maybe_unused]] T1 t1; // clase, llama al constructor por defecto implícito const T2 t2; // clase const, llama al constructor por defecto proporcionado por el usuario // t2.mem se inicializa por defecto }
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 178 | C++98 |
no existía la inicialización por valor;
el inicializador vacío invocaba la inicialización por defecto (aunque new T ( ) también realiza inicialización a cero) |
el inicializador vacío invoca
inicialización por valor |
| CWG 253 | C++98 |
la inicialización por defecto de un objeto const no podía
llamar a un constructor por defecto declarado implícitamente |
permitido si todos los subobjetos están inicializados |
| CWG 616 | C++98 |
la conversión de lvalue a rvalue de cualquier
objeto no inicializado siempre era UB |
se permite unsigned char indeterminado |
| CWG 1787 | C++98 |
la lectura de un
unsigned
char
indeterminado
almacenado en caché en un registro era UB |
se define correctamente |