Un agregado es justo lo que parece: un grupo de cosas agrupados juntos. 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 rodeadas de llaves. Para arrays de tipos básicos 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
).
Un segundo método 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 sítio, 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 [59]:
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 estilo-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 una 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 inicializó 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 un constructor parecido a éste,
struct Y { float f; int i; Y(int a); };
Debe indicar la llamada al constructor. La mejor aproximación es una 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, si es 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 cómo se invoca un constructor explícito para cada objeto de un array.
[59] 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.