La necesidad de inicializar sin constructor
(consteval.ca)Necesito inicializar y no hay constructor
-
Introducción
- Cuando aprendí C++ por primera vez, aprendí en qué casos el compilador proporciona un constructor por defecto.
- Eso me llevó a pensar en el riesgo de que un objeto pueda quedar sin inicializar en ciertas situaciones.
-
Inicialización por defecto e inicialización por valor
T t;realiza inicialización por defecto.- Si
Tes un tipo de clase y tiene constructor por defecto, este se ejecuta. - Si
Tes un tipo arreglo, cada elemento se inicializa por defecto. - En caso contrario, no hace nada.
- Si
T t{};realiza inicialización por valor.- Si
Tes un tipo de clase, se inicializa por defecto si no tiene constructor por defecto o si tiene un constructor por defecto proporcionado por el usuario o eliminado. - En caso contrario, primero se inicializa en 0 y luego se inicializa por defecto.
- Si
Tes un tipo arreglo, cada elemento se inicializa por valor. - En caso contrario, se inicializa en 0.
- Si
-
Constructor por defecto
- Si no se declara un constructor por defecto, el compilador lo declara implícitamente.
- El constructor por defecto declarado implícitamente tiene un cuerpo vacío y una lista de inicialización de miembros vacía.
- Ejemplo:
struct T { int x; T() = default; }; T t{}; std::cout << t.x << std::endl; // el resultado es 0
-
Constructor por defecto definido implícitamente
- Si el constructor por defecto se declara implícitamente o se declara explícitamente como predeterminado, el compilador proporciona un constructor por defecto definido implícitamente.
- Ejemplo:
struct T { T(); }; T::T() = default; T t{}; std::cout << t.x << std::endl; // el resultado es un valor basura
-
Casos en los que no se puede proporcionar un constructor por defecto
- Cuando
Ttiene un miembro de referencia no estático - Cuando
Ttiene un miembro no estático o una clase base no abstracta que no puede construirse por defecto o destruirse - Cuando
Ttiene un miembroconstno estático sin inicializador de miembro por defecto
- Cuando
-
Inicialización correcta
T t{};realiza inicialización por lista.- La inicialización por lista se divide en inicialización directa por lista e inicialización por copia con lista.
- Ejemplo:
struct S { int a; float b; char c; }; S s{3, 4.0f, 'S'}; // no se llama a ningún constructor
-
Inicialización por lista e inicialización de agregados
- La inicialización de agregados es una forma especial de inicialización por lista, en la que cada elemento de una clase o arreglo se inicializa por copia con cada elemento de la lista de inicialización.
- Ejemplo:
struct A { const int x; }; A a{}; // a.x se inicializa en 0
-
Inicialización con paréntesis
- La inicialización con paréntesis realiza inicialización directa no basada en lista.
- Ejemplo:
struct T { const int& r; }; T t(42); // t.r es una referencia que apunta a 42
-
Resumen
- Las reglas de inicialización son complejas, pero escribir constructores directamente permite evitar la mayoría de los problemas.
- Es mejor no dejarlo en manos del compilador y escribir los constructores uno mismo.
Opinión de GN⁺
- Este artículo explica bien la complejidad de las reglas de inicialización en C++.
- Entender las reglas de inicialización de C++ es importante, ya que tienen un gran impacto en la estabilidad y el rendimiento del código.
- Escribir constructores directamente es la mejor forma de evitar problemas de inicialización.
- Un lenguaje con funcionalidades similares es Rust, y en Rust las reglas de inicialización son más claras.
- Al adoptar nuevas tecnologías, es importante comprender y usar bien detalles como las reglas de inicialización.
1 comentarios
Comentarios de Hacker News
El resultado de inicializar
tserá 0tse inicializa por valor y, comoTno tiene un constructor predeterminado definido por el usuario, el objeto se inicializa a 0 y luego se llama al constructor predeterminadoEl constructor predeterminado inicializa los miembros de forma predeterminada, lo cual es distinto de la inicialización por valor
GCC parece estar de acuerdo con esto
El autor pasó por alto que en realidad está inicializando
xpor valorLos detalles de las reglas son complejos y a veces tienen partes poco razonables
Hacer explícita la inicialización predeterminada sería una gran mejora
std::array<int, 100> = void;sería mejorEl vínculo entre la inicialización por lista y la inicialización de agregados es que, cuando la inicialización por lista se realiza sobre un agregado, se lleva a cabo la inicialización de agregados
El caso de un solo elemento funciona distinto al de dos o más elementos
Uno puede escribir su propio constructor e inicializar una tupla o arreglo al que se le proporcione un solo elemento
Cuando aparecieron por primera vez las listas de inicialización de C++11, alguien notó esto y pensó que era una locura
Se menciona "I Have No Mouth, and I Must Scream" (1967)
Uso de la sintaxis
T::T() = default;Se espera que la salida sea 0, pero en realidad aparecerá un valor basura
Permite que quien consume la biblioteca pueda cambiar el comportamiento de la biblioteca
Si se quiere más complejidad de C++, se recomienda C++ FQA
El tema del blog está inspirado en computadoras de la era DEC, pero se ve limpio y minimalista
Leer esto provoca mareo
Go y Rust no tienen constructores especiales, lo que simplifica muchas cosas
Da curiosidad si existe alguna herramienta de C++ que muestre todos los comportamientos implícitos
Se da información incorrecta sobre la clase
La afirmación de que
T t;"no hace nada" es incorrectaT t;fallaEn el encabezado del blog hay un panel frontal de DEC