La lista de inicializadores se utiliza para inicializar los miembros de datos de una clase. La lista de miembros que se inicializarán se indica con el constructor como una lista separada por comas seguida de dos puntos. A continuación se muestra un ejemplo que utiliza la lista de inicializadores para inicializar xey de la clase Punto.
Ejemplo
C++
#include> using> namespace> std;> class> Point {> private>:> >int> x;> >int> y;> public>:> >Point(>int> i = 0,>int> j = 0): x(i), y(j) {}> >/* The above use of Initializer list is optional as the> >constructor can also be written as:> >Point(int i = 0, int j = 0) {> >x = i;> >y = j;> >}> >*/> >int> getX()>const> {>return> x; }> >int> getY()>const> {>return> y; }> };> int> main()> {> >Point t1(10, 15);> >cout <<>'x = '> << t1.getX() <<>', '>;> >cout <<>'y = '> << t1.getY();> >return> 0;> }> |
>
>Producción
x = 10, y = 15>
El código anterior es sólo un ejemplo de sintaxis de la lista de inicializadores. En el código anterior, xey también se pueden inicializar fácilmente dentro del constructor. Pero hay situaciones en las que la inicialización de los miembros de datos dentro del constructor no funciona y se debe utilizar la Lista de inicializadores. Los siguientes son tales casos:
1. Para la inicialización de miembros de datos constantes no estáticos
Los miembros de datos constantes deben inicializarse utilizando la Lista de inicializadores. En el siguiente ejemplo, t es un miembro de datos constante de la clase Test y se inicializa usando la Lista de inicializadores. La razón para inicializar el miembro de datos constantes en la lista de inicializadores es porque no se asigna memoria por separado para el miembro de datos constantes, está plegado en la tabla de símbolos, por lo que debemos inicializarlo en la lista de inicializadores.
Además, es un constructor parametrizado y no necesitamos llamar al operador de asignación, lo que significa que evitamos una operación adicional.
Ejemplo
C++
// C++ progmram to demonstrate the use of> // initializer list to initialize the const> // data member> #include> using> namespace> std;> class> Test {> >const> int> t;> public>:> >//Initializer list must be used> >Test(>int> t):t(t) {}> >int> getT() {>return> t; }> };> int> main() {> >Test t1(10);> >cout< return 0; }> |
>
>Producción
10>
2. Para la inicialización de miembros de referencia
Los miembros de referencia deben inicializarse utilizando la Lista de inicializadores. En el siguiente ejemplo, t es un miembro de referencia de la clase Test y se inicializa mediante la Lista de inicializadores.
Ejemplo
C++
organización y arquitectura de la computadora
// Initialization of reference data members> #include> using> namespace> std;> class> Test {> >int> &t;> public>:> >Test(>int> &t):t(t) {}>//Initializer list must be used> >int> getT() {>return> t; }> };> int> main() {> >int> x = 20;> >Test t1(x);> >cout< x = 30; cout< return 0; }> |
>
>Producción
20 30>
3. Para inicialización de objetos miembro que no tienen un constructor predeterminado
En el siguiente ejemplo, un objeto a de clase A es un miembro de datos de clase B y A no tiene un constructor predeterminado. La lista de inicializadores se debe utilizar para inicializar a.
Ejemplo
C++
// C++ progmam to initialize a member object without default> // constructor> #include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int>);> };> A::A(>int> arg)> {> >i = arg;> >cout <<>'A's Constructor called: Value of i: '> << i> ><< endl;> }> // Class B contains object of A> class> B {> >A a;> public>:> >B(>int>);> };> B::B(>int> x) : a(x)> {>// Initializer list must be used> >cout <<>'B's Constructor called'>;> }> int> main()> {> >B obj(10);> >return> 0;> }> |
>
>Producción
A's Constructor called: Value of i: 10 B's Constructor called>
Si la clase A tenía constructores predeterminados y parametrizados, entonces la Lista de inicializadores no es imprescindible si queremos inicializar a usando el constructor predeterminado, pero sí es necesario inicializar a usando el constructor parametrizado.
4. Para la inicialización de miembros de la clase base
Al igual que el punto 3, el constructor parametrizado de la clase base solo se puede llamar utilizando la Lista de inicializadores.
Ejemplo
C++
#include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int> );> };> A::A(>int> arg) {> >i = arg;> >cout <<>'A's Constructor called: Value of i: '> << i << endl;> }> // Class B is derived from A> class> B: A {> public>:> >B(>int> );> };> B::B(>int> x):A(x) {>//Initializer list must be used> >cout <<>'B's Constructor called'>;> }> int> main() {> >B obj(10);> >return> 0;> }> |
>
>Producción
A's Constructor called: Value of i: 10 B's Constructor called>
5. Cuando el nombre del parámetro del constructor es el mismo que el del miembro de datos
Si el nombre del parámetro del constructor es el mismo que el nombre del miembro de datos, entonces el miembro de datos debe inicializarse usando este puntero o Lista de inicializadores. En el siguiente ejemplo, tanto el nombre del miembro como el nombre del parámetro para A() es i.
Ejemplo
C++
#include> using> namespace> std;> class> A {> >int> i;> public>:> >A(>int>);> >int> getI()>const> {>return> i; }> };> A::A(>int> i) : i(i)> {> }>// Either Initializer list or this pointer must be used> /* The above constructor can also be written as> A::A(int i) {> >this->yo = yo;> }> */> int> main()> {> >A a(10);> >cout << a.getI();> >return> 0;> }> |
>
>Producción
10>
6. Por motivos de rendimiento
Es mejor inicializar todas las variables de clase en la Lista de inicializadores en lugar de asignar valores dentro del cuerpo. Considere el siguiente ejemplo:
Ejemplo
C++
// 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
1. Primero se llama al constructor del tipo para a.
2. Variable de construcción predeterminada
3. El operador de asignación de Tipo se llama dentro del cuerpo del constructor MyClass() para asignar
variable = a;>
4. Y luego, finalmente, se llama al destructor de Tipo a ya que está fuera de alcance.
Ahora considere el mismo código con el constructor MyClass() con la Lista de inicializadores
C++
// 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:
1. Primero se llama al constructor del tipo para a.
2. Se llama al constructor parametrizado de la clase Tipo para inicializar: variable (a). Los argumentos de la lista de inicializadores se utilizan para copiar la variable de construcción directamente.
3. Se llama al destructor de Tipo a ya que está fuera de alcance.
Como podemos ver en este ejemplo, si usamos la asignación dentro del cuerpo del constructor, hay tres llamadas a funciones: constructor + destructor + una llamada al operador de asignación de suma. Y si usamos la Lista de inicializadores, solo hay dos llamadas a funciones: copiar constructor + llamada destructor. Consulte esta publicación para ver un ejemplo en ejecución sobre este punto.
Esta penalización de asignación será mucho mayor en aplicaciones reales donde habrá muchas variables de este tipo. Gracias a ptr por añadir este punto.
Parámetro vs inicialización uniforme en C++
Es mejor utilizar una lista de inicialización con inicialización uniforme {} en lugar de inicialización de parámetros () para evitar el problema de conversiones limitadas y comportamientos inesperados. Proporciona una verificación de tipos más estricta durante la inicialización y evita posibles conversiones limitadas.
Código que utiliza la inicialización de parámetros ()
C++
#include> class> Base {> >char> x;> public>:> >Base(>char> a)> >: x{ a }> >{> >}> >void> print() { std::cout <<>static_cast><>int>>(X); }> };> int> main()> {> >Base b{ 300 };>// Using uniform initialization with {}> >b.print();> >return> 0;> }> |
>
>Producción
44>
En el código anterior, el valor 300 está fuera del rango válido para char, lo que puede provocar un comportamiento indefinido y resultados potencialmente incorrectos. El compilador puede generar una advertencia o un error para esta situación, según la configuración de compilación.
Código que usa inicialización uniforme {}
Al utilizar la inicialización uniforme con {} e inicializar x con el valor proporcionado a, el compilador realizará una verificación de tipos más estricta y emitirá una advertencia o error durante la compilación, indicando la reducción de la conversión de int a char.
Aquí hay un código con inicialización uniforme {}, lo que genera una advertencia y, por lo tanto, es mejor usarlo
C++
método igual en java
#include> class> Base {> >char> x;> public>:> >Base(>char> a)> >: x{ a }> >{> >}> >void> print() { std::cout <<>static_cast><>int>>(X); }> };> int> main()> {> >Base b{ 300 };>// Using uniform initialization with {}> >b.print();> >return> 0;> }> |
>
>
main.cpp: In function ‘int main()’: main.cpp:17:17: error: narrowing conversion of ‘300’ from ‘int’ to ‘char’ [-Wnarrowing] 17 | Base b{ 300 }; // Using uniform initialization with {} | ^>