oz10
Soy partidario de usar listas de inicializadores de miembros para mis constructores, pero hace mucho tiempo que olvidé las razones detrás de esto.
¿Utiliza listas de inicializadores de miembros en sus constructores? Si es así, ¿por qué? ¿Si no, porque no?
adam rosenfield
Para los miembros de datos POD, no hay diferencia, es solo una cuestión de estilo. Para los miembros de clase que son clases, evita una llamada innecesaria a un constructor predeterminado. Considerar:
class A
{
public:
A() { x = 0; }
A(int x_) { x = x_; }
int x;
};
class B
{
public:
B() { a.x = 3; }
private:
A a;
};
En este caso, el constructor de B
llamará al constructor predeterminado para A
y luego inicializar a.x
a 3
. Una mejor manera sería para B
constructor de para llamar directamente A
constructor de en la lista de inicializadores:
B() : a(3) {}
Esto solo llamaría A
‘s A(int)
constructor y no su constructor predeterminado. En este ejemplo, la diferencia es insignificante, pero imagina si quieres que A
El constructor predeterminado de hizo más, como asignar memoria o abrir archivos. No querrías hacer eso innecesariamente.
Además, si una clase no tiene un constructor predeterminado, o si tiene un const
o miembro de datos de referencia, usted debe use una lista de inicializadores:
class A
{
public:
A(int x_) { x = x_; }
int x;
};
class B
{
public:
// 'a' and 'y' MUST be initialized in an initializer list;
// it is an error not to do so.
B() : a(3), y(2) {}
private:
A a;
const int y;
};
-
una necesidad es también para el caso importante de una referencia
– 4pie0
6 mayo 2014 a las 12:38
-
¿Por qué no usar “a(3);” o “a = A(3);” en el cuerpo del constructor predeterminado de B?
– sergey
6 de marzo de 2015 a las 10:07
-
¿Podría explicar qué quiere decir con POD?
– Jonás Stein
19 de noviembre de 2015 a las 11:12
-
@JonasStein POD es un conjunto bien definido de reglas relacionadas con estructuras de datos simples (en lugar de clases completas). Lea las preguntas frecuentes para obtener más información: stackoverflow.com/questions/146452/what-are-pod-types-in-c
– mono0506
28 de marzo de 2016 a las 4:02
-
@Sergey, todavía se llamaría al constructor predeterminado de A.
– Vasilis
2 mayo 2017 a las 18:24
naveen
Además de las razones de rendimiento mencionadas anteriormente, si su clase almacena referencias a objetos pasados como parámetros de constructor o su clase tiene variables constantes, entonces no tiene otra opción que usar listas de inicializadores.
-
Lo mismo ocurre con los miembros constantes, creo.
-Richard Corden
29 de mayo de 2009 a las 16:44
-
sí, no se puede usar la asignación para modificar las variables constantes, por lo que debe inicializarse.
– Hareen Laks
14 de abril de 2018 a las 13:53
yuvi
- Inicialización de la clase base
Una razón importante para usar la lista de inicializadores del constructor que no se menciona en las respuestas aquí es la inicialización de la clase base.
Según el orden de construcción, la clase base debe construirse antes que la clase secundaria. Sin la lista de inicializadores del constructor, esto es posible si su clase base tiene un constructor predeterminado que se llamará justo antes de ingresar al constructor de la clase secundaria.
Pero, si su clase base solo tiene un constructor parametrizado, entonces debe usar la lista de inicializadores del constructor para asegurarse de que su clase base se inicialice antes que la clase secundaria.
-
Inicialización de subobjetos que solo tienen constructores parametrizados
-
Eficiencia
Usando la lista de inicializadores del constructor, inicializa sus miembros de datos al estado exacto que necesita en su código en lugar de inicializarlos primero a su estado predeterminado y luego cambiar su estado al que necesita en su código.
- Inicializar miembros de datos const no estáticos
Si los miembros de datos const no estáticos en su clase tienen constructores predeterminados y no usa la lista de inicializadores de constructores, no podrá inicializarlos en el estado previsto, ya que se inicializarán en su estado predeterminado.
- Inicialización de miembros de datos de referencia
Los miembros de datos de referencia deben inicializarse cuando el compilador ingresa al constructor, ya que las referencias no pueden declararse e inicializarse más tarde. Esto solo es posible con la lista de inicializadores del constructor.
mloskot
Además de los problemas de rendimiento, hay otro muy importante al que llamaría mantenibilidad y extensibilidad del código.
si un T
es POD y comienza a preferir la lista de inicialización, entonces si una vez T
cambiará a un tipo que no sea POD, no necesitará cambiar nada en torno a la inicialización para evitar llamadas innecesarias al constructor porque ya está optimizado.
Si escribe T
tiene un constructor predeterminado y uno o más constructores definidos por el usuario y una vez que decide eliminar u ocultar el predeterminado, entonces, si se usó la lista de inicialización, no necesita actualizar el código de sus constructores definidos por el usuario porque ya están implementado correctamente.
Lo mismo con const
miembros o miembros de referencia, digamos inicialmente T
se define de la siguiente manera:
struct T
{
T() { a = 5; }
private:
int a;
};
A continuación, decide calificar a
como const
si usara la lista de inicialización desde el principio, entonces este fue un cambio de una sola línea, pero teniendo el T
definido como arriba, también requiere cavar la definición del constructor para eliminar la asignación:
struct T
{
T() : a(5) {} // 2. that requires changes here too
private:
const int a; // 1. one line change
};
No es un secreto que el mantenimiento es mucho más fácil y menos propenso a errores si el código no fue escrito por un “mono de código” sino por un ingeniero que toma decisiones basadas en una consideración más profunda sobre lo que está haciendo.
Antes de que se ejecute el cuerpo del constructor, se invocan todos los constructores para su clase principal y luego para sus campos. De forma predeterminada, se invocan los constructores sin argumentos. Las listas de inicialización le permiten elegir a qué constructor se llama y qué argumentos recibe ese constructor.
Si tiene un campo de referencia o const, o si una de las clases utilizadas no tiene un constructor predeterminado, debe usar una lista de inicialización.
amal k
// Without Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};
Aquí el compilador sigue los siguientes pasos para crear un objeto de tipo MyClass
:
Type
El constructor de se llama primero para “a
”.- El operador de asignación de “
Type
” se llama dentro del cuerpo deMyClass()
constructor a asignar.
variable = a;
- Y finalmente el destructor de “
Type
” se llama para “a
” ya que está fuera de alcance.
Ahora considere el mismo código con MyClass()
constructor con una lista de inicializadores:
// With Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};
Con la lista de inicializadores, el compilador sigue los siguientes pasos:
-
Copiar constructor de “
Type
Se llama a la clase para inicializar:variable(a)
. Los argumentos en la lista de inicializadores se utilizan para copiar la construcción “variable
” directamente. -
Destructor de “
Type
” se llama para “a
” ya que está fuera de alcance.
Daniel
Como se explica en las Directrices básicas de C++ C.49: Preferir la inicialización a la asignación en constructores
evita llamadas innecesarias a los constructores predeterminados.
Razones enumeradas aquí… https://www.geeksforgeeks.org/when-do-we-use-initializer-list-in-c/
– u8it
11 de junio de 2018 a las 22:17