Actualizado el 20 de Mayo del 2018 (Publicado el 18 de Enero del 2017)
1.000 visualizaciones desde el 18 de Enero del 2017
233,6 KB
12 paginas
Creado hace 16a (06/10/2008)
Algoritmos y Estructuras de Datos
2º de Ingeniería Informática, Curso 2008/2009
SEMINARIO DE C++
(orientado a programadores java,
pero no es parte del plan piloto, sólo para alumnos de AED)
Sesión 3
Contenidos:
1. Funciones y clases genéricas
2. Excepciones
3. Asertos
4. El puntero this
5. Redefinición de operadores
Ejemplo
Algoritmos y Estructuras de Datos, 2007/2008
Seminario de C++ – Sesión 3
2/12
1. Funciones y clases genéricas1
• Genericidad (parametrización de tipo): el significado de una función o de un
tipos de datos está definido en base a unos parámetros de tipo que pueden variar.
o Ejemplo: funciones genéricas. En lugar de: OrdenaInt(int array[]),
OrdenaCharP(char *array[]), OrdenaFloat(float array[]), ...,
tenemos Ordena<T>(T array[]).
o Ejemplo: clases genéricas. En lugar de las clases PilaInt, PilaChar,
PilaFloat, ..., tenemos una clase genérica Pila<T>.
• En C++, las funciones y clases genéricas se definen mediante sentencias
template (plantilla). La sintaxis es:
template < parámetros genéricos > función o clase genérica
- parámetros genéricos: Lista con uno o varios tipos genéricos (clases). Por
ejemplo: template <class T>..., template <class A, class B>...
- función o clase genérica: Declaración normal de una función o una clase,
pudiendo usar los tipos de la lista de parámetros genéricos.
• Funciones genéricas ---------------------------------------------------------------------
o Definición.
template < parámetros genéricos >
tipoDevuelto nombreFunción ( parámetros de la función )
{
cuerpo de la función
}
o Utilización. Llamar directamente a la función. El compilador, según los tipos
de los parámetros de entrada, instanciará la versión adecuada.
• Ejemplo. Función genérica ordena(T array[], int tam), que ordena un
array de elementos de cualquier tipo T.
#include <string> // Contiene la clase string, alternativa
// a los (char *) de C
#include <iostream>
using namespace std;
template <class T>
void ordena (T array[], int tam)
{
for (int i= 0; i<tam-1; i++)
for (int j= i+1; j<tam; j++)
if (array[j]<array[i]) {
T tmp= array[i];
array[i]= array[j];
array[j]= tmp;
}
}
1 desde la versión 1.5 (octubre 2004) java incluye clases genéricas; ¿visto en primero?
Algoritmos y Estructuras de Datos, 2007/2008
Seminario de C++ – Sesión 3
3/12
template <class T>
void escribe (T array[], int tam)
{
for (int i= 0; i<tam; i++)
cout << array[i] << (i<tam-1? ", ": "\n");
}
#define numElems(ARRAY) sizeof(ARRAY)/sizeof(*ARRAY)
main ()
{
int a1[]= {65, 23, 12, 87};
ordena (a1, numElems(a1));
escribe(a1, numElems(a1));
string a2[]= {"hola", "esto", "es", "una", "prueba"};
ordena (a2, numElems(a2));
escribe(a2, numElems(a2));
}
• Clases genéricas --------------------------------------------------------------------------
o Definición de la clase.
template < parámetros genéricos >
class nombreClase
{
definición de miembros de la clase
};
o Implementación de los métodos. Para cada método de la clase tenemos:
template < parámetros genéricos >
tipoDevuelto nombreclase<par>::nombreFunción ( parámetros )
{
cuerpo de la función
}
o Utilización. La clase genérica se debe instanciar a un tipo concreto antes de
usarla. Instanciación: nombreclase<parámetros>.
• Ejemplo. Clase genérica pila<T>, de las pilas de cualquier tipo T.
#include <iostream>
#define MAXIMO 100
using namespace std;
template <class T> // Declaración de la clase
class pila
{
private:
int ultimo;
T datos[MAXIMO];
public:
pila () {ultimo= 0;} // Constructor
void push (T v);
void pop ();
T tope();
};
Algoritmos y Estructuras de Datos, 2007/2008
Seminario de C++ – Sesión 3
4/12
// Implementación de los métodos
template <class T>
void pila<T>::push (T v)
{
if (ultimo<MAXIMO)
datos[ultimo++]= v;
}
template <class T>
void pila<T>::pop ()
{
if (ultimo>0)
ultimo--;
}
template <class T>
T pila<T>::tope ()
{
return datos[ultimo-1];
}
// Programa principal
// Ejemplo de uso de la clase genérica
main ()
{
pila<int> p1; // p1 es una pila de enteros
p1.push(4); p1.push(9);
p1.pop();
cout << p1.tope() << '\n';
pila<char *> p2; // p2 es una pila de cadenas
p2.push("Ultimo"); p2.push("en"); p2.push("entrar");
p2.push("primero"); p2.push("en"); p2.push("salir");
for (int i= 0; i<6; i++, p2.pop())
cout << p2.tope() << '\n';
pila<float> *p3; // p3 es una pila de float que
p3= new pila<float>; // se crea dinámicamente
p3->push(1.3); p3->push(2.19); p3->push(3.14);
cout << p3->tope() << '\n';
delete p3;
}
• Probar:
1. Probar pilas de otros tipos: pila<char>, pila<pila<char> >, etc.
2. Implementar una operación igual, que compare dos pilas y devuelva true si
son iguales.
3. Probar la función igual.
• Existe una "pequeña pega" relacionada con las clases genéricas y la compilación
separada: el compilador sólo genera código cuando se instancia la clase genérica.
Problema: si separamos la definición de una clase genérica en fichero de
cabecera (pila.hpp) y de implementación (pila.cpp), la compilación (g++ -c
pila.cpp) no genera código objeto. Si otro módulo usa la case genérica (#include
"pila.hpp"), el compilador dirá que no encuentra la implementación de la clase.
la definición como
implementación de la clase deben ir en el mismo fichero, el hpp.
las clases genéricas,
Solución: para
tanto
la
Algoritmos y Estructuras de Datos, 2007/2008
Seminario de C++ – Sesión 3
2. Excepciones2
5/12
• ¿Qué hacer si se produce un error irreversible dentro de un procedimiento? Por
ejemplo, hacer una división por cero, aplicar tope() sobre una pila vacía,
imposibilidad de reservar más memoria o de leer un fichero, etc.
1. Mostrar un mensaje de error por pantalla, o por la salida de error estándar:
cerr << "Error: la pila está vacía";
2. Devolver al procedimiento que hizo la llamada un valor especial que
signifique "se ha producido un error".
3. Interrumpir la ejecución del programa: exit(1)
4. No hacer nada y continuar con la ejecución del programa como mejor se
pueda.
5. Provocar o lanzar una excepción.
• Significado de las excepciones:
ejecución en ese punto.
para manejar excepciones.
1. El procedimiento que lanza la excepción (lo llamamos A), interrumpe su
2. El procedimiento que ha llamado al A (lo llamamos B), debe tener un código
3. Si el procedimiento B no maneja la excepción, la excepción pasa al que ha
llamado al B, y así sucesivamente hasta que alguien trate la excepción
(propagación de la excepción).
4. Si la excepción se propaga hasta el main() y este no maneja la excepción,
se interrumpe la ejecución del programa y se muestra un mensaje de error.
• Conceptualmente, una excepción es un objeto3.
class ErrorPilaVacia {};
class DivisionPorCero {};
• Provocar una excepción. Sentencia: throw 4 nombreClase();
double divide (int a, int b)
{
if (b==0)
throw DivisionPorCero();
return double(a)/double(b);
}
• Manejar una excepción. Bloque:
try {
sentencias1
}
catch (nombreClase_1) {
sentencias2
}
catch (nombreClase_2) {
sentencias2
}
// no existe la sentencia “finally”
2 muy parecido a java, ¿qué habéis visto?
3 cualquier objeto, no es necesario que sea un objeto de clase heredera de otra que sea una excepción, es
decir, no necesario extends nombre_excepcion.
4 sin añadir new, es decir, no se usa throw new nombreClase();
Algoritmos y Estructuras de Datos, 2007/2008
Seminario de C++ – Sesión 3
6/12
• Funcionamiento del bloque try-catch:
1. Ejecutar las sentencias de sentencias1 normalmente.
2. Si se produce una excepción del tipo nombreClase, ejecutar sentencias2.
Estas sentencias contienen el código de manejo de excepciones.
try {
double d1, d2, d3;
d1= divide(3, 4);
d2= divide(5, 0);
d3= divide(1, 2);
}
catch (DivisionPorCero) {
cerr << "División por cero\n";
};
• Al contrario que en java, no se usa throws nombre_excepcion para indicar
que un método lanza excepciones del tipo nombre_excepcion.
• Manejar excepciones de cualquier tipo5:
try {
sentencias1
}
catch (...) {
sentencias2
}
• Ejemplo.
#include <iostream>
class DivisionPorCero {};
double divide (int a, int b)
{
if (b==0)
throw DivisionPorCero();
return double(a)/double(b);
}
main()
{
try {
cout << divide(3, 4) << '\n';
cout << divide(5, 0) << '\n';
cout << divide(1, 2) << '\n';
}
catch (DivisionPorCero) {
cerr << "División por cero\n";
}
cout << "Acaba\n";
}
• Probar:
1. Ejecutar la división por cero fuera del bloque try ... catch ...
2. Cambiar catch (DivisionPorCero) por catch (...)
3. Ejecutar una sentencia throw dentro del main()
5 en java, se haría catch con la clase exception.
Algoritmos y Estructuras de Datos, 2007/2008
Seminario de C++ – Sesión 3
7/12
3. Asertos
• Asertos: verificar una condición. Si se cumple, seguir normalmente. Si no, se
muestra la línea de código donde falló el programa y se interrumpe la ejecución.
assert ( condición booleana );
• La función assert() está definida en la librería <assert.h
Comentarios de: SEMINARIO DE C++ (orientado a programadores java, pero no es parte del plan piloto, sólo para alumnos de AED) Sesión 3 (0)
No hay comentarios