La anterior implementación tiene el problema de que no verifica en ningún momento si la memoria se a agotado, el programa no es robusto.Esto es necesario, porque un programa no debe comenzar a funcionar aletoriamente, debe tener un comportamiento predecible ante los casos de excepción.
Par hacer más robusta nuestra implementación del TDA Arreglo vamos a incluir el concepto de excepción. Veamos el código:
arregloExc.h
Autor: L. Alejandro Bernal R.
Fecha: 2000.12.02
Descripción: Implementa el concepto de un arreglo variable con exceptiones.
*/
#ifndef _ARREGLOEXC_H_
#define _ARREGLOEXC_H_
#include <stdlib.h>
#include <stdio.h>
/*
TDA Arreglo
Descripción: Un arreglo es una secuencia de elementos de tamaño variable.
Invariante: Arreglo=<elem[0],...,elem[n-1]> y (Para todo i,0 <= i<n,elem[i] pertenece a TipoB)
*/
template<class TipoB> class Arreglo
{
private:
// Atributos:
TipoB *elem; // Arreglo de elementos.
int num; // Número actual de elementos.
public:
// Excepciones:
class NoHayMemoria { }; // Excepción: No hay memoria.
class FueraRango { }; // Excepción: Fuera de rango.
// Operaciones:
/*
Operación Arreglo
Descripción: Crea un arreglo vacio.
Descripción operacional: Arreglo: -> Arreglo
Precondición: verdadero
Poscondición: Arreglo=<>
*/
Arreglo() { elem = NULL; num= 0; }
/*
Operación Arreglo
Descripción: Crea un arreglo basado en un arreglo fuente.
Descripción operacional: Arreglo: Arreglo -> Arreglo
Precondición: fuente pertenece a Arreglo
Poscondición: Para todo i, 0 <= i < n, elem[i] = fuente.elem[i]
*/
Arreglo(Arreglo<TipoB> &fuente);
/*
Operación: Arreglo
Descripción: Libera el espacio ocupado por el arreglo.
Descripción operacional: Arreglo: Arreglo ->
Precondición:
Poscondición:
*/
Arreglo() { delete elem; }
/*
Operación =
Descripción: Asigna (copia) la información del arreglo fuente.
Descripción operacional: =: Arreglo x Arreglo -> Arreglo
Precondición: fuente pertenece a Arreglo
Poscondición: Para todo i, 0<= i < n, elem[i] =fuente.elem[i]
*/
Arreglo &operator =(Arreglo<TipoB> fuente);
/*
Operación []
Descripción: Retorna una referencia a un elemento del arreglo.
Descripción operacional: []: Arreglo x N -> TipoB
Precondición: i pertenece a N
Poscondición: []=elem[i]
*/
TipoB &operator [](int i);
/*
Operación tam
Descripción: Retorna el tamaño del arreglo.
Descripción operacional: tam: Arreglo -> N
Precondición:
Poscondición: tam=n
*/
int tam() { return num; }
}; // template <class TipoB> class Arreglo
/*
Operación Arreglo
Descripción: Crea un arreglo basado en un arreglo fuente.
Descripción operacional: Arreglo: Arreglo -> Arreglo
Precondición: fuente pertenece a Arreglo
Poscondición: Para todo i, 0 <= i < n, elem[i] = fuente.elem[i]
*/
template<class TipoB>
Arreglo<TipoB>::Arreglo(Arreglo<TipoB> &fuente)
{
num = fuente.num;
elem = new TipoB[num];
// Si mo hay memoria
if(elem == NULL){
throw NoHayMemoria();
}
for(int i = 0; i < num; i++){
elem[i] = fuente.elem[i];
}
}
/*
Operación =
Descripción: Asigna (copia) la información del arreglo fuente.
Descripción operacional: =: Arreglo x Arreglo -> Arreglo
Precondición: fuente pertenece a Arreglo
Poscondición: Para todo i, 0<= i < n, elem[i] =fuente.elem[i]
*/
template<class TipoB>
Arreglo<TipoB> &Arreglo<TipoB>::operator =(Arreglo<TipoB> fuente)
{
delete elem;
num = fuente.num;
elem = new TipoB[num];
// Si no hay memoria
if(elem == NULL){
throw NoHayMemoria();
}
for(int i = 0; i < num; i++){
elem[i] = fuente.elem[i];
}
}
/*
Operación []
Descripción: Retorna una referencia a un elemento del arreglo.
Descripción operacional: []: Arreglo x N -> TipoB
Precondición: i pertenece a N
Poscondición: []=elem[i]
*/
template<class TipoB>
TipoB &Arreglo<TipoB>::operator [](int i)
{
if(i < 0){
throw FueraRango();
}
if(i < num){
return elem[i];
}
// Crear un nuevo arreglo con el espacio suficiente.
TipoB *nuevo = new TipoB[i + 1];
// Si no hay memoria
if(nuevo == NULL){
throw NoHayMemoria();
}
// Pasar los elementos a nuevo arreglo.
for(int j = 0; j < num; j++){
nuevo[j] = elem[j];
}
// Liberar el anterior arreglo de elementos.
delete elem;
elem = nuevo;
num = i + 1;
return elem[i];
} // TipoB &Arreglo::operator [](int i)
#endif
//--- Fin de arregloExc.h
class NoHayMemoria { }; // Excepción: No hay memoria.
class FueraRango { }; // Excepción: Fuera de rango.
Otro cambio está en las funcion constructora:
template<class TipoB>
Arreglo<TipoB>::Arreglo(Arreglo<TipoB> &fuente)
{
num = fuente.num;
elem = new TipoB[num];
// Si no hay memoria
if(elem == NULL){
throw NoHayMemoria();
}
for(int i = 0; i < num; i++){
elem[i] = fuente.elem[i];
}
}
TipoB &Arreglo<TipoB>::operator [](int i)
{
if(i < 0){
throw FueraRango();
}
.
.
.
} // TipoB &Arreglo::operator [](int i)
arreglo_prueba.cpp
Autor: L. Alejandro Bernal R.
Fecha: 200.12.02
Descripción: Prueba del TDA arreglo.
*/
#include "arregloExc.h"
#include <iostream.h>
int main(void)
{
try{
Arreglo<int> arr;
arr[10] = 67;
arr[2] = 3;
arr[20] = 345;
Arreglo<int> arr2 = arr;
Arreglo<int> arr3;
arr3 = arr;
arr[-1] = 345; // Produce la excepción FueraRango.
}
catch(Arreglo<int>::NoHayMemoria){
cerr << "Error: No hay memoria\n";
return 1;
}
catch(Arreglo<int>::FueraRango){
cerr << "Error: Fuera de rango\n";
return 1;
}
return 0;
}
//---- Fin arreglo_prueba.cpp
En el programa anterior la captura de la excepción es simplemente imprimir por el error estándar y salir del programa con un código de error. En programas más profecionales es necesario un manejo de excepciónes más sofisticado.
La sentencia:
Se debe tener manejo de excepciones en las implementaciones de los TDAs para hacer más robustos nuestros programas.