En C++ es igual de fácil crear vectores de objetos en la pila o en el montículo, con la certeza de que se producirá la llamada al constructor para cada uno de los objetos del vector. Hay una restricción: debe existir un constructor por defecto, o sea, sin argumentos, que será invocado para cada objeto.
Cuando se crean vectores de objetos dinámicamente, usando new
, hay otras
cosas que hay que tener en cuenta. Como ejemplo de este tipo de vectores véase
MyType* fp = new MyType[100];
Esta sentencia asigna espacio suficiente en el montículo para 100 objetos
MyType
y llama al constructor para cada uno de ellos. Lo que
se ha obtenido es simplemente un MyType*
, exactamente lo mismo que
hubiera obtenido de esta otra forma, que crea un único objeto:
MyType* fp2 = new MyType;
El escritor del programa sabe que fp
es la dirección del primer
elemento de un vector, por lo que tiene sentido seleccionar elementos del mismo
mediante una expresión como fp[3]
, pero ¿qué pasa cuando
destruimos el vector?. Las sentencias
delete fp2; // Correcta delete fp; // Ésta no tendrá el efecto deseado
parecen iguales, y sus efectos serán los mismos. Se llamará al destructor del objeto
MyType
al que apunta el puntero dado y después se liberará el
bloque asignado. Esto es correcto para fp2
, pero no lo es para
fp
, significa que los destructores de los 99 elementos restantes
del vector no se invocarán. Sin embargo, sí se liberará toda la memoria asignada al
vector, ya que fue obtenida como un único gran bloque cuyo tamaño quedó anotado en
alguna parte por las rutinas de asignación.
Esto se soluciona indicando al compilador que el puntero que pasamos es la dirección de inicio de un vector, usando la siguiente sintaxis:
delete [] fp;
Los corchetes indican al compilador la necesidad de generar el código para obtener el número de objetos en el vector, que fue guardado en alguna parte cuando se creó, y llamar al destructor para cada uno de dichos elementos. Esta es una mejora sobre la sintaxis primitiva, que puede verse ocasionalmente en el código de viejos programas:
delete [100] fp;
que forzaba al programador a incluir el número de objetos contenidos en el vector, introduciendo con ello una posible fuente de errores. El esfuerzo adicional que supone para el compilador tener en esto en cuenta es pequeño, y por eso se consideró preferible especificar el número de objetos en un lugar y no en dos.
Como defecto colateral, existe la posibilidad de modificar el puntero
fp
anteriormente definido, para que apunte a cualquier otra
cosa, lo que no es consistente con el hecho de ser la dirección de inicio de un
vector. Tiene más sentido definirlo como una constante, de modo que cualquier
intento de modificación sea señalado como un error. Para conseguir este efecto se
podría probar con:
int const* q = new int[10];
o bien:
const int* q = new int[10];
pero en ambos casos el especificador const
quedaría asociado al
int
, es decir, al valor al que apunta, en lugar de al puntero en
sí. Si se quiere conseguir el efecto deseado, en lugar de las anteriores, se debe
poner:
int* const q = new int[10];
Ahora es posible modificar el valor de los elementos del vector, siendo ilegal
cualquier intento posterior de modificar q
, como
q++
por ejemplo, al igual que ocurre con el identificador de un
vector ordinario.