Un agregado es justo lo que parece: un grupo de cosas agrupadas. Esta definición incluye agregados de tipos mixtos, como estructuras o clases. Un array es un agregado de un único tipo.
Inicializar agregados puede ser tedioso y propenso a errores. La inicialización de agregados en C++ lo hace mucho más seguro. Cuando crea un objeto agregado, todo lo que tiene que hacer es una asignación, y la inicialización la hará el compilador. Esta asignación tiene varias modalidades, dependiendo del tipo de agregado del que se trate, pero en cualquier caso los elementos en la asignación deben estar rodeados de llaves. Para arrays de tipos predefinidos es bastante simple:
int a[5] = { 1, 2, 3, 4, 5};
Si intenta escribir más valores que elementos tiene el array, el compilador dará un mensaje de error. Pero, ¿qué ocurre si escribe menos valores? Por ejemplo:
int b[6] = {0};
Aquí, el compilador usará el primer valor para el primer elemento del array, y después usará ceros para todos los elementos para los que no se tiene un valor. Fíjese en que este comportamiento en la inicialización no ocurre si define un array sin una lista de valores de inicialización. Así que la expresión anterior es una forma resumida de inicializar a cero un array sin usar un bucle for, y sin ninguna posibilidad de un «error por uno» (Dependiendo del compilador, también puede ser más eficiente que un bucle for).
Una segunda forma abreviada para los arrays es el conteo automático, en el cual se permite que el compilador determine el tamaño del array basándose en el número de valores de inicialización:
int c[] = { 1, 2, 3, 4 };
Ahora, si decide añadir otro elemento al array, simplemente debe añadir otro valor. Si puede hacer que su código necesite modificaciones en un único sitio, reducirá la posibilidad de introducir errores durante la modificación. Pero, ¿cómo determinar el tamaño del array? La expresión sizeof c / sizeof *c (el tamaño del array completo dividido entre el tamaño del primer elemento) es un truco que hace que no sea necesario cambiarlo si cambia el tamaño del array [62]:
for(int i = 0; i < sizeof c / sizeof *c; i++) c[i]++;
Dado que las estructuras también son agregados, se pueden inicializar de un modo similar. Como en una estructura al estilo de C todos sus miembros son públicos, se pueden asignar directamente:
struct X { int i; float f; char c; }; X x1 = { 1, 2.2, 'c'};
Si tiene un array de esos objetos, puede inicializarlos usando un conjunto anidado de llaves para cada elemento:
X x2[3] = { {1, 1.1, 'a'}, {2, 2.2, 'b'} };
Aquí, el tercer objeto se inicializa a cero.
Si alguno de los atributos es privado (algo que ocurre típicamente en el caso de clases bien diseñadas en C++), o incluso si todos son públicos pero hay un constructor, las cosas son distintas. En el ejemplo anterior, los valores se han asignado directamente a los elementos del agregado, pero los constructores son una manera de forzar que la inicialización ocurra por medio de una interfaz formal. Aquí, los constructores deben ser invocados para realizar la inicialización. De modo que si tiene una estructura como ésta,
struct Y { float f; int i; Y(int a); };
Debe indicar las llamadas al constructor. La mejor forma de hacerlo es la explícita, como la siguiente:
Y y1[] = { Y(1), Y(2), Y(3) };
Obtendrá tres objetos y tres llamadas al constructor. Siempre que tenga un constructor, ya sea una estructura con todos sus miembros públicos o una clase con atributos privados, toda la inicialización debe ocurrir a través del constructor, incluso si está usando la inicialización de agregados.
Se muestra un segundo ejemplo con un constructor con múltiples argumentos.
//: C06:Multiarg.cpp // Multiple constructor arguments // with aggregate initialization #include <iostream> using namespace std; class Z { int i, j; public: Z(int ii, int jj); void print(); }; Z::Z(int ii, int jj) { i = ii; j = jj; } void Z::print() { cout << "i = " << i << ", j = " << j << endl; } int main() { Z zz[] = { Z(1,2), Z(3,4), Z(5,6), Z(7,8) }; for(int i = 0; i < sizeof zz / sizeof *zz; i++) zz[i].print(); } ///:~
Listado 6.10. C06/Multiarg.cpp
Fíjese en que parece que se invoca explícitamente un constructor para cada objeto del array.
[62] En el segundo volumen de este libro (disponible libremente en www.BruceEckel.com), verá una forma más corta de calcular el tamaño de un array usando plantillas.