Anuncios

Hola, a todos sean bienvenidos a mi nuevo post. Hoy seguiremos con el post anterior, en ese post iniciamos la explicacion sobre como trabaja try() y catch(), ahora veremos como obtener datos de un objeto de excepcion, a traves del siguiente ejemplo:

error03.cpp

#include <iostream>
using namespace std;
const int TamanioPredet = 10;

class Arreglo
{
public:
 	Arreglo(int suTamanio = TamanioPredet);
 	Arreglo(const Arreglo & rhs);
 	~Arreglo() { delete [] apTipo; }
 	Arreglo & operator = (const Arreglo &);
 	int & operator[] (int desplazamiento);
 	const int & operator[] (int desplazamiento) const;
 	int ObtenersuTamanio() const { return suTamanio; }
 	friend ostream & operator << (ostream &, const Arreglo &);
 	class xLimite{};
 	class xTamanio
	{
 	public:
 		xTamanio(int tamanio):suTamanio(tamanio){}
 		~xTamanio(){};
 		int ObtenerTamanio() { return suTamanio; }
	private:
 		int suTamanio;
 	};
 	class xMuyGrande : public xTamanio
 	{
 	public:
 		xMuyGrande(int tamanio) : xTamanio(tamanio) {}
 	};
 	class xMuyChico : public xTamanio
 	{
 	public:
 		xMuyChico(int tamanio) : xTamanio(tamanio) {}
 	};
 	class xCero : public xMuyChico
 	{
 	public:
 		xCero(int tamanio): xMuyChico(tamanio) {}
 	};
 	class xNegativo : public xTamanio
 	{
 	public:
 		xNegativo(int tamanio) : xTamanio(tamanio) {}
 	};
private:
 	int *apTipo;
 	int suTamanio;
};

int & Arreglo::operator[](int desplazamiento)
{
 	int tamanio = ObtenersuTamanio();
 	if (desplazamiento >= 0 && desplazamiento < ObtenersuTamanio())
 		return apTipo[ desplazamiento ];
 	throw xLimite();
 	return apTipo[ 0 ];
}

const int & Arreglo::operator[](int desplazamiento) const
{
 	int tamanio = ObtenersuTamanio();
 	if (desplazamiento >= 0 && desplazamiento < ObtenersuTamanio())
 		return apTipo[ desplazamiento ];
 	throw xLimite();
 	return apTipo[ 0 ];
}

Arreglo::Arreglo (int tamanio):
 	suTamanio(tamanio)
 	{
 		if (tamanio == 0) throw xCero(tamanio);
 		if (tamanio < 0) throw xNegativo(tamanio);
 		if (tamanio > 30000) throw xMuyGrande(tamanio);
 		if (tamanio < 10) throw xMuyChico(tamanio);
		apTipo = new int[ tamanio ];
		for(int i = 0; i < tamanio; i++)
 			apTipo[ i ] = 0;
 	}

int main()
{
	try
 	{
 		Arreglo arregloInt(9);
		for(int j = 0; j < 100; j++)
 		{
 			arregloInt[ j ] = j;
 			cout << "arregloInt[ " << j;
 			cout << " ] esta bien..." << endl;
 		}
 	}
 	catch (Arreglo::xLimite)
 	{
 		cout << "No se pudo procesar su entrada.\n";
 	}
 	catch(Arreglo::xMuyGrande laExcepcion)
 	{
 		cout << "Este arreglo es muy grnde...\n";
 		cout << "Se recibieron ";
 		cout << laExcepcion.ObtenerTamanio() << endl;
 	}
 	catch(Arreglo::xCero laExcepcion)
 	{
 		cout << "Pidio un arreglo de cero objetos\n";
 		cout << "Se recibieron ";
 		cout << laExcepcion.ObtenerTamanio() << endl;
 	}
 	catch(Arreglo::xMuyChico laExcepcion)
 	{
 		cout << "Este arreglo es muy chico...\n";
 		cout << "Se recibieron ";
 		cout << laExcepcion.ObtenerTamanio() << endl;
 	}
 	catch(...)
 	{
 		cout << "Algo salio mal!\n";
 	}
	cout << "Listo!\n";
 	return 0;
}
Anuncios

