13.3. new y delete para vectores

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.

13.3.1. Cómo hacer que un puntero sea más parecido a un vector

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.