Este caso va a ser muy similar al ultimo ejemplo visto en el post anterior, vamos a tener varias clases relacionadas a la de Arreglo para manipular las excepciones, en el caso anterior solamente hicimos las clases xMuyGrande, xMuyChico y xNegativo herederas de xTamanio, y a xCero heredera de xMuyChico pero no definimos ningun metodo adicional a los predeterminados. En este caso vamos a ver primero a xTamanio:

 	class xTamanio
	{
 	public:
 		xTamanio(int tamanio):suTamanio(tamanio){}
 		~xTamanio(){};
 		int ObtenerTamanio() { return suTamanio; }
	private:
 		int suTamanio;
 	};
Anuncios

Como veran la nueva clase posee un constructor, un destructor, y un metodo para obtener el valor de nuestra variable creada en la zona privada (suTamanio), ahora veamos uno de sus herederos:

 	class xMuyGrande : public xTamanio
 	{
 	public:
 		xMuyGrande(int tamanio) : xTamanio(tamanio) {}
 	};
Anuncios

Como dijimos antes, hicimos un constructor el cual se encargara de asignar un valor a suTamanio cuando lo utilicemos y ahora en las clases herederas vamos a utilizar este constructor para asignarle un valor a suTamanio y luego veremos porque, con xCero ocurre lo mismo, si bien no es heredero de xTamanio al ser heredero de xMuyChico cuando lo utilizamos le enviamos la informacion a xMuyChico y este se encarga de invocar al constructor de xTamanio y agregarle el valor, nuestra siguiente modificacion es la siguiente:

Arreglo::Arreglo (int tamanio):
 	suTamanio(tamanio)
 	{
 		if (tamanio == 0) throw xCero(tamanio);
 		if (tamanio < 0) throw xNegativo(tamanio);
 		if (tamanio > 30000) throw xMuyGrande(tamanio);
 		if (tamanio < 10) throw xMuyChico(tamanio);
		apTipo = new int[ tamanio ];
		for(int i = 0; i < tamanio; i++)
 			apTipo[ i ] = 0;
 	}
Anuncios

En este bloque nosotros definiamos unas condiciones if para evaluar en base al valor de tamanio informado aplicara un metodo de excepcion u otro pero resalte la linea utilizada porque a diferencia de antes le vamos a enviar el valor de la variable tamanio pero luego veremos para que. Despues sigue todo igual pero va a variar algunos bloques catch(), veamos uno de los modificados:

 	catch(Arreglo::xMuyGrande laExcepcion)
 	{
 		cout << "Este arreglo es muy grnde...\n";
 		cout << "Se recibieron ";
 		cout << laExcepcion.ObtenerTamanio() << endl;
 	}
Anuncios

Nuestra primera modificacion es la creacion de un objeto dentro de los parentesis del catch() para luego agregar una nueva linea donde mostraremos el valor almacenado en suTamanio, el cual fue enviado por el throw() y a su vez en los constructores de la clase xTamanio, la salida del programa es la siguiente:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./error03
Este arreglo es muy chico…
Se recibieron 9
Listo!
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Observen como utilizo la clase correcta y a su vez nos devolvio el valor del tamaño del Arreglo, vamos a suponer esta situacion donde variamos el tamaño del Arreglo de nueve a cero, para hacerlo cambien la siguiente linea:

Arreglo arregloInt(9);

por esta otra:

Arreglo arregloInt(0);

Una vez hecho esto, vuelvan a compilarlo para obtener esta salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./error03
Pidio un arreglo de cero objetos
Se recibieron 0
Listo!
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Y esta es la forma de poder enviar informacion a las excepciones por medio de un objeto de excepcion, ahora veremos como pasar valores por referencia y como utilizar funciones virtuales por medio del siguiente ejemplo:

error04.cpp

#include <iostream>

using namespace std;

const int TamanioPredet = 10;

class Arreglo
{
public:
 	Arreglo(int suTamanio = TamanioPredet);
 	Arreglo(const Arreglo & rhs);
 	~Arreglo() { delete [] apTipo; }
 	Arreglo & operator = (const Arreglo &);
 	int & operator[] (int desplazamiento);
 	const int & operator[] (int desplazamiento) const;
 	int ObtenersuTamanio() const { return suTamanio; }
 	friend ostream & operator << (ostream &, const Arreglo &);
 	class xLimite{};
 	
	class xTamanio
 	{
 	public:
 		xTamanio(int tamanio):suTamanio(tamanio){}
 		~xTamanio(){};
 		virtual int ObtenerTamanio() { return suTamanio; }
 		virtual void ImprimirError()
 		{
 			cout << "Error en tamaño. Se recibieron ";
 			cout << suTamanio << endl;
 		}
 	protected:
 		int suTamanio;
 	};
 	
	class xMuyGrande : public xTamanio
 	{
 	public:
 		xMuyGrande(int tamanio) : xTamanio(tamanio) {}
 		virtual void ImprimirError()
 		{
 			cout << "Muy grande! Se recibieron ";
 			cout << xTamanio :: suTamanio << endl;
 		}
 	};
 	
	class xMuyChico : public xTamanio
 	{
 	public:
 		xMuyChico(int tamanio) : xTamanio(tamanio) {}
 		virtual void ImprimirError()
 		{
 			cout << "Muy Chico! Se recibieron ";
 			cout << xTamanio :: suTamanio << endl;
 		}
 	};
 	
	class xCero : public xMuyChico
 	{
 	public:
 		xCero(int tamanio): xMuyChico(tamanio) {}
 		virtual void ImprimirError()
 		{
 			cout << "Cero! Se recibieron ";
 			cout << xTamanio :: suTamanio << endl;
 		}
 	};

 	class xNegativo : public xTamanio
 	{
 	public:
 		xNegativo(int tamanio) : xTamanio(tamanio) {}
 		virtual void ImprimirError()
 		{
 			cout << "Negativo! Se recibieron ";
 			cout << xTamanio :: suTamanio << endl;
 		}
 	};
private:
 	int *apTipo;
 	int suTamanio;
};

Arreglo::Arreglo (int tamanio):
 	suTamanio(tamanio)
 	{
 		if (tamanio == 0) throw xCero(tamanio);
 		if (tamanio < 0) throw xNegativo(tamanio);
 		if (tamanio > 30000) throw xMuyGrande(tamanio);
 		if (tamanio < 10) throw xMuyChico(tamanio);
		apTipo = new int[ tamanio ];
		for(int i = 0; i < tamanio; i++)
 			apTipo[ i ] = 0;
 	}

int & Arreglo::operator[](int desplazamiento)
{
 	int tamanio = ObtenersuTamanio();
 	if (desplazamiento >= 0 && desplazamiento < ObtenersuTamanio())
 		return apTipo[ desplazamiento ];
 	throw xLimite();
 	return apTipo[ 0 ];
}

const int & Arreglo::operator[](int desplazamiento) const
{
 	int tamanio = ObtenersuTamanio();
 	if (desplazamiento >= 0 && desplazamiento < ObtenersuTamanio())
 		return apTipo[ desplazamiento ];
 	throw xLimite();
 	return apTipo[ 0 ];
}

int main()
{
	try
 	{
 		Arreglo arregloInt(9);
		for(int j = 0; j < 100; j++)
 		{
 			arregloInt[ j ] = j;
 			cout << "arregloInt[ " << j;
 			cout << " ] esta bien..." << endl;
 		}
 	}
 	catch (Arreglo::xLimite)
 	{
 		cout << "No se pudo procesar su entrada.\n";
 	}
 	catch(Arreglo::xTamanio & laExcepcion)
 	{
 		laExcepcion.ImprimirError();
 	}
 	catch(...)
 	{
 		cout << "Algo salio mal!\n";
 	}
	cout << "Listo!\n";
 	return 0;
}
Anuncios

Es similar al caso anterior pero con sutiles diferencias, vamos a analizarlas:

	class xTamanio
 	{
 	public:
 		xTamanio(int tamanio):suTamanio(tamanio){}
 		~xTamanio(){};
 		virtual int ObtenerTamanio() { return suTamanio; }
 		virtual void ImprimirError()
 		{
 			cout << "Error en tamaño. Se recibieron ";
 			cout << suTamanio << endl;
 		}
 	protected:
 		int suTamanio;
 	};
Anuncios

Nuestra primer gran diferencia es, transformar al metodo en virtual, luego vamos a crear un metodo virtual el cual se encargara de imprimir un mensaje de error y a este metodo lo llamaremos ImprimirError(), nos mostrara un mensaje y el valor almacenado en suTamanio y por ultimo a suTamanio lo sacamos de private y lo pasamos a protected, esto dara un acceso semipublico es decir acceso publico a los herederos directos e indirectos pero privado para el resto del programa, ahora vamos a analizar uno de las clases herederas:

	class xMuyGrande : public xTamanio
 	{
 	public:
 		xMuyGrande(int tamanio) : xTamanio(tamanio) {}
 		virtual void ImprimirError()
 		{
 			cout << "Muy grande! Se recibieron ";
 			cout << xTamanio :: suTamanio << endl;
 		}
 	};
Anuncios

Pueden observar como de ahora en mas, vamos a redefinir al metodo ImprimirError() para mostrar un mensaje acorde a nuestra excepcion y como suTamanio es de tipo protected podremos acceder a el sin necesidad de ningun metodo solamente poniendo la ruta correcta, xTamanio :: suTamanio, y en todas las clases herederas vamos a hacer lo mismo, inclusive con xCero a pesar de ser heredera de xMuyChico porque al no ser heredera directa va a tener acceso por medio de xMuyChico (esto es lo bueno de protected), despues el programa es exactamente igual hasta llegar a los bloques catch(), vamos a analizarlos:

 	catch (Arreglo::xLimite)
 	{
 		cout << "No se pudo procesar su entrada.\n";
 	}
 	catch(Arreglo::xTamanio & laExcepcion)
 	{
 		laExcepcion.ImprimirError();
 	}
 	catch(...)
 	{
 		cout << "Algo salio mal!\n";
 	}
Anuncios

Observen como se modifico la cantidad de bloques, seguimos teniendo uno para xLimite y otro predeterminado pero ahora desaparecieron el resto pertenecientes a xTamanio. Como pueden ver a la hora de crear el bloque catch() de xTamanio utilizamos una referencia para nuestro objeto, esto nos permitira utilizar la excepcion correcta porque el polimorfismo permite ejecutar la version correcta de ImprimirError(), para mas informacion sobre polimorfismo visiten este post y este otro, aca les muestro la salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./error04
Muy Chico! Se recibieron 9
Listo!
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Si hacemos como en el caso anterior y modificamos el valor del tamaño del Arreglo creado de nueve a cero, lo compilamos nuevamente y obtendremos esta salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./error04
Cero! Se recibieron 0
Listo!
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Ahora pudimos obtener el mismo resultado, dejar mas claro y compacto nuestro codigo fuente y por ende ayudarnos a una mejor depuracion, nuestro siguiente tema sera excepciones y plantillas, para poder trabajar entre ambas se puede utilizar las siguientes dos opciones:

  • Crear una excepcion para cada instancia de la plantilla
  • Utilizar clases de excepciones declaradas fuera de la declaracion de la plantilla

Vamos a ver ambos casos por medio del siguiente ejemplo:

error05.cpp

#include <iostream>

using namespace std;

const int TamanioPredet = 10;

class xLimite{};

template < class T >
class Arreglo
{
public:
 	Arreglo(int suTamanio = TamanioPredet);
 	Arreglo(const Arreglo & rhs);
 	~Arreglo() { delete [] apTipo; }
 	Arreglo & operator = (const Arreglo< T > &);
 	T & operator[] (int desplazamiento);
 	const T & operator[] (int desplazamiento) const;
 	int ObtenersuTamanio() const { return suTamanio; }
 	friend ostream & operator << (ostream & salida, const Arreglo< T >& elArreglo)
 	{
 		for(int i = 0; i < elArreglo.ObtenersuTamanio(); i++)
 			salida << "[ " << i << " ]" << elArreglo[ i ] << endl;
 		return salida;
 	}
 	class xTamanio{};
private:
 	int *apTipo;
 	int suTamanio;
};

template< class T >
Arreglo< T >::Arreglo (int tamanio):
 	suTamanio(tamanio)
 	{
 		if (tamanio < 10 || tamanio > 30000)
 			throw xTamanio();
		apTipo = new int[ tamanio ];
 		for(int i = 0; i < tamanio; i++)
 			apTipo[ i ] = 0;
 	}

template < class T >
Arreglo< T > & Arreglo< T >::operator=(const Arreglo< T > & rhs)
{
 	if (this == &rhs)
 		return *this;
 	delete [] apTipo;
	suTamanio = rhs.ObtenersuTamanio();
 	apTipo = new T[ suTamanio ];
 	for( int i = 0; i < suTamanio; i++)
 		apTipo[ i ] = rhs[ i ];
}

template < class T >
Arreglo< T >::Arreglo(const Arreglo< T > & rhs)
{
 	suTamanio = rhs.ObtenersuTamanio();
 	apTipo = new T[ suTamanio ];
	for(int i = 0; i < suTamanio; i++)
 		apTipo[ i ] = rhs[ i ];
}

template < class T >
T & Arreglo< T >::operator[](int desplazamiento)
{
 	int tamanio = ObtenersuTamanio();
 	if (desplazamiento >= 0 && desplazamiento < ObtenersuTamanio())
 		return apTipo[ desplazamiento ];
 	throw xLimite();
 	return apTipo[ 0 ];
}

template < class T >
const T & Arreglo< T >::operator[](int desplazamiento) const
{
 	int mitamanio = ObtenersuTamanio();
 	if (desplazamiento >= 0 && desplazamiento < ObtenersuTamanio())
 		return apTipo[ desplazamiento ];
 	throw xLimite();
}

int main()
{
 	try
 	{
 		Arreglo< int > arregloInt(9);
		for(int j = 0; j < 100; j++)
 		{
 			arregloInt[ j ] = j;
 			cout << "arregloInt[ " << j;
 			cout << " ] esta bien..." << endl;
 		}
 	}
 	catch (xLimite)
 	{
 		cout << "No se pudo procesar su entrada.\n";
 	}
 	catch(Arreglo< int >::xTamanio)
 	{
 		cout << "Tamaño incorrecto!\n";
 	}
 	cout << "Listo!\n";
 	return 0;
}
Anuncios

En este caso, vemos primero como creamos aun clase llamada xLimite por fuera de la plantilla, luego transformaremos a Arreglo en una plantilla y en ella si vamos a crear una clase llamada xTamanio, despues haremos las definiciones pertintentes en base a la nueva plantilla, observen como antes de cada definicion pertenecente a la plantilla se le pone: template < class T >, y la verdadera diferencia va a estar en los bloques catch() porque ahora tendremos esto:

 	catch (xLimite)
 	{
 		cout << "No se pudo procesar su entrada.\n";
 	}
 	catch(Arreglo< int >::xTamanio)
 	{
 		cout << "Tamaño incorrecto!\n";
 	}
Anuncios

En el primer catch() como esta fuera de la plantilla se utiliza directamente el nombre de la excepcion creada pero en el caso de la excepcion perteneciente a la plantilla debemos informar toda la ruta completa y la instancia debe ser igual a la creada en el main(), por eso es Arreglo< int >, y basicamente asi es como trabajarlo por dentro o fuera de una plantillas, y como digo siempre queda a criterio del programador la forma de aplicarlo en los programas, antes de continuar compilamos y ejecutemos para ver su salida:

tinchicus@dbn001dsk:~/lenguaje/c++$ ./error05
Tamaño incorrecto!
Listo!
tinchicus@dbn001dsk:~/lenguaje/c++$
Anuncios

Las excepciones estan conceptualmente pensadas para ser utilizadas ante errores pero tambien puede ser utilizadas para algunos eventos, como por ejemplo cancelar una carga de usuario pero es raro utilizarlo de esta forma y en general se utilizan para errores.

Anuncios

Hasta aqui vimos como trabajar con excepciones, un poco mas avanzado y como trabajarlo con metodos virtuales y referencias para un mejor acceso de los bloques catch() y por ultimo como usarlo por medio de plantillas. Como vimos entre este post y el anterior, por medio de excepciones podremos mejorar la conducta de nuestros programas a la hora de manipular errores permitiendo una mejor experiencia al usuario, espero les haya sido util, sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00