Ejercicios - Universidad de Málaga

Anuncio
Programación II
Relación de Ejercicios
UNIVERSIDAD DE MÁLAGA
Dpto. Lenguajes y CC. Computación
E.T.S.I. Telecomunicación
Sonido e Imagen
Contenido
Tema 1: Almacenamiento en Memoria Secundaria. Ficheros
2
Tema 2: Tipos Abstractos de Datos
3
Tema 2.1: Programacion Modular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
Tema 2.2: Tipos Abstractos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
Tema 3: Gestión de Memoria Dinámica
8
Tema 3.1: Listas Enlazadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
Tema 3.2: Abstracción en la Gestión de Memoria Dinámica . . . . . . . . . . . . . . . . .
10
Tema 4: Introducción a la Programación Orientada a Objetos
15
Tema 5: Colecciones
19
Prácticas de Laboratorio
20
Práctica 1: Almacenamiento Persistente de Datos . . . . . . . . . . . . . . . . . . . . . . .
20
Práctica 2: Tipos Abstractos de Datos (I) . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
Práctica 3: Tipos Abstractos de Datos (II) . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
Práctica 4: Tipos Abstractos de Datos (III) . . . . . . . . . . . . . . . . . . . . . . . . . .
26
Práctica 5: Gestión de Memoria Dinámica . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
Práctica 6: Abstracción en la Gestión de Memoria Dinámica (I) . . . . . . . . . . . . . . .
30
Práctica 7: Abstracción en la Gestión de Memoria Dinámica (II) . . . . . . . . . . . . . .
32
Práctica 8: Introducción a la Programación Orientada a Objetos . . . . . . . . . . . . . .
34
Práctica 9: La Clase Vector de la Biblioteca Estándar . . . . . . . . . . . . . . . . . . . .
38
Prácticas de Autoevaluación
40
Gestión de Factoría de Androides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
Gestión de Tareas Periódicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
44
cc Esta obra se encuentra bajo una licencia Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional (CC BY-NC-SA 4.0) de
Creative Commons. Véase http://creativecommons.org/licenses/by-nc-sa/4.0/deed.es_ES
1
Tema 1: Almacenamiento en Memoria Secundaria. Ficheros
1. Escribir un programa que cuente el número de letras minúsculas, de letras mayúsculas y de dígitos de un
fichero.
2. Escribir un programa con la opción de encriptar y de desencriptar un fichero de texto, dependiendo de la
extensión del fichero de entrada. La encriptación (codificación) consiste en que dado un fichero de texto
de entrada (extensión txt) genere otro fichero de salida encriptado (extensión cod). Esta codificación
consiste reemplazar cada letra por la tercera siguiente de forma circular (ej. a→d, b→e, · · ·, w→z, x→a,
y→b, z→c). La opción de desencriptado consiste en leer un fichero codificado (extensión cod) y recuperar
la información original en un fichero de texto (extensión txt).
3. Escribir un programa para procesar información sobre los clientes de una agencia matrimonial. El programa
debe crear un fichero de texto (cuyo nombre se leerá por teclado) en el que se guarde la información de
un número indeterminado de personas. La información que se guardará por cada persona será:
Nombre:
Edad
Género
Arte
Deporte
Libros
Música
Cadena de caracteres.
int.
char (M/F).
char (S/N).
char (S/N).
char (S/N).
char (S/N).
La información correspondiente a cada persona se leerá del teclado. El proceso finalizará cuando se teclee
un campo Nombre que esté vacío.
4. Ampliar el programa que procesa clientes de una agencia matrimonial para que tome los datos de todos los
candidatos a estudiar del fichero del ejercicio anterior, lea el cliente del teclado y finalmente genere como
resultado un fichero (cuyo nombre será leído desde teclado) con toda la información correspondiente a los
candidatos aceptados. Una persona del fichero de entrada se considerará aceptable como candidato si tiene
diferente género y por lo menos tres aficiones comunes con respecto al aspirante introducido por pantalla.
(El programa debe ser capaz de trabajar con cualquier número de personas en el fichero de entrada).
5. Codifique un programa que cree un fichero para contener los datos relativos a los artículos de un almacén.
Para cada artículo habrá de guardar la siguiente información:
Código del artículo
Nombre del artículo
Existencias
Precio
Numérico
Cadena de caracteres
Numérico
Numérico
Se deberá pedir datos de cada artículo por teclado hasta que se teclee 0 (cero) como código de artículo.
6. Escriba un programa que tome como entrada el fichero del ejercicio anterior y una condición sobre los
campos existencias o precio. La condición podrá ser: <campo> [<,<=,>,>=, ==, ! =] <número>
Este programa debe generar como salida un fichero que contenga todos aquellos artículos para los que se
cumple la condición de entrada.
2
Tema 2: Tipos Abstractos de Datos
Tema 2.1: Programacion Modular
Diseñe e implemente los siguientes Módulos, dentro del espacio de nombres umalcc, así como programas de
utilización que permitan comprobar su funcionamiento.
1. Diseñe un módulo que proporcione soporte adecuado a la arímetica con números complejos. El módulo
deberá definir el tipo Complejo, así como los siguientes subprogramas:
void sumar ( Complejo & r , const Complejo & a , const Complejo & b ) ;
// D e v u e l v e un numero c o m p l e j o ( r ) que c o n t i e n e e l r e s u l t a d o de
// sumar l o s numeros c o m p l e j o s ( a ) y ( b ) .
void restar ( Complejo & r , const Complejo & a , const Complejo & b ) ;
// D e v u e l v e un numero c o m p l e j o ( r ) que c o n t i e n e e l r e s u l t a d o de
// r e s t a r l o s numeros c o m p l e j o s ( a ) y ( b ) .
void multiplicar ( Complejo & r , const Complejo & a , const Complejo & b ) ;
// D e v u e l v e un numero c o m p l e j o ( r ) que c o n t i e n e e l r e s u l t a d o de
// m u l t i p l i c a r l o s numeros c o m p l e j o s ( a ) y ( b ) .
void dividir ( Complejo & r , const Complejo & a , const Complejo & b ) ;
// D e v u e l v e un numero c o m p l e j o ( r ) que c o n t i e n e e l r e s u l t a d o de
// d i v i d i r l o s numeros c o m p l e j o s ( a ) y ( b ) .
bool iguales ( const Complejo & a , const Complejo & b ) ;
// D e v u e l v e t r u e s i l o s numeros c o m p l e j o s ( a ) y ( b ) son i g u a l e s .
void escribir ( const Complejo & a ) ;
// Muestra en p a n t a l l a e l numero c o m p l e j o ( a )
void leer ( Complejo & a ) ;
// Lee de t e c l a d o e l v a l o r d e l numero c o m p l e j o ( a ) .
// Lee l a p a r t e r e a l y l a p a r t e i m a g i n a r i a d e l numero
2. Diseñe un módulo que proporcione soporte adecuado a la arímetica con números racionales. El módulo
deberá definir el tipo Racional, así como los subprogramas adecuados teniendo en cuenta las siguientes
consideraciones:
Los números racionales se representan mediante fracciones, donde tanto el numerador como denominador son de tipo int).
Las fracciones se representarán en todo momento normalizadas (simplificadas):
• Es recomendable implementar un subprograma privado de simplificación de la fracción mediante
el cálculo del máximo común divisor (m.c.d.) del numerador y el denominador y su posterior
división por dicho m.c.d.
Para calcular el máximo común divisor de dos numeros enteros positivos n y m, el algoritmo de
Euclides funciona como se indica a continuación:
a) Si n < m, entonces se intercambian los valores de n y m (para asegurarnos que n siempre sea mayor
o igual que m).
b) Si el valor de m es distinto de 0, entonces sea r el resto de la división entera entre n y m; después
modificamos los valores de n y m de la siguiente forma: n toma ahora el valor de m y m toma ahora
el valor de r (el resto de la división entera entre n y m calculado anteriormente), y se repite este
proceso especificado hasta que el valor de m sea igual a 0.
c) Finalmente, cuando el proceso iterativo acaba, el valor de n es el máximo común divisor de ambos
números.
• Para simplificar la determinación del signo de la fracción puede asumirse que el denominador de
la fracción es siempre positivo (por lo que una fracción de signo negativo tendrá un numerador
de signo negativo y un denominador de signo positivo).
El número cero se guardará siempre en su forma canónica 0/1.
Debe evitarse en todo momento la asignación de cero a un denominador.
Debe proporcionar subprogramas para leer, escribir, sumar, restar, multiplicar y dividir fracciones.
Pueden también suministrarse métodos para comparación de números racionales (iguales, menor,
etc).
void sumar ( Racional & r , const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l ( r ) e l r e s u l t a d o de
// sumar l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
void restar ( Racional & r , const Racional & r1 , const Racional & r2 );
3
// Asigna a l numero r a c i o n a l ( r ) e l r e s u l t a d o de
// r e s t a r l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
void multiplicar ( Racional & r , const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l ( r ) e l r e s u l t a d o de
// m u l t i p l i c a r l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
void dividir ( Racional & r , const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l ( r ) e l r e s u l t a d o de
// d i v i d i r l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
void escribir ( const Racional & r );
// Muestra en p a n t a l l a e l v a l o r d e l numero r a c i o n a l ( r )
void leer ( Racional & r );
// Lee de t e c l a d o e l v a l o r d e l numero r a c i o n a l ( r )
bool igual ( const Racional & r1 , const Racional & r2 );
// D e v u e l v e t r u e s i e l numero r a c i o n a l ( r1 ) e s i g u a l a ( r2 )
bool menor ( const Racional & r1 , const Racional & r2 );
// D e v u e l v e t r u e s i e l numero r a c i o n a l ( r1 ) e s menor que ( r2 )
3. Diseñe un módulo que proporcione soporte adecuado a la manipulación de polinomios de grados positivos
menores que 100. El módulo deberá definir el tipo Polinomio, así como las siguientes operaciones:
double evaluar ( const Polinomio & a , double x );
// D e v u e l v e e l r e s u l t a d o de e v a l u a r e l p o l i n o m i o ( a )
// para un v a l o r de ( x ) e s p e c i f i c a d o como parametro
void derivar ( Polinomio & r , const Polinomio & a );
// PRECOND: (&r != &a )
// D e v u e l v e un p o l i n o m i o ( r ) que c o n t i e n e e l r e s u l t a d o de
// c a l c u l a r l a d e r i v a d a d e l p o l i n o m i o ( a )
void sumar ( Polinomio & r , const Polinomio & a , const Polinomio & b );
// PRECOND: ((& r != &a)&&(&r != &b ) )
// D e v u e l v e un p o l i n o m i o ( r ) que c o n t i e n e e l r e s u l t a d o de
// sumar l o s p o l i n o m i o s ( a ) y ( b )
void escribir ( const Polinomio & a ) ;
// Muestra en p a n t a l l a e l c o n t e n i d o d e l p o l i n o m i o ( a )
void leer ( Polinomio & a ) ;
// Lee de t e c l a d o e l v a l o r d e l p o l i n o m i o ( a )
// Lee p a r e s de c o e f i c i e n t e y gr ado h a s t a que e l c o e f i c i e n t e s e a c e r o
Tema 2.2: Tipos Abstractos de Datos
Diseñe e implemente los siguientes TADs, dentro del espacio de nombres umalcc, así como programas de
utilización que permitan comprobar su funcionamiento.
1. TAD número complejo que defina los siguientes métodos públicos:
class Complejo {
public :
Complejo () ;
// C o n s t r u c t o r por D e f e c t o
Complejo ( double parte_real , double parte_imag ) ;
// C o n s t r u c t o r e s p e c i f i c o
double parte_real () const ;
// D e v u e l v e l a p a r t e r e a l d e l numero c o m p l e j o
double parte_imag () const ;
// D e v u e l v e l a p a r t e i m a g i n a r i a d e l numero c o m p l e j o
void sumar ( const Complejo & a , const Complejo & b ) ;
// Asigna a l numero c o m p l e j o ( a c t u a l ) e l r e s u l t a d o de
// sumar l o s numeros c o m p l e j o s ( a ) y ( b ) .
void restar ( const Complejo & a , const Complejo & b ) ;
// Asigna a l numero c o m p l e j o ( a c t u a l ) e l r e s u l t a d o de
// r e s t a r l o s numeros c o m p l e j o s ( a ) y ( b ) .
void multiplicar ( const Complejo & a , const Complejo & b ) ;
// Asigna a l numero c o m p l e j o ( a c t u a l ) e l r e s u l t a d o de
// m u l t i p l i c a r l o s numeros c o m p l e j o s ( a ) y ( b ) .
void dividir ( const Complejo & a , const Complejo & b ) ;
// Asigna a l numero c o m p l e j o ( a c t u a l ) e l r e s u l t a d o de
// d i v i d i r l o s numeros c o m p l e j o s ( a ) y ( b ) .
bool igual ( const Complejo & b ) const ;
// D e v u e l v e t r u e s i e l numero c o m p l e j o ( a c t u a l ) e s
// i g u a l a l numero c o m p l e j o ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l numero c o m p l e j o ( a c t u a l )
4
} ;
void leer () ;
// Lee de t e c l a d o e l v a l o r d e l numero c o m p l e j o ( a c t u a l ) .
// Lee l a p a r t e r e a l y l a p a r t e i m a g i n a r i a d e l numero
2. TAD polinomio de grados positivos menores que 100 que defina los siguientes métodos públicos:
class Polinomio {
public :
˜ Polinomio ();
// D e s t r u c t o r
Polinomio ();
// C o n s t r u c t o r por D e f e c t o
Polinomio ( const Polinomio & p );
// C o n s t r u c t o r de c o p i a
Polinomio & operator = ( const Polinomio & p );
// Operador de A s i g n a c i o n
int max_grado () const ;
// D e v u e l v e e l mayor grado d e l p o l i n o m i o a c t u a l
// cuyo c o e f i c i e n t e e s d i s t i n t o de c e r o
void poner ( int e , double c );
// Asigna e l c o e f i c i e n t e ( c ) a l t e r m i n o de grad o ( e )
// d e l p o l i n o m i o a c t u a l
double obtener ( int e ) const ;
// D e v u e l v e e l c o e f i c i e n t e c o r r e s p o n d i e n t e a l
// t e r m i n o de grado ( e ) d e l p o l i n o m i o a c t u a l
double evaluar ( double x ) const ;
// D e v u e l v e e l r e s u l t a d o de e v a l u a r e l p o l i n o m i o a c t u a l
// para un v a l o r de ( x ) e s p e c i f i c a d o como parametro
void derivar ( const Polinomio & a );
// PRECOND: ( t h i s != &a )
// Asigna a l p o l i n o m i o a c t u a l e l r e s u l t a d o de
// c a l c u l a r l a d e r i v a d a d e l p o l i n o m i o ( a )
void sumar ( const Polinomio & a , const Polinomio & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
// Asigna a l p o l i n o m i o a c t u a l e l r e s u l t a d o de
// sumar l o s p o l i n o m i o s ( a ) y ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o d e l p o l i n o m i o a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r d e l p o l i n o m i o a c t u a l
// Lee p a r e s de c o e f i c i e n t e y gr ado h a s t a que e l c o e f i c i e n t e s e a c e r o
};
3. Un conjunto de números enteros es una colección de elementos homogéneos (números enteros) sin repetición
y ninguna relación de orden entre ellos (no ordenados y sin repetición). Defina un TAD Conjunto de
números enteros que defina los siguientes métodos públicos:
class Conjunto {
public :
˜ Conjunto ();
// D e s t r u c t o r
Conjunto ();
// C o n s t r u c t o r por D e f e c t o : c o n j u n t o v a c i o
Conjunto ( const Conjunto & c );
// C o n s t r u c t o r de c o p i a
Conjunto & operator = ( const Conjunto & c );
// Operador de A s i g n a c i o n
void clear ();
// Elimina t o d o s l o s e l e m e n t o s d e l c o n j u n t o a c t u a l ( queda v a c i o )
bool es_vacio () const ;
// D e v u e l v e t r u e s i e l c o n j u n t o a c t u a l e s t a v a c i o
void incluir ( int e );
// I n c l u y e e l e l e m e n t o ( e ) en e l c o n j u n t o a c t u a l ( s i n r e p e t i c i o n )
void eliminar ( int e );
// Elimina e l e l e m e n t o ( e ) d e l c o n j u n t o a c t u a l
bool pertenece ( int e ) const ;
// D e v u e l v e t r u e s i e l e l e m e n t o ( e ) p e r t e n e c e a l c o n j u n t o a c t u a l
bool e s_subc onjunt o ( const Conjunto & a ) const ;
// D e v u e l v e t r u e s i e l c o n j u n t o a c t u a l e s s u b c o n j u n t o d e l c o n j u n t o ( a )
void union_conj ( const Conjunto & a , const Conjunto & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
5
};
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a union de l o s c o n j u n t o s ( a ) y ( b )
void interseccion ( const Conjunto & a , const Conjunto & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a i n t e r s e c c i o n de l o s c o n j u n t o s ( a ) y ( b )
void diferencia ( const Conjunto & a , const Conjunto & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a d i f e r e n c i a de l o s c o n j u n t o s ( a ) y ( b )
void d i f e r e n c i a _ s i m e t r i c a ( const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a d i f e r e n c i a s i m e t r i c a de l o s c o n j u n t o s ( a ) y ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o d e l c o n j u n t o a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r d e l c o n j u n t o a c t u a l ,
4. TAD matriz matemática de números reales (de dimensiones menores de N × N ) que defina los siguientes
métodos públicos:
class Matriz {
public :
˜ Matriz ();
// D e s t r u c t o r
Matriz ();
// C o n s t r u c t o r por D e f e c t o : m a t r i z v a c i a
Matriz ( int nfils , int ncols );
// C o n s t r u c t o r e s p e c i f i c o : c r e a m a t r i z de ( n f i l s ) x ( n c o l s ) con v a l o r e s 0
Matriz ( const Matriz & m );
// C o n s t r u c t o r de c o p i a
Matriz & operator = ( const Matriz & m );
// Operador de A s i g n a c i o n
void clear ( int nfils , int ncols );
// Elimina t o d o s l o s e l e m e n t o s de l a m a t r i z a c t u a l , y a s i g n a
// a l a m a t r i z a c t u a l una m a t r i z de ( n f i l s ) x ( n c o l s ) con v a l o r e s 0
int nfils () const ;
// D e v u e l v e e l numero de f i l a s de l a m a t r i z a c t u a l
int ncols () const ;
// D e v u e l v e e l numero de columnas de l a m a t r i z a c t u a l
void poner ( int f , int c , double val );
// PRECOND: (0 <= f && f < n f i l s ( ) && 0 <= c && c < n c o l s ( ) )
// Asigna e l v a l o r ( v a l ) a l e l e m e n t o de l a f i l a ( f )
// y columna ( c ) de l a m a t r i z a c t u a l
double obtener ( int f , int c ) const ;
// PRECOND: ( f < n f i l s ( ) && c < n c o l s ( ) )
// D e v u e l v e e l v a l o r d e l e l e m e n t o de l a f i l a ( f )
// y columna ( c ) de l a m a t r i z a c t u a l
void sumar ( const Matriz & a , const Matriz & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
// Asigna a l a m a t r i z a c t u a l e l r e s u l t a d o de
// sumar l a s m a t r i c e s ( a ) y ( b )
void restar ( const Matriz & a , const Matriz & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
// Asigna a l a m a t r i z a c t u a l e l r e s u l t a d o de
// r e s t a r l a s m a t r i c e s ( a ) y ( b )
void multiplicar ( const Matriz & a , const Matriz & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) )
// Asigna a l a m a t r i z a c t u a l e l r e s u l t a d o de
// m u l t i p l i c a r l a s m a t r i c e s ( a ) y ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o de l a m a t r i z a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r de l a m a t r i z a c t u a l ,
// Lee n f i l s , n c o l s y l o s v a l o r e s de l o s e l e m e n t o s
};
5. Un TAD Lista de números enteros es una secuencia de elementos homogéneos (números enteros), donde
cada elemento ocupa una determinada posición dentro de la secuencia, de tal forma que se puede acceder
a cada elemento por la posición donde se encuentra. Así mismo, también es posible insertar y eliminar
6
elementos de la secuencia en la posición indicada, manteniendo el mismo orden posicional de los elementos
en la secuencia. Diseñe e implemente el TAD que proporcione los siguientes métodos públicos:
class ListaInt {
public :
˜ ListaInt () ;
// D e s t r u c t o r
ListaInt () ;
// C o n s t r u c t o r por D e f e c t o
ListaInt ( const ListaInt & o ) ;
// C o n s t r u c t o r de c o p i a
ListaInt & operator = ( const ListaInt & o ) ;
// Operador de A s i g n a c i o n
bool llena () const ;
// D e v u e l v e t r u e s i e l numero de e l e m e n t o s almacenados
// a l c a n z a l a c a p a c i d a d maxima de almacenamiento
int size () const ;
// D e v u e l v e e l numero de e l e m e n t o s almacenados
void clear () ;
// Elimina t o d o s l o s e l e m e n t o s de l a l i s t a a c t u a l ( queda v a c i a )
void insertar ( int pos , int dato ) ;
// PRECOND: ( ! l l e n a ( ) && 0 <= pos && pos <= s i z e ( ) )
// I n s e r t a ( d a t o ) en l a l i s t a a c t u a l en l a p o s i c i o n ( pos )
void eliminar ( int pos ) ;
// PRECOND: (0 <= pos && pos < s i z e ( ) )
// Elimina de l a l i s t a a c t u a l e l e l e m e n t o que ocupa l a p o s i c i o n ( pos )
int acceder ( int pos ) const ;
// PRECOND: (0 <= pos && pos < s i z e ( ) )
// D e v u e l v e e l e l e m e n t o de l a l i s t a a c t u a l que ocupa l a p o s i c i o n ( pos )
void modificar ( int pos , int dato );
// PRECOND: (0 <= pos && pos < s i z e ( ) )
// Asigna ( d a t o ) a l e l e m e n t o de l a l i s t a a c t u a l que ocupa l a p o s i c i o n ( pos )
} ;
7
Tema 3: Gestión de Memoria Dinámica
Tema 3.1: Listas Enlazadas
1. Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de listas
enlazadas en memoria dinámica (véase siguiente figura). El módulo deberá definir el tipo PNodo como un
puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente nodo, así
como un dato de tipo int. Además, el módulo deberá definir los siguientes subprogramas. Diseñe también
un módulo principal que permita comprobar la corrección del módulo lista implementado.
lista:
◦−−→
◦−−−−→
0
◦−−−−→
1
2
void inicializa ( PNodo & lista ) ;
// I n i c i a l i z a ( l i s t a ) a una l i s t a v a c i a
void destruir ( PNodo & lista ) ;
// D e s t r u y e t o d o s l o s e l e m e n t o s de l a l i s t a , l i b e r a n d o
// t o d o s l o s nodos de memoria dinamica . ( l i s t a ) queda v a c i a
void escribir ( PNodo lista ) ;
// Muestra en p a n t a l l a e l c o n t e n i d o de ( l i s t a )
PNodo buscar ( PNodo lista , int dt ) ;
// D e v u e l v e un p u n t e r o a l nodo que c o n t i e n e e l e l e m e n t o
// i g u a l a ( d t ) . S i no s e e n c u e n t r a , e n t o n c e s d e v u e l v e NULL
void i n s e r t a r _ p r i n c i p i o ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o a l p r i n c i p i o de ( l i s t a )
void i nserta r_fina l ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o a l f i n a l de ( l i s t a )
void insertar_ord ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o de forma ordenada en ( l i s t a ) , que d e b e e s t a r ordenada
void e l i m i n a r _ p r i m e r o ( PNodo & lista ) ;
// Elimina e l primer e l e m e n t o de ( l i s t a ) , s i e x i s t e
void e li mi n ar _u l ti mo ( PNodo & lista ) ;
// Elimina e l u l t i m o e l e m e n t o de ( l i s t a ) , s i e x i s t e
void eliminar_elem ( PNodo & lista , int dt ) ;
// Elimina de ( l i s t a ) e l primer e l e m e n t o i g u a l a ( d t ) , s i e x i s t e
PNodo situar ( PNodo lista , int pos ) ;
// D e v u e l v e un p u n t e r o a l nodo que s e e n c u e n t r a en l a p o s i c i o n
// i n d i c a d a por ( pos ) . La p o s i c i o n c e r o ( 0 ) i n d i c a e l primer nodo .
// S i e l nodo no e x i s t e , e n t o n c e s d e v u e l v e NULL
void insertar ( PNodo & lista , int pos , int dt ) ;
// I n s e r t a en ( l i s t a ) un e l e m e n t o en l a p o s i c i o n i n d i c a d a por ( pos )
void eliminar ( PNodo & lista , int pos ) ;
// Elimina de ( l i s t a ) e l e l e m e n t o de l a p o s i c i o n i n d i c a d a por ( pos ) , s i e x i s t e
PNodo duplicar ( PNodo lista ) ;
// D e v u e l v e un p u n t e r o a una nueva l i s t a r e s u l t a d o de d u p l i c a r en
// memoria dinamica l a l i s t a r e c i b i d a como parametro
PNodo leer () ;
// D e v u e l v e una l i s t a con l o s numeros l e i d o s de t e c l a d o ( en e l mismo
// orden que son i n t r o d u c i d o s ) h a s t a que l e a e l numero 0 ( que no e s
// i n t r o d u c i d o )
void e limina r_mayo r ( PNodo & lista ) ;
// Elimina e l mayor e l e m e n t o de ( l i s t a ) , s i e x i s t e
void purgar ( PNodo & lista , int dt ) ;
// Elimina de ( l i s t a ) t o d o s l o s e l e m e n t o s que sean i g u a l e s a ( d t ) , s i e x i s t e n
2. Un conjunto de números enteros es una colección de elementos homogéneos (números enteros) sin repetición
y ninguna relación de orden entre ellos (no ordenados y sin repetición).
Un conjunto de elementos se puede representar mediante una lista enlazada, donde cada nodo contiene
un elemento del conjunto. Por ejemplo el conjunto con los elementos {1, 2, 3} puede ser representado por
la siguiente lista enlazada:
lista:
◦−−→
◦−−−−→
1
8
◦−−−−→
2
3
Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de conjuntos de números enteros implementados mediante listas enlazadas en memoria dinámica (véase figura
anterior). El módulo deberá definir el tipo Conjunto como un registro con un campo de tipo PNodo, que se
define como un tipo puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente nodo, así como un dato de tipo int. Además, el módulo deberá definir los siguientes subprogramas.
Diseñe también un módulo principal que permita comprobar la corrección del módulo implementado.
void inicializar ( Conjunto & c );
// I n i c i a l i z a ( c ) a un c o n j u n t o v a c i o
void destruir ( Conjunto & c );
// D e s t r u y e t o d o s l o s e l e m e n t o s d e l c o n j u n t o , l i b e r a n d o
// t o d o s l o s nodos de memoria dinamica . ( c ) queda v a c i o
bool es_vacio ( const Conjunto & c ) ;
// D e v u e l v e t r u e s i e l c o n j u n t o ( c ) e s t a v a c i o
void incluir ( Conjunto & c , int e );
// I n c l u y e e l e l e m e n t o ( e ) en e l c o n j u n t o ( c ) ( s i n r e p e t i c i o n )
void eliminar ( Conjunto & c , int e );
// Elimina e l e l e m e n t o ( e ) d e l c o n j u n t o ( c )
bool pertenece ( const Conjunto & c , int e ) ;
// D e v u e l v e t r u e s i e l e l e m e n t o ( e ) p e r t e n e c e a l c o n j u n t o ( c )
void escribir ( const Conjunto & c ) ;
// Muestra en p a n t a l l a e l c o n t e n i d o d e l c o n j u n t o ( c )
void leer ( Conjunto & c );
// Lee de t e c l a d o e l v a l o r d e l c o n j u n t o ( c )
bool e s_subc onjunt o ( const Conjunto & a , const Conjunto & b ) ;
// D e v u e l v e t r u e s i e l c o n j u n t o ( a ) e s s u b c o n j u n t o d e l c o n j u n t o ( b )
void union_conj ( Conjunto & c , const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o ( c ) e l r e s u l t a d o de
// l a union de l o s c o n j u n t o s ( a ) y ( b ) ( d u p l i c a l o s nodos )
void interseccion ( Conjunto & c , const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o ( c ) e l r e s u l t a d o de
// l a i n t e r s e c c i o n de l o s c o n j u n t o s ( a ) y ( b ) ( d u p l i c a l o s nodos )
void diferencia ( Conjunto & c , const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o ( c ) e l r e s u l t a d o de
// l a d i f e r e n c i a de l o s c o n j u n t o s ( a ) y ( b ) ( d u p l i c a l o s nodos )
void d i f e r e n c i a _ s i m e t r i c a ( Conjunto & c , const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o ( c ) e l r e s u l t a d o de
// l a d i f e r e n c i a s i m e t r i c a de l o s c o n j u n t o s ( a ) y ( b ) ( d u p l i c a l o s nodos )
void copiar ( Conjunto & d , const Conjunto & o ) ;
// Copia ( d u p l i c a ) e l c o n j u n t o ( o ) en e l c o n j u n t o ( d )
Nota: aunque el orden en el que se encuentran los elementos en el conjunto no es importante, y por lo
tanto, la opción más simple para incluir elementos en él podría ser “insertar siempre los elementos por el
principio”, con objeto de prácticar los conceptos de listas enlazadas, en esta práctica, los elementos del
conjunto serán insertados (sin repetición) ordenados en la lista enlazada donde se encuentran almacenados.
3. Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de listas
doblemente enlazadas en memoria dinámica (véase siguiente figura). El módulo deberá definir el tipo
PNodo como un puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente
nodo, un enlace al nodo anterior, así como un dato de tipo int. Además, el módulo deberá definir los
siguientes subprogramas. Diseñe también un módulo principal que permita comprobar la corrección del
módulo lista implementado.
lista:
◦−−→
◦−−−
−−→
◦−−−
−−→
←−−−−
−◦
←−−−−
−◦
0
1
2
void inicializa ( PNodo & lista ) ;
// I n i c i a l i z a ( l i s t a ) a una l i s t a v a c i a
void destruir ( PNodo & lista ) ;
// D e s t r u y e t o d o s l o s e l e m e n t o s de l a l i s t a , l i b e r a n d o
// t o d o s l o s nodos de memoria dinamica . ( l i s t a ) queda v a c i a
void escribir ( PNodo lista ) ;
// Muestra en p a n t a l l a e l c o n t e n i d o de ( l i s t a )
PNodo buscar ( PNodo lista , int dt ) ;
// D e v u e l v e un p u n t e r o a l nodo que c o n t i e n e e l e l e m e n t o
// i g u a l a ( d t ) . S i no s e e n c u e n t r a , e n t o n c e s d e v u e l v e NULL
9
void i n s e r t a r _ p r i n c i p i o ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o a l p r i n c i p i o de ( l i s t a )
void i nserta r_fina l ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o a l f i n a l de ( l i s t a )
void insertar_ord ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o de forma ordenada en ( l i s t a ) , que d e b e e s t a r ordenada
void e l i m i n a r _ p r i m e r o ( PNodo & lista ) ;
// Elimina e l primer e l e m e n t o de ( l i s t a ) , s i e x i s t e
void e li mi n ar _u l ti mo ( PNodo & lista ) ;
// Elimina e l u l t i m o e l e m e n t o de ( l i s t a ) , s i e x i s t e
void eliminar_elem ( PNodo & lista , int dt ) ;
// Elimina de ( l i s t a ) e l primer e l e m e n t o i g u a l a ( d t ) , s i e x i s t e
PNodo situar ( PNodo lista , int pos ) ;
// D e v u e l v e un p u n t e r o a l nodo que s e e n c u e n t r a en l a p o s i c i o n
// i n d i c a d a por ( pos ) . La p o s i c i o n c e r o ( 0 ) i n d i c a e l primer nodo .
// S i e l nodo no e x i s t e , e n t o n c e s d e v u e l v e NULL
void insertar ( PNodo & lista , int pos , int dt ) ;
// I n s e r t a en ( l i s t a ) un e l e m e n t o en l a p o s i c i o n i n d i c a d a por ( pos )
void eliminar ( PNodo & lista , int pos ) ;
// Elimina de ( l i s t a ) e l e l e m e n t o de l a p o s i c i o n i n d i c a d a por ( pos ) , s i e x i s t e
PNodo duplicar ( PNodo lista ) ;
// D e v u e l v e un p u n t e r o a una nueva l i s t a r e s u l t a d o de d u p l i c a r en
// memoria dinamica l a l i s t a r e c i b i d a como parametro
PNodo leer () ;
// D e v u e l v e una l i s t a con l o s numeros l e i d o s de t e c l a d o ( en e l mismo
// orden que son i n t r o d u c i d o s ) h a s t a que l e a e l numero 0 ( que no e s
// i n t r o d u c i d o )
void e limina r_mayo r ( PNodo & lista ) ;
// Elimina e l mayor e l e m e n t o de ( l i s t a ) , s i e x i s t e
void purgar ( PNodo & lista , int dt ) ;
// Elimina de ( l i s t a ) t o d o s l o s e l e m e n t o s que sean i g u a l e s a ( d t ) , s i e x i s t e n
Tema 3.2: Abstracción en la Gestión de Memoria Dinámica
1. Un polinomio en x de grado arbitrario se puede representar mediante una lista enlazada, donde cada nodo
contiene el coeficiente y el exponente de un término del polinomio, y donde los coeficientes con valor cero
(0) no serán almacenados. Por ejemplo el polinomio 25x − 14x5 puede ser representado por la siguiente
lista enlazada:
lista:
◦−−→
◦−−−−→
25
1
-14
5
Diseñe e implemente el TAD polinomio que defina los siguientes métodos públicos, así como un programa
para comprobar su funcionamiento:
class Polinomio {
public :
˜ Polinomio ();
// D e s t r u c t o r
Polinomio ();
// C o n s t r u c t o r por D e f e c t o
Polinomio ( const Polinomio & p );
// C o n s t r u c t o r de c o p i a
Polinomio & operator = ( const Polinomio & p );
// Operador de A s i g n a c i o n
int max_grado () const ;
// D e v u e l v e e l mayor grado d e l p o l i n o m i o a c t u a l
// cuyo c o e f i c i e n t e e s d i s t i n t o de c e r o
void poner ( int e , double c );
// Asigna e l c o e f i c i e n t e ( c ) a l t e r m i n o de grad o ( e )
// d e l p o l i n o m i o a c t u a l
double obtener ( int e ) const ;
// D e v u e l v e e l c o e f i c i e n t e c o r r e s p o n d i e n t e a l
// t e r m i n o de grado ( e ) d e l p o l i n o m i o a c t u a l
double evaluar ( double x ) const ;
// D e v u e l v e e l r e s u l t a d o de e v a l u a r e l p o l i n o m i o a c t u a l
// para un v a l o r de ( x ) e s p e c i f i c a d o como parametro
void derivar ( const Polinomio & a );
10
};
// Asigna a l p o l i n o m i o a c t u a l e l r e s u l t a d o de
// c a l c u l a r l a d e r i v a d a d e l p o l i n o m i o ( a )
void sumar ( const Polinomio & a , const Polinomio & b );
// Asigna a l p o l i n o m i o a c t u a l e l r e s u l t a d o de
// sumar l o s p o l i n o m i o s ( a ) y ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o d e l p o l i n o m i o a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r d e l p o l i n o m i o a c t u a l
// Lee p a r e s de c o e f i c i e n t e y gr ado h a s t a que e l c o e f i c i e n t e s e a c e r o
2. Un conjunto de números enteros es una colección de elementos homogéneos (números enteros) sin repetición
y ninguna relación de orden entre ellos (no ordenados y sin repetición).
Un conjunto de elementos se puede representar mediante una lista enlazada, donde cada nodo contiene
un elemento del conjunto. Por ejemplo el conjunto con los elementos {1, 2, 3} puede ser representado por
la siguiente lista enlazada:
lista:
◦−−→
◦−−−−→
1
◦−−−−→
2
3
Diseñe e implemente el siguiente TAD conjunto de números enteros que defina los siguientes métodos
públicos, así como un programa para comprobar su funcionamiento:
class Conjunto {
public :
˜ Conjunto ();
// D e s t r u c t o r
Conjunto ();
// C o n s t r u c t o r por D e f e c t o : c o n j u n t o v a c i o
Conjunto ( const Conjunto & c );
// C o n s t r u c t o r de c o p i a
Conjunto & operator = ( const Conjunto & c );
// Operador de A s i g n a c i o n
void clear ();
// Elimina t o d o s l o s e l e m e n t o s d e l c o n j u n t o a c t u a l ( queda v a c i o )
bool es_vacio () const ;
// D e v u e l v e t r u e s i e l c o n j u n t o a c t u a l e s t a v a c i o
void incluir ( int e );
// I n c l u y e e l e l e m e n t o ( e ) en e l c o n j u n t o a c t u a l ( s i n r e p e t i c i o n )
void eliminar ( int e );
// Elimina e l e l e m e n t o ( e ) d e l c o n j u n t o a c t u a l
bool pertenece ( int e ) const ;
// D e v u e l v e t r u e s i e l e l e m e n t o ( e ) p e r t e n e c e a l c o n j u n t o a c t u a l
bool e s_subc onjunt o ( const Conjunto & a ) const ;
// D e v u e l v e t r u e s i e l c o n j u n t o a c t u a l e s s u b c o n j u n t o d e l c o n j u n t o ( a )
void union_conj ( const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a union de l o s c o n j u n t o s ( a ) y ( b )
void interseccion ( const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a i n t e r s e c c i o n de l o s c o n j u n t o s ( a ) y ( b )
void diferencia ( const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a d i f e r e n c i a de l o s c o n j u n t o s ( a ) y ( b )
void d i f e r e n c i a _ s i m e t r i c a ( const Conjunto & a , const Conjunto & b );
// Asigna a l c o n j u n t o a c t u a l e l r e s u l t a d o de
// l a d i f e r e n c i a s i m e t r i c a de l o s c o n j u n t o s ( a ) y ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o d e l c o n j u n t o a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r d e l c o n j u n t o a c t u a l ,
};
3. Se dispone de un procesador con varias etapas, y de unas determinadas tareas que se ejecutarán pasando
sucesivamente por todas las etapas del procesador, desde la primera hasta la última. Como el procesador
dispone de varias etapas, es posible que esté procesando simultáneamente diferentes tareas. Por tanto,
11
una tarea sólo podrá pasar a la siguiente etapa si ésta se encuentra libre. La siguiente figura muestra un
procesador con cuatro etapas y dos tareas, la tarea T1 va ejecutándose por la etapa e2 y la tarea T2 va
ejecutándose por la etapa e4.
t1
procesador
e1
t2
e2
e3
e4
Se debe diseñar la estructura de datos necesaria para representar la información del estado del procesador
en cada momento. Para ello, se sigue el esquema mostrado en la siguiente figura. Como se puede observar
cada etapa se representa con dos elementos, una lista de nodos intermedios con tantos nodos como réplicas
(véase más adelante) haya de la etapa (en el ejemplo sólo se muestra una réplica por cada etapa) y los
correspondientes nodos de etapa. Cada nodo de etapa contendrá su identificador, el número de la tarea
que está ejecutando (un número cero indica que está libre, es decir, no está ejecutando ninguna tarea),
y un puntero al primer nodo de la lista de nodos intermedios de la siguiente etapa. También se puede
observar que la tarea T1 se está ejecutando en la etapa 2 y la tarea T2 se está ejecutando en la etapa 4.
procesador
1
2
0
3
1
4
0
2
Es posible que en un determinado momento interese ampliar la potencia de una determinada etapa. Para
ello, se replicará dicha etapa haciendo tantos duplicados de la misma como se desee. Así mismo, es posible
que interese reducir la potencia de una determinada etapa, para ello se eliminarán réplicas de la misma.
Defina el TAD Procesador que implemente la estructura de datos especificada anteriormente, y que proporcione los siguientes métodos públicos, así mismo, también se deberá implementar un programa principal
que permita su utilización:
a) El Constructor recibe el número de etapas que tiene un determinado procesador, y crea la estructura
base de étapas sin replicación y todas en estado inicial libre, como se muestra en la siguiente figura
para un procesador de 4 etapas:
procesador
1
2
0
0
3
0
4
0
b) El Destructor libera todos los recursos asociados al objeto que está siendo destruido.
c) El método Mostrar muestra en pantalla la estructura interna de un procesador. Por ejemplo, para
la figura anterior:
Etapa: 1
Replica:
Replica:
Etapa: 2
Replica:
Replica:
Etapa: 3
Replica:
Etapa: 4
Replica:
libre
libre
tarea 1
tarea 2
libre
libre
d ) Un método Replicar, que se usará para ampliar la potencia de una determinada etapa del procesador
actual. Recibe como parámetros el identificador de la etapa a replicar y el número de réplicas que
deseamos añadir. Las réplicas añadidas tendrán el estado inicial libre (no tienen asignadas la ejecución
de ninguna tarea).
Las réplicas se añadirán a la estructura añadiendo (en cualquier posición SALVO AL PRINCIPIO)
nodos a la lista de nodos intermedios correspondiente a la etapa y nodos de etapa apuntados desde los
nodos intermedios añadidos. Como se puede observar, todas las réplicas añadidas deben apuntar al
primer nodo intermedio de la etapa siguiente (salvo en la última etapa). La siguiente figura muestra
el resultado de añadir dos réplicas de la etapa 2 a la figura anterior:
12
procesador
1
2
0
3
1
4
0
2
2
0
2
0
La siguiente figura muestra el resultado de añadir una réplica de la etapa 1 a la figura anterior:
procesador
1
2
0
3
1
1
4
0
2
2
0
0
2
0
e) Un método para Compactar que se usará para disminuir el número de réplicas de una etapa del
procesador actual. Para ello, recibe como parámetro el identificador de la etapa a compactar y
elimina todas las réplicas de dicha etapa cuyo estado sea libre (no esté ejecutando ninguna tarea).
Al compactar etapas habrá que tener en cuenta que:
De cada etapa deberá quedar al menos una réplica.
Las réplicas que estén ejecutando alguna tarea no pueden ser eliminadas.
No se podrá liberar la memoria ocupada por el primer nodo de la lista de nodos intermedios de
una etapa (pues sirve de enlace entre una etapa y la siguiente). En caso de que la compactación
requiera eliminar la primera réplica de una etapa (porque esté libre), el puntero al nodo de
etapa del primer nodo de la lista de nodos intermedios pasará a apuntar a un nodo de etapa que
permanezca tras la compactación. Por ejemplo, para la siguiente figura:
procesador
1
2
0
0
1
3
0
4
0
2
0
1
2
2
la siguiente figura es el resultado de compactar la etapa 2 de la figura anterior:
procesador
1
3
0
0
1
4
0
2
0
1
2
2
f ) El método AvanzarEvaluacion intenta avanzar una tarea de una determinada etapa a la siguiente.
Para ello, recibe como parámetro el número de tarea (distinto de cero) que pretende avanzar a la
siguiente etapa.
Si la tarea es nueva, buscará una réplica libre de la primera etapa del procesador. Si no hay
réplicas libres en esta primera etapa, entonces lanzará una excepción.
Si la tarea se está ejecutando en la última etapa del procesador, entonces la tarea habrá terminado
su ejecución, y su número desaparecerá de la estructura.
En otro caso, intenta avanzar la tarea desde la réplica de la etapa en que se encuentra ejecutándose
hasta alguna réplica libre de la siguiente etapa. Si no hay ninguna réplica libre de la siguiente
etapa, entonces lanzará una excepción.
13
g) Así mismo, también deberá ser posible realizar la copia y asignación (duplicación) de la estructura
de datos que representa a un determinado procesador.
4. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.
14
Tema 4: Introducción a la Programación Orientada a Objetos
1. Para calcular los sueldos de cada empleado de una factoría, se diseña la siguiente jerarquía de clases
polimórficas:
Factoria
Empleado
Directivo
AgVentas
AgVentas_A
Directivo_A
AgVentas_B
Directivo_B
La clase polimórfica Empleado representa un empleado general de una factoría, tiene un constructor
que permite crear un objeto con el nombre (string) del empleado recibido como parámetro, y
proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• sueldo(): calcula y devuelve el sueldo del empleado. En caso de no tener información suficiente
para calcular el sueldo, entonces devolverá 0.
• nombre(): devuelve el nombre del objeto empleado actual.
La clase polimórfica AgVentas representa un agente de ventas general, empleado de una factoría,
tiene un constructor que permite crear un objeto con el nombre (string) del empleado recibido
como parámetro, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• borrar_ventas(): reinicia a cero la cantidad total de ventas que acumula este agente de ventas.
• anyadir_ventas(x): permite añadir una cantidad de euros recibida como parámetro a la cantidad total de ventas que acumula este agente de ventas.
• ventas(): devuelve la cantidad total de ventas que acumula este agente de ventas.
La clase polimórfica AgVentas_A representa un agente de ventas de clase A, empleado de una factoría,
tiene un constructor que permite crear un objeto con el nombre (string) del empleado recibido como
parámetro, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• sueldo(): redefine este método para calcular y devolver el sueldo de este tipo de agente de
ventas, según la siguiente ecuación:
sueldo = 2000.0 +
10.0 × total _ventas
100.0
La clase polimórfica AgVentas_B representa un agente de ventas de clase B, empleado de una factoría,
tiene un constructor que permite crear un objeto con el nombre (string) del empleado recibido como
parámetro, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• sueldo(): redefine este método para calcular y devolver el sueldo de este tipo de agente de
ventas, según la siguiente ecuación:
sueldo = 1000.0 +
5.0 × total _ventas
100.0
La clase polimórfica Directivo representa un directivo general, empleado de una factoría, tiene
un constructor que permite crear un objeto con el nombre (string) del empleado recibido como
parámetro, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
La clase polimórfica Directivo_A representa un directivo de clase A, empleado de una factoría, tiene
un constructor que permite crear un objeto con el nombre (string) del empleado recibido como
parámetro, y proporciona los siguientes métodos públicos virtuales:
15
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• sueldo(): redefine este método para calcular y devolver el sueldo de este tipo de directivos,
según la siguiente ecuación:
sueldo = 5000.0
La clase polimórfica Directivo_B representa un directivo de clase B, empleado de una factoría, tiene
un constructor que permite crear un objeto con el nombre (string) del empleado recibido como
parámetro, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• sueldo(): redefine este método para calcular y devolver el sueldo de este tipo de directivos,
según la siguiente ecuación:
sueldo = 3000.0
La clase polimórfica Factoria representa una factoría con un máximo de 100 empleados, su constructor por defecto permite crear un nuevo objeto de clase Factoria sin ningún empleado, y proporciona
los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• anyadir_empleado(e, ok): intenta añadir el empleado que recibe como primer parámetro (de
entrada) a la lista de empleados de la factoría actual. Si la operación es posible o no, entonces
devuelve true o false, respectivamente, en el segundo parámetro (de salida).
• sueldos(): muestra en pantalla tanto el nombre como el sueldo de cada empleado de la factoría.
Finalmente, el programa principal creará un objeto de la clase Factoria, le añadirá empleados
(Directivo_A, Directivo_B, AgVentas_A y AgVentas_B) y ventas acumuladas a los agentes de ventas, y calculará sus sueldos. Así mismo, tambien probará que la clonación de la factoría funciona
adecuadamente.
2. Se desea simular el comportamiento de una serie de vehículos, por lo que se establece la siguiente jerarquía
de clases:
Vehiculo
Automovil
Bicicleta
Todo objeto de la clase base polimórfica Vehiculo debe guardar en su estado la siguiente información
y definir el siguiente constructor:
• ident: identificador del vehículo (cadena de caracteres).
• distancia: la distancia recorrida por el vehículo con respecto al punto de partida (origen) del
mismo (unsigned).
• Vehiculo(i): inicializa el identificador del vehículo con i y su distancia a cero.
Además, el comportamiento de los objetos Vehículo queda definido por los siguientes métodos públicos virtuales:
•
•
•
•
id(): devuelve el indentificador del vehículo
consultar_dist(): devuelve un unsigned con la distancia recorrida por el vehículo
estacionar(): establece la distancia recorrida por el vehículo en 0 (origen)
tiene_energia(): indica si el vehículo tiene energía (función lógica). Debe devolver siempre
true
• mover(): incrementa en 1 el valor de la distancia recorrida por el vehículo.
• clone(): devuelve un puntero a un objeto Vehiculo, copia del vehículo actual
Además, la clase Vehículo proporciona a sus clases derivadas los siguientes métodos protected,
para poder manipular adecuadamente su estado interno:
• mover(x): incrementa en x el valor de la distancia recorrida por el vehículo.
16
La clase derivada polimórfica Automovil añade los siguientes atributos y define el siguiente constructor:
• DPL: constante simbólica static, de tipo unsigned, que indica la distancia que el automóvil
recorre por litro de combustible (= 50).
• deposito: atributo unsigned que guarda el número de litros de combustible del depósito del
automóvil.
• Automovil(i): inicializa el identificador del automóvil a i, su distancia a 0 y su deposito a 0.
Además, el comportamiento de los objetos Automovil queda definido por los siguientes métodos
públicos virtuales:
• tiene_energia(): redefine el método de la clase base, devolviendo true si la cantidad almacenada en el deposito es mayor que 0.
• mover(): redefine el método de la clase base, de tal forma que si el automóvil tiene energía,
entonces se mueve una distancia igual al valor de DPL, es decir, se incrementa la distancia recorrida
actual sumándole a la misma el valor almacenado en la constante miembro DPL.
• repostar(ltr): incrementa la cantidad de combustible almacenada en el deposito del automóvil
en ltr litros.
• clone(): devuelve un puntero a un objeto Automovil, copia del automóvil actual.
La clase derivada polimórfica Bicicleta añade los siguientes atributos y define el siguiente constructor:
• NUM_PLATOS: constante simbólica static, de tipo unsigned, indicando el número de platos que
tiene la bicicleta.
• plato: un unsigned que indica el plato que tiene puesto en este momento la bicicleta (a mayor
tamaño de plato, mayor será su desplazamiento cuando la bicicleta se mueva).
• Bicicleta(i): inicializa el identificador de la bicicleta a i, su distancia a 0 y su plato a 0.
Además, el comportamiento de los objetos Bicicleta queda definido por los siguientes métodos
públicos virtuales:
• mover(): redefine el método de la clase base, de tal forma que la bicicleta se mueve una distancia
igual al valor de plato + 1, es decir, se incrementa la distancia recorrida actual sumándole a la
misma el valor de plato + 1.
• cambiar(): cambia el valor del plato, sumándole 1 y aplicando posteriormente % NUM_PLATOS
(el valor del plato siempre estará entre 0 y NUM_PLATOS - 1).
• clone(): devuelve un puntero a un objeto Bicicleta, copia de la bicicleta actual.
El programa principal debe definir el tipo Vehiculos como un tipo array de NUM_VEHICULOS = 4
punteros a objetos de la clase Vehiculo.
Implementar el procedimiento viajar(v, d, t), que recibe un puntero a un objeto de la clase
Vehiculo, un unsigned con la distancia total a recorrer por el vehículo, y devuelve, a través de un
parámetro de salida el tiempo (“minutos”) que el vehículo ha tardado en llegar a su destino (si ha
tenido energía suficiente para llegar). Este algoritmo debe proceder de la siguiente manera:
a) Estacionar el vehículo (es decir, inicializar la distancia recorrida a 0).
b) Inicializar el tiempo a 0.
c) Mientras que el vehículo tenga energía y su distancia recorrida sea menor que la distancia
solicitada, el vehículo debe moverse (para ello, el procedimiento debe invocar la función miembro
mover() del objeto Vehiculo) y tiempo se incrementa en 1.
Implementar el programa principal (main), con las siguientes características:
• Declara un array mis_vehiculos de tipo Vehiculos.
• Crea un objeto Automovil, con identificador "5555JJJ" y 50 litros de gasolina y pone mis_vehiculos[0]
apuntando a él.
• Crea un objeto Bicicleta, con identificador "6666JJJ" y pone mis_vehiculos[1] apuntando
a él.
• Crea un objeto Bicicleta, con identificador "7777JJJ", cambia su plato y pone mis_vehiculos[2]
apuntando a él.
• Crea un objeto Bicicleta, con identificador "8888JJJ", cambia su plato dos veces y pone
mis_vehiculos[3] apuntando a él.
17
• Para todos los vehiculos que se encuentran en mis_vehiculos, debe calcular el tiempo de duración del viaje para una distancia recorrida de destino 1000 invocando al procedimiento viajar
y, en caso de que el vehículo tuviera energía suficiente, escribe ese tiempo por pantalla (en caso
contrario, se indica con un mensaje por pantalla: El vehículo x no ha podido llegar a su
destino por falta de energía)
• Un ejemplo de ejecución podría ser:
El vehículo 5555JJJ no ha podido llegar a su destino por falta de energía
Tiempo vehículo 6666JJJ: 1000 minutos
Tiempo vehículo 7777JJJ: 500 minutos
Tiempo vehículo 8888JJJ: 334 minutos
3. Dada la jerarquía de clases de Vehiculo del ejercicio anterior, se debe desarrollar la clase no-polimórfica
Parking para gestionar un parking con un máximo de MAX = 100 vehículos (de cualquier tipo) estacionados
en él.
Los objetos de la clase Parking deben almacenar en su estado la siguiente información:
• MAX: constante simbólica static con el máximo (= 100) de vehículos que pueden estacionarse
en el parking.
• parking: un array de MAX punteros a objetos de la clase Vehiculo.
• n_v: número de vehículos estacionados en el parking.
Además, la clase Parking debe suministrar los siguientes métodos, constructores, etc. públicos:
• Parking(): inicializa el número de vehículos estacionados en el parking a 0.
• Parking(otro_parking): constructor de copia de la clase: copia (clona) todos los vehículos
estacionados en otro_parking al parking actual.
• ~Parking(): destructor de la clase: libera todos los vehículos almacenados en el parking (los
destruye).
• operator=(otro_parking): operador de asignación para la clase Parking.
• anyadir(v, ok): recibe como parámetro de entrada v, un puntero a un objeto de la clase
Vehiculo. Si hay espacio en el parking para estacionar un nuevo vehículo, entonces v se añade al
final de la lista de vehículos estacionados en el parking y el número de vehículos estacionados se
incrementa en 1; finalmente, se debe invocar al método de estacionar sobre este nuevo vehículo
estacionado (para que su distancia recorrida se ponga a 0); ok debe ponerse a true en ese caso.
Si no hay espacio para estacionar en el parking, se devuelve false a través de ok.
• mostrar(): muestra por pantalla los identificadores de todos los vehículos estacionados en el
parking.
• extraer(id): busca en la lista de vehículos almacenados en el parking el vehículo con idenfificador id. Si lo encuentra, debe extraer el vehículo del parking y devolver un puntero al objeto
vehículo extraído. Si no lo encuentra, entonces devuelve NULL.
Se aconseja, además, implementar los siguientes métodos privados de la clase Parking:
• buscar(id): devuelve la posición del array parking que contiene un puntero apuntando al
vehículo con identificador id, si éste está estacionado en el parking. Si no, devuelve una posición
fuera de rango (por ejemplo, el n_v).
• copiar(otro_parking): copia todo el estado de otro_parking (esto es, clona todos los vehículos
almacenados en él y copia su n_v) en el objeto actual. Nota: deben clonarse todos los vehículos
del objeto otro_parking para que el parking actual trabaje con copias de los mismos.
• destruir(): destruye todos los vehículos almacenados en el parking.
Debe implementarse un programa principal que declare un objeto de la clase Parking y “pruebe”
toda su funcionalidad: añada vehículos de distinto tipo (vehículos genéricos, automóviles y bicicletas)
al parking, los muestre, los extraiga, haga una copia completa del parking, etc.
18
Tema 5: Colecciones
1. Diseñe un programa que utilice el tipo vector de la biblioteca estándar para almacenar las personas de
una agenda, donde por cada persona se almacena el nombre y el teléfono. La agenda se gestiona mediante
la siguientes operaciones:
Añadir los datos de una persona a la agenda, de tal forma que si la persona ya existe, entonces se
actualiza su número de teléfono, y en otro caso, se añade la nueva persona y su teléfono a la agenda.
Mostrar todos los datos de la agenda.
Mostrar los datos de una determinada persona, a partir de su nombre recibido como parámetro. En
caso de que no exista en la agenda ninguna persona con dicho nombre, entonces se mostrará un
mensaje de error.
Borrar los datos de una determinada persona, a partir de su nombre recibido como parámetro. En
caso de que no exista en la agenda ninguna persona con dicho nombre, entonces se mostrará un
mensaje de error.
2. Realice el ejercicio anterior, utilizando el TAD Persona definido en la práctica 6, e implementando el TAD
Agenda de la misma práctica con vector en vez de con una lista enlazada.
3. Diseñe un programa que lea y almacene en un vector de la biblioteca estándar las ventas realizadas por
unos agentes de ventas. Leerá el nombre y el total de ventas que ha realizado, hasta leer un nombre de
agente vacío. Posteriormente se eliminarán del vector aquellos agentes cuyas ventas sean inferiores a la
media de las ventas realizadas. Finalmente se mostrará el contenido del vector.
4. Diseñe un programa que lea dos matrices de números reales de tamaños arbitrarios (primero leerá las
dimensiones y posteriormente los elementos de la matriz) y muestre el resultado de multiplicar ambas
matrices. Las matrices se almacenarán en vectores de dos dimensiones (utilizando el tipo vector de la
biblioteca estándar).
5. Diseñe un programa que lea una expresión aritmética que contiene llaves {}, paréntesis () y corchetes []
y compruebe si éstos se encuentran correctamente balanceados o no. Por ejemplo la siguiente expresión
3 + 5 ∗ ([4] − 67) + [(7)] se encuentra correctamente balanceada, donde los posibles casos de error son los
siguientes:
Un símbolo de apertura no se corresponde con su correspondiente símbolo de cierre. Por ejemplo:
3 + 5 ∗ ([4] − 67] + [(7)]
Para un determinado símbolo de apertura, no existe el correspondiente símbolo de cierre. Por ejemplo:
(3 + 5 ∗ ([4] − 67 + [(7)])
Para un determinado símbolo de cierre, no existe el correspondiente símbolo de apertura. Por ejemplo:
(3 + 5 ∗ [4] − 67) + [(7)])
19
Prácticas de Laboratorio
Práctica 1: Almacenamiento Persistente de Datos
1. Diseñe un programa que gestione una agenda que almacene información sobre personas (nombre y edad),
permita añadir, modificar y eliminar personas desde el teclado, así como mostrar el contenido de la agenda.
Adicionalmente, el programa permitirá cargar el contenido de la agenda desde un fichero, así como guardar
el contenido de la agenda a un fichero.
Se diseñarán versiones para trabajar con tres formatos de ficheros diferentes (donde los símbolos
representan el espacio en blanco y el fin de línea respectivamente):
Opción (A)
nombre_apellidos
nombre_apellidos
···
edad
edad
←←-
Opción (B)
nombre apellidos ←edad ←nombre apellidos ←edad ←···
y
←-
Opción (C)
nombre apellidos
nombre apellidos
···
edad
edad
←←-
Nótese que en la primera opción (A), el nombre de la persona no contiene espacios en blanco, mientras
que en las siguientes opciones (B y C), si es posible que el nombre de la persona contenga espacios en
blanco.
El programa mostrará un menú iterativo para poder seleccionar las acciones deseadas por el usuario del
programa, tal como:
# include < iostream >
# include < fstream >
# include < string >
# include < array >
# include < cctype >
using namespace std ;
struct Persona {
string nombre ;
unsigned edad ;
};
const unsigned MAX = 100;
typedef array < Persona , MAX > APers ;
struct Agenda {
unsigned nelms ;
APers elm ;
};
// . . . . . . a c o m p l e t a r por e l alumno . . . . . . . . . . . .
char menu ()
{
char op ;
cout < < endl ;
cout < < " C . Cargar Agenda desde Fichero " < < endl ;
cout < < " G . Guardar Agenda en Fichero " < < endl ;
cout < < " M . Mostrar Agenda en Pantalla " < < endl ;
cout < < " A . Anadir Persona a la Agenda " < < endl ;
cout < < " X . Fin " < < endl ;
do {
cout < < endl < < "
Opcion : " ;
cin > > op ;
op = char ( toupper ( op ));
} while ( ! (( op = = 'C ') | | ( op = = 'G ') | | ( op = = 'M ') | | ( op = = 'A ') | | ( op = = 'X ' )));
cout < < endl ;
return op ;
}
int main ()
{
Agenda ag ;
char op ;
inic_agenda ( ag );
do {
op = menu ();
switch ( op ) {
case 'C ':
cargar_agenda ( ag );
20
}
break ;
case 'G ':
guar dar_ag enda ( ag );
break ;
case 'M ':
e sc ri b ir _ ag en d a ( ag );
break ;
case 'A ':
nueva_persona ( ag );
break ;
}
} while ( op ! = 'X ' );
21
Práctica 2: Tipos Abstractos de Datos (I)
1. Diseñe e implemente un TAD para números racionales. Se deberán seguir las siguientes consideraciones:
Los números racionales se representan mediante fracciones, donde tanto el numerador como denominador son de tipo int).
Las fracciones se representarán en todo momento normalizadas (simplificadas):
• Es recomendable implementar un método privado de simplificación de la fracción mediante el
cálculo del máximo común divisor (m.c.d.) del numerador y el denominador y su posterior división
por dicho m.c.d.
Para calcular el máximo común divisor de dos numeros enteros positivos n y m, el algoritmo de
Euclides funciona como se indica a continuación:
a) Si n < m, entonces se intercambian los valores de n y m (para asegurarnos que n siempre sea mayor
o igual que m).
b) Si el valor de m es distinto de 0, entonces sea r el resto de la división entera entre n y m; después
modificamos los valores de n y m de la siguiente forma: n toma ahora el valor de m y m toma ahora
el valor de r (el resto de la división entera entre n y m calculado anteriormente), y se repite este
proceso especificado hasta que el valor de m sea igual a 0.
c) Finalmente, cuando el proceso iterativo acaba, el valor de n es el máximo común divisor de ambos
números.
• Para simplificar la determinación del signo de la fracción puede asumirse que el denominador de
la fracción es siempre positivo (por lo que una fracción de signo negativo tendrá un numerador
de signo negativo y un denominador de signo positivo).
El cero se guardará siempre en su forma canónica 0/1.
Por defecto, un número racional debe asignarse a cero cuando se crea.
Debe evitarse en todo momento la asignación de cero a un denominador.
Un determinado método permitirá comprobar si un determinado número racional se encuentra en
estado de error. Un denominador igual a cero (0) se considera un estado de error.
Debe poder ser posible crear números racionales especificando su numerador y denominador, como
sólamente especificando su numerador (en este último caso, se considerará un denominador igual a
1).
class Racional {
public :
Racional () ;
// Construye e l numero r a c i o n a l 0 /1
Racional ( int n ) ;
// Construye e l numero r a c i o n a l n/1
Racional ( int n , int d ) ;
// Construye e l numero r a c i o n a l n/d
bool fail () const ;
// D e v u e l v e t r u e s i e l numero r a c i o n a l a c t u a l e s t a en e s t a d o e r r o n e o
void multiplicar ( const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l a c t u a l e l r e s u l t a d o de
// m u l t i p l i c a r l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
void escribir () const ;
// Muestra en p a n t a l l a e l v a l o r d e l numero r a c i o n a l a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r d e l numero r a c i o n a l a c t u a l
private :
void normalizar ();
};
2. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.
3. Añada de forma incremental los siguientes métodos públicos al TAD Racional realizado anteriormente:
class Racional {
public :
// . . .
void dividir ( const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l a c t u a l e l r e s u l t a d o de
// d i v i d i r l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
22
};
void sumar ( const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l a c t u a l e l r e s u l t a d o
// sumar l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
void restar ( const Racional & r1 , const Racional & r2 );
// Asigna a l numero r a c i o n a l a c t u a l e l r e s u l t a d o
// r e s t a r l o s numeros r a c i o n a l e s ( r1 ) y ( r2 )
bool igual ( const Racional & r1 ) const ;
// D e v u e l v e t r u e s i e l numero r a c i o n a l a c t u a l e s
bool menor ( const Racional & r1 ) const ;
// D e v u e l v e t r u e s i e l numero r a c i o n a l a c t u a l e s
de
de
i g u a l a ( r1 )
menor que ( r1 )
4. Modifique el programa que utiliza el TAD Racional especificado anteriormente para que utilice los nuevos
métodos añadidos anteriormente.
23
Práctica 3: Tipos Abstractos de Datos (II)
1. Diseñe e implemente un TAD Punto del plano cartesiano, considerando que un Punto describe una determinada posición en el plano cartesiano, especificada por el valor de las componentes X e Y de las
coordenadas cartesianas, y proporciona los siguientes métodos públicos:
class Punto {
public :
˜ Punto ();
// d e s t r u y e e l o b j e t o a c t u a l y s u s r e c u r s o s a s o c i a d o s .
Punto ();
// c r e a un punto en e l o r i g e n de c o o r d e n a d a s .
Punto ( double x , double y );
// c r e a un punto en l a s c o o r d e n a d a s r e c i b i d a s como p a r a m e t r o s .
Punto ( const Punto & o );
// c r e a un punto c o p i a n d o s u s v a l o r e s d e l o b j e t o r e c i b i d o como
// parametro .
Punto & operator = ( const Punto & o );
// a s i g n a a un o b j e t o punto e l v a l o r de o t r o o b j e t o punto
// r e c i b i d o como parametro .
void desplazar ( double dx , double dy );
// D e s p l a z a e l o b j e t o a c t u a l d e s d e l a p o s i c i o n a c t u a l una
// d e t e r m i n a d a d i s t a n c i a e s p e c i f i c a d a como p a r a m e t r o s en ambos e j e s .
double distancia ( const Punto & o ) const ;
// d e v u e l v e l a d i s t a n c i a a b s o l u t a e n t r e e l o b j e t o a c t u a l y e l
// o b j e t o punto r e c i b i d o como parametro .
double coord_x () const ;
// d e v u e l v e e l v a l o r de l a componente X d e l o b j e t o a c t u a l .
double coord_y () const ;
// d e v u e l v e e l v a l o r de l a componente Y d e l o b j e t o a c t u a l .
};
2. Diseñe un programa que permita comprobar el funcionamiento del TAD especificado en el ejercicio anterior.
3. Diseñe e implemente un TAD Segmento del plano cartesiano, considerando que un Segmento es un fragmento de una recta que se encuentra comprendido entre dos Puntos (origen y destino) en el plano cartesiano,
y proporciona los siguientes métodos públicos:
class Segmento {
public :
˜ Segmento ();
// d e s t r u y e e l o b j e t o a c t u a l y s u s r e c u r s o s a s o c i a d o s .
Segmento ();
// c r e a un o b j e t o Segmento nulo , donde ambos p u n t o s que
// l o d e l i m i t a n s e e n c u e n t r a n en e l o r i g e n de c o o r d e n a d a s .
Segmento ( double x1 , double y1 , double x2 , double y2 );
// c r e a un o b j e t o Segmento según l o s v a l o r e s de l a s 4
// c o o r d e n a d a s de dos p u n t o s e s p e c i f i c a d o s como parámetros .
Segmento ( const Punto & a , const Punto & b );
// c r e a un o b j e t o Segmento segun l o s v a l o r e s de dos p u n t o s
// e s p e c i f i c a d o s como p a r a m e t r o s .
Segmento ( const Segmento & o );
// c r e a un Segmento c o p i a n d o s u s v a l o r e s d e l o b j e t o r e c i b i d o
// como parametro .
Segmento & operator = ( const Segmento & o );
// a s i g n a a un o b j e t o Segmento e l v a l o r de o t r o o b j e t o Segmento
// r e c i b i d o como parametro .
void desplazar ( double dx , double dy );
// D e s p l a z a e l o b j e t o a c t u a l d e s d e l a p o s i c i o n a c t u a l una
// d e t e r m i n a d a d i s t a n c i a e s p e c i f i c a d a como p a r a m e t r o s en ambos e j e s .
double longitud () const ;
// d e v u e l v e l a l o n g i t u d d e l o b j e t o Segmento .
Punto origen () const ;
// d e v u e l v e e l v a l o r d e l Punto o r i g e n d e l Segmento a c t u a l .
Punto destino () const ;
// d e v u e l v e e l v a l o r d e l Punto d e s t i n o d e l Segmento a c t u a l .
};
4. Diseñe un programa que permita comprobar el funcionamiento del TAD especificado en el ejercicio anterior.
24
5. Diseñe e implemente un TAD Polígono del plano cartesiano, considerando que un Polígono es una figura
plana compuesta por una secuencia finita de lados consecutivos determinados por vértices (Puntos) en el
plano cartesiano, y proporciona los siguientes métodos públicos:
class Poligono {
public :
˜ Poligono ();
// d e s t r u y e e l o b j e t o a c t u a l y s u s r e c u r s o s a s o c i a d o s .
Poligono ();
// c r e a un o b j e t o P o l i g o n o v a c i o , s i n v e r t i c e s n i l a d o s .
Poligono ( const Poligono & o );
// c r e a un P o l i g o n o c o p i a n d o s u s v a l o r e s d e l o b j e t o r e c i b i d o
// como parametro .
Poligono & operator = ( const Poligono & o );
// a s i g n a a un o b j e t o P o l i g o n o e l v a l o r de o t r o o b j e t o P o l i g o n o
// r e c i b i d o como parametro .
void nuevo_vertice ( const Punto & v );
// a g r e g a un nuevo v e r t i c e a l o b j e t o P o l i g o n o a c t u a l .
void mover_vertice ( int pos , const Punto & v );
// cambia l a p o s i c i o n d e l v e r t i c e e s p e c i f i c a d o en e l primer
// parametro d e l o b j e t o a c t u a l , por e l nuevo v a l o r
// e s p e c i f i c a d o e l e l segundo parametro .
void desplazar ( double dx , double dy );
// d e s p l a z a e l o b j e t o a c t u a l d e s d e l a p o s i c i o n a c t u a l una
// d e t e r m i n a d a d i s t a n c i a e s p e c i f i c a d a como p a r a m e t r o s en ambos
// e j e s .
double perimetro () const ;
// d e v u e l v e e l p e r i m e t r o d e l o b j e t o a c t u a l .
Punto vertice ( int pos ) const ;
// d e v u e l v e e l v a l o r d e l v e r t i c e e s p e c i f i c a d o en e l primer
// parametro .
Segmento lado ( int pos ) const ;
// d e v u e l v e e l v a l o r d e l l a d o e s p e c i f i c a d o en e l primer
// parametro .
int n_vertices () const ;
// d e v u e l v e e l numero de v e r t i c e s d e l p o l i g o n o a c t u a l .
};
6. Diseñe un programa que permita comprobar el funcionamiento del TAD especificado en el ejercicio anterior.
25
Práctica 4: Tipos Abstractos de Datos (III)
1. Diseñe e implemente un TAD (Cuenta) bancaria que represente una cuenta bancaria de un cliente de un
banco. Así, almacena el nombre del cliente (cadena de caracteres), el número de cuenta (número entero)
y el saldo de la cuenta (número real), de tal forma que gestione su manipulación segura, especialmente
considerando que no se puede quedar con saldo negativo.
El TAD Cuenta se definirá en el espacio de nombres umalcc_1, y deberá proporcionar métodos públicos
que soporten adecuadamente las siguientes operaciones. Diseñe también un programa que permita su
utilización.
class Cuenta {
public :
Cuenta ();
// c r e a una c u e n t a con v a l o r e s por d e f e c t o .
Cuenta ( const std :: string & n , int c , double s );
// c r e a una c u e n t a con l o s v a l o r e s r e c i b i d o s como p a r a m e t r o s .
bool error () const ;
// d e v u e l v e f a l s e s i l a c u e n t a e s c o r r e c t a , y t r u e s i l a c u e n t a
// e s e r r o n e a ( nombre v a c i o o numero de c u e n t a n e g a t i v o o s a l d o
// n e g a t i v o ) .
void mostrar () const ;
// muestra en p a n t a l l a t o d a l a i n f o r m a c i o n a s o c i a d a a l a
// c u e n t a b a n c a r i a . En c a s o de c u e n t a erronea , mostrara un
// mensaje adecuado .
std :: string obtene r_nomb re () const ;
// d e v u e l v e e l v a l o r d e l nombre d e l o b j e t o c u e n t a .
int obtene r_cuen ta () const ;
// d e v u e l v e e l v a l o r d e l numero de c u e n t a d e l o b j e t o c u e n t a .
double obtener_saldo () const ;
// d e v u e l v e e l v a l o r d e l s a l d o d e l o b j e t o c u e n t a .
};
2. Diseñe un programa que permita comprobar el funcionamiento del TAD especificado en el ejercicio anterior.
3. Diseñe e implemente un TAD (Banco) que represente las cuentas bancarias de los clientes de un banco,
utilizando para ello el TAD Cuenta bancaria diseñado en el apartado anterior.
Así, el TAD Banco se encargará de la gestión del global de cuentas a nivel del banco y considerando
múltiples clientes, utilizando para cada uno de ellos el TAD Cuenta, el cual se encargará de la manipulación
de la cuenta de un usuario, controlando que operaciones son válidas y cuales no.
Así, para cada cliente, se almacena su nombre (cadena de caracteres), el número de cuenta (número entero)
y el saldo de la cuenta (número real), teniendo en cuenta que nunca podrá existir un saldo negativo. El
tipo abstracto de datos deberá permitir tanto crear nuevas cuentas bancarias como eliminarlas. Así mismo,
deberá permitir mostrar los datos de las cuentas bancarias almacenadas.
El TAD Banco se definirá en el espacio de nombres umalcc_2, y deberá proporcionar métodos públicos
que soporten adecuadamente las siguientes operaciones:
class Banco {
public :
Banco ();
// c r e a un banco v a c i o , s i n c l i e n t e s , i n i c i a l i z a n d o
// adecuadamente e l c o n t a d o r para a s i g n a r numeros de c u e n t a .
void crear_cuenta ( const std :: string & nombre , int & num , bool & ok );
// c r e a una c u e n t a b a n c a r i a para una d e t e r m i n a d a persona ,
// u t i l i z a n d o su nombre e s p e c i f i c a d o como parametro . El numero
// de c u e n t a s e c a l c u l a a p a r t i r de un v a l o r almacenado en l o s
// a t r i b u t o s d e l banco , que l l e v a una c u e n t a i n c r e m e n t a l de l o s
// numeros de c u e n t a s p r e v i a m e n t e a s i g n a d o s . D e v o l v e r a en un
// parametro de s a l i d a e s t e numero de c u e n t a a s i g n a d o por e l
// banco . S i l a o p e r a c i o n s e r e a l i z o c o r r e c t a o i n c o r r e c t a m e n t e ,
// s e r a n o t i f i c a d o mediante un parametro de s a l i d a .
void e li mi n ar _c u en ta ( int num , bool & ok );
// e l i m i n a una c u e n t a b a n c a r i a a p a r t i r de un numero de c u e n t a
// e s p e c i f i c a d o como parametro . S i l a o p e r a c i o n s e r e a l i z o
// c o r r e c t a o i n c o r r e c t a m e n t e , s e r a n o t i f i c a d o mediante un
// parametro de s a l i d a .
void m os tr a r_ cu e nt as () const ;
// muestra t o d a l a i n f o r m a c i o n almacenada en l a e s t r u c t u r a
// de d a t o s .
26
};
4. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.
5. Añada de forma incremental los siguientes métodos públicos al TAD Cuenta realizado anteriormente, de
tal forma que deberá permitir realizar ingresos y retirar efectivo de la cuenta bancaria. Además, también
permitirá aplicar un determinado interés al saldo, así como cobrar unos gastos de comisión. Considerando
que si la cuenta se encuentra en estado erróneo, la operación será errónea y no se realizará ninguna
operación. Además, si la operación se realizó correcta o incorrectamente, será notificado mediante un
parámetro de salida.
class Cuenta {
public :
// . . .
void i ngresa r_sald o ( double cant , bool & ok );
// i n g r e s a r una d e t e r m i n a d a c a n t i d a d de d i n e r o e s p e c i f i c a d a
// como parametro , para sumar a l s a l d o de l a c u e n t a .
void retirar_saldo ( double cant , bool & ok );
// r e t i r a r una d e t e r m i n a d a c a n t i d a d de d i n e r o e s p e c i f i c a d a
// como parametro d e l s a l d o de l a cuenta , c o n s i d e r a n d o que e l
// s a l d o nunca podra s e r n e g a t i v o .
void i n g r e s a r _ i n t e r e s ( double porcentaje , bool & ok );
// i n c r e m e n t a r a e l s a l d o de l a c u e n t a en un d e t e r m i n a d o
// p o r c e n t a j e e s p e c i f i c a d o como parametro .
void r e t i r a r _ c o m i s i o n ( double cant , bool & ok );
// r e t i r a d e l s a l d o una c a n t i d a d e s p e c i f i c a d a como parametro ,
// c o n s i d e r a n d o que s i l a c a n t i d a d a r e t i r a r e s mayor que
// e l s a l d o d i s p o n i b l e , s o l o r e t i r a r a l a c a n t i d a d de s a l d o
// d i s p o n i b l e .
};
6. Añada de forma incremental los siguientes métodos públicos al TAD Banco realizado anteriormente,
considerando que nunca podrá existir un saldo negativo. El tipo abstracto de datos deberá permitir
realizar ingresos y retirar efectivo de una determinada cuenta bancaria, así como realizar transferencias de
efectivo de una cuenta a otra. Además, también permitirá aplicar un determinado interés a los saldos de las
cuentas bancarias existentes, cobrar unos gastos de comisión, y mostrar los datos de las cuentas bancarias
almacenadas (para un determinado nombre de cliente, y para un determinado número de cuenta). Así
mismo, también permitirá guardar y cargar los datos de ficheros.
class Banco {
public :
// . . .
void m ostrar _cuent a ( int num , bool & ok ) const ;
// muestra en p a n t a l l a t o d a l a i n f o r m a c i o n a s o c i a d a a l a
// c u e n t a b a n c a r i a cuyo numero de c u e n t a s e e s p e c i f i c a como
// parametro . S i l a o p e r a c i o n s e r e a l i z o c o r r e c t a o
// i n c o r r e c t a m e n t e , s e r a n o t i f i c a d o mediante un parametro de
// s a l i d a .
void m os tr a r_ cu e nt as ( const std :: string & nombre ) const ;
// muestra en p a n t a l l a t o d a l a i n f o r m a c i o n a s o c i a d a a t o d a s
// l a s c u e n t a s b a n c a r i a s de de una p e r s o n a cuyo nombre s e
// e s p e c i f i c a como parametro .
void ingreso ( int num , double cantidad , bool & ok );
// i n g r e s a una d e t e r m i n a d a c a n t i d a d de d i n e r o e s p e c i f i c a d a
// como parametro a un d e t e r m i n a d o numero de c u e n t a e s p e c i f i c a d o
// como parametro . S i l a o p e r a c i o n s e r e a l i z o c o r r e c t a o
// i n c o r r e c t a m e n t e , s e r a n o t i f i c a d o mediante un parametro de
// s a l i d a .
void retirar ( int num , double cantidad , bool & ok );
// r e t i r a una d e t e r m i n a d a c a n t i d a d de d i n e r o e s p e c i f i c a d a como
// parametro de un d e t e r m i n a d o numero de c u e n t a e s p e c i f i c a d o como
// parametro , c o n s i d e r a n d o que e l s a l d o nunca podra s e r n e g a t i v o .
// S i l a o p e r a c i o n s e r e a l i z o c o r r e c t a o i n c o r r e c t a m e n t e , s e r a
// n o t i f i c a d o mediante un parametro de s a l i d a .
void transferir ( int dest , int origen , double cantidad , bool & ok );
// t r a n s f i e r e una d e t e r m i n a d a c a n t i d a d de d i n e r o e s p e c i f i c a d a
// como parametro d e s d e un numero de c u e n t a o r i g e n a o t r o numero
// de c u e n t a d e s t i n o , ambos e s p e c i f i c a d o s como parametros ,
// c o n s i d e r a n d o que e l s a l d o nunca podra s e r n e g a t i v o . S i l a
// o p e r a c i o n s e r e a l i z o c o r r e c t a o i n c o r r e c t a m e n t e , s e r a
27
};
// n o t i f i c a d o mediante un parametro de s a l i d a .
void i n g r e s a r _ i n t e r e s e s ( double porcentaje );
// i n g r e s a l o s i n t e r e s e s en t o d a s l a s c u e n t a s d e l banco , para
// un d e t e r m i n a d o p o r c e n t a j e e s p e c i f i c a d o como parametro .
void g as to s _c om i si on ( double cantidad );
// c a r g a l o s g a s t o s de c o m i s i o n en t o d a s l a s c u e n t a s d e l
// banco , una c a n t i d a d que s e e s p e c i f i c a como parametro . S i
// l a c a n t i d a d a c a r g a r e x c e d e d e l s a l d o que una d e t e r m i n a d a
// c u e n t a posee , e n t o n c e s s e l e r e t i r a r a t o d a l a c a n t i d a d de
// s a l d o que p o s e a .
void g u a r d a r _ c u e n t a s _ f i c h ( const std :: string & nombre_fich , bool & ok ) const ;
// guarda t o d a l a i n f o r m a c i o n almacenada en l a e s t r u c t u r a de
// d a t o s en e l f i c h e r o cuyo nombre s e e s p e c i f i c a como parametro ,
// en un f o r m a t o adecuado para p o s t e r i o r m e n t e pod er r e c u p e r a r l a
// i n f o r m a c i o n almacenada . S i l a o p e r a c i o n s e r e a l i z o c o r r e c t a o
// i n c o r r e c t a m e n t e , s e r a n o t i f i c a d o mediante un parametro de
// s a l i d a .
void c a r g a r _ c u e n t a s _ f i c h ( const std :: string & nombre_fich , bool & ok ) ;
// c a r g a t o d a l a i n f o r m a c i o n de l a e s t r u c t u r a de d a t o s d e s d e
// e l f i c h e r o cuyo nombre s e e s p e c i f i c a como parametro , s i g u i e n d o
// e l f o r m a t o u t i l i z a d o en l a o p e r a c i o n a n t e r i o r . S i l a
// o p e r a c i o n s e r e a l i z o c o r r e c t a o i n c o r r e c t a m e n t e , s e r a
// n o t i f i c a d o mediante un parametro de s a l i d a .
7. Modifique el programa que utiliza el TAD Banco especificado anteriormente para que utilice los nuevos
métodos añadidos anteriormente.
28
Práctica 5: Gestión de Memoria Dinámica
1. Diseñe un módulo (programación modular) que proporcione soporte adecuado a la manipulación de listas
enlazadas en memoria dinámica (véase siguiente figura). El módulo deberá definir el tipo PNodo como un
puntero a un registro en memoria dinámica de tipo Nodo que contiene un enlace al siguiente nodo, así
como un dato de tipo int. Además, el módulo deberá definir los siguientes subprogramas. Diseñe también
un módulo principal que permita comprobar la corrección del módulo lista implementado.
lista:
◦−−→
◦−−−−→
0
◦−−−−→
1
2
void inicializa ( PNodo & lista ) ;
// I n i c i a l i z a ( l i s t a ) a una l i s t a v a c i a
void destruir ( PNodo & lista ) ;
// D e s t r u y e t o d o s l o s e l e m e n t o s de l a l i s t a , l i b e r a n d o
// t o d o s l o s nodos de memoria dinamica . ( l i s t a ) queda v a c i a
void escribir ( PNodo lista ) ;
// Muestra en p a n t a l l a e l c o n t e n i d o de ( l i s t a )
PNodo buscar ( PNodo lista , int dt ) ;
// D e v u e l v e un p u n t e r o a l nodo que c o n t i e n e e l e l e m e n t o
// i g u a l a ( d t ) . S i no s e e n c u e n t r a , e n t o n c e s d e v u e l v e NULL
void i n s e r t a r _ p r i n c i p i o ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o a l p r i n c i p i o de ( l i s t a )
void i nserta r_fina l ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o a l f i n a l de ( l i s t a )
void insertar_ord ( PNodo & lista , int dt ) ;
// I n s e r t a un e l e m e n t o de forma ordenada en ( l i s t a ) , que d e b e e s t a r ordenada
void e l i m i n a r _ p r i m e r o ( PNodo & lista ) ;
// Elimina e l primer e l e m e n t o de ( l i s t a ) , s i e x i s t e
void e li mi n ar _u l ti mo ( PNodo & lista ) ;
// Elimina e l u l t i m o e l e m e n t o de ( l i s t a ) , s i e x i s t e
void eliminar_elem ( PNodo & lista , int dt ) ;
// Elimina de ( l i s t a ) e l primer e l e m e n t o i g u a l a ( d t ) , s i e x i s t e
PNodo situar ( PNodo lista , int pos ) ;
// D e v u e l v e un p u n t e r o a l nodo que s e e n c u e n t r a en l a p o s i c i o n
// i n d i c a d a por ( pos ) . La p o s i c i o n c e r o ( 0 ) i n d i c a e l primer nodo .
// S i e l nodo no e x i s t e , e n t o n c e s d e v u e l v e NULL
void insertar ( PNodo & lista , int pos , int dt ) ;
// I n s e r t a en ( l i s t a ) un e l e m e n t o en l a p o s i c i o n i n d i c a d a por ( pos )
void eliminar ( PNodo & lista , int pos ) ;
// Elimina de ( l i s t a ) e l e l e m e n t o de l a p o s i c i o n i n d i c a d a por ( pos ) , s i e x i s t e
PNodo duplicar ( PNodo lista ) ;
// D e v u e l v e un p u n t e r o a una nueva l i s t a r e s u l t a d o de d u p l i c a r en
// memoria dinamica l a l i s t a r e c i b i d a como parametro
PNodo leer () ;
// D e v u e l v e una l i s t a con l o s numeros l e i d o s de t e c l a d o ( en e l mismo
// orden que son i n t r o d u c i d o s ) h a s t a que l e a e l numero 0 ( que no e s
// i n t r o d u c i d o )
void e limina r_mayo r ( PNodo & lista ) ;
// Elimina e l mayor e l e m e n t o de ( l i s t a ) , s i e x i s t e
void purgar ( PNodo & lista , int dt ) ;
// Elimina de ( l i s t a ) t o d o s l o s e l e m e n t o s que sean i g u a l e s a ( d t ) , s i e x i s t e n
29
Práctica 6: Abstracción en la Gestión de Memoria Dinámica (I)
1. Diseñe e implemente un TAD Persona, que almacene el nombre y el teléfono de una persona (ambos de
tipo cadena de caracteres), y proporcione los siguientes métodos publicos:
class Persona {
public :
˜ Persona () ;
// D e s t r u y e e l o b j e t o y t o d o s s u s r e c u r s o s a s o c i a d o s .
Persona () ;
// Construye una p e r s o n a con nombre y t e l e f o n o v a c i o .
Persona ( const std :: string & n , const std :: string & t ) ;
// Construye una p e r s o n a con e l nombre y e l t e l e f o n o
// segun l o s p a r a m e t r o s r e c i b i d o s .
bool error () const ;
// D e v u e l v e t r u e s i e l o b j e t o a c t u a l e s t a en e s t a d o erroneo ,
// e s d e c i r , e l nombre o e l t e l e f o n o son v a c i o s .
std :: string obtene r_nomb re () const ;
// D e v u e l v e e l v a l o r d e l nombre d e l o b j e t o a c t u a l .
std :: string o b t e n e r _ t e l e f on o () const ;
// D e v u e l v e e l v a l o r d e l t e l e f o n o d e l o b j e t o a c t u a l .
};
2. Diseñe e implemente un TAD Agenda que, utilizando el TAD Persona definido anteriormente, almacene
el nombre y teléfono de múltiples personas, y permita su acceso de forma adecuada. La agenda se puede
implementar eficientemente utilizando técnicas hash, de la siguiente forma:
Cuando se quiere almacenar una nueva persona, se calcula el número que proporciona la función hash
aplicada al nombre de la persona, y posteriormente se añade la persona a la lista enlazada que se
encuentra en la posición indicada por el número hash calculado (véase ejemplo a continuación).
Cuando se quiere acceder a una determinada persona, se calcula el número que proporciona la función
hash aplicada al nombre de la persona, y se busca la persona en la lista enlazada que se encuentra
en la posición indicada por el número hash calculado (véase ejemplo a continuación).
Cuando se quiere borrar a una determinada persona, se calcula el número que proporciona la función
hash aplicada al nombre de la persona, y se elimina la persona con ese nombre de la lista enlazada que
se encuentra en la posición indicada por el número hash calculado (véase ejemplo a continuación).
Por ejemplo, la función hash para el nombre de la persona puede ser la siguiente, donde el primer parámetro
especifica el nombre de la persona, y el segundo parámetro especifica el tamaño del array de listas enlazadas:
unsigned hash ( const std :: string & nombre , unsigned tamanyo )
{
unsigned valor = 0;
for ( unsigned i = 0; i < nombre . size (); + + i ) {
valor = valor * 7 + unsigned ( nombre [ i ] );
}
return valor % tamanyo ;
}
Por ejemplo, dado un tamaño del array de 5, si la función hash para los nombres pepe, maria y ana
proporciona el valor 0, para el nombre juan proporciona el valor 2 y para los nombres lola y jaime
proporciona el valor 4, tendríamos la estructura que se muestra a continuación:
nelms
6
elms
0
pepe
maria
ana
1
30
22
35
lola
jaime
23
37
2
juan
3
27
4
El TAD Agenda debe proporcionar los siguientes métodos públicos:
30
class Agenda {
public :
˜ Agenda () ;
// D e s t r u y e e l o b j e t o y t o d o s s u s r e c u r s o s a s o c i a d o s .
Agenda () ;
// Construye una agenda v a c i a .
void anyadir ( const Persona & p );
// S i l a p e r s o n a e s p e c i f i c a d a como parametro no e s t a en modo
// erroneo , s i l a p e r s o n a ya s e e n c o n t r a b a en l a agenda ,
// e n t o n c e s l o s nuevos v a l o r e s de l a p e r s o n a r ee mp la za n a l o s
// a n t e r i o r e s , en o t r o c a s o anyade l a p e r s o n a e s p e c i f i c a d a a
// l a agenda a c t u a l .
Persona buscar ( const std :: string & nombre ) const ;
// Busca en l a agenda l a p e r s o n a cuyo nombre s e e s p e c i f i c a
// como parametro , y s i l a e n c u e n t r a , e n t o n c e s d e v u e l v e un o b j e t o
// Persona con l o s v a l o r e s de l a p e r s o n a almacenados . S i no
// l a e n c u e n t r a , e n t o n c e s d e v u e l v e una p e r s o n a en e s t a d o de
// e r r o r .
void eliminar ( const std :: string & nombre , bool & ok );
// Elimina de l a agenda a c t u a l l a p e r s o n a cuyo nombre s e
// e s p e c i f i c a como parametro y ok tomara e l v a l o r t r u e . En c a s o
// de no e x i s t i r , ok tomara e l v a l o r f a l s e .
void mostrar () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o c o m p l e t o de l a agenda .
};
3. Diseñe un programa que permita utilizar el TAD especificado en el ejercicio anterior.
31
Práctica 7: Abstracción en la Gestión de Memoria Dinámica (II)
1. Hay muchas aplicaciones en las que se deben almacenar en la memoria matrices de grandes dimensiones. Si
la mayoría de los elementos de la matriz son ceros, ésta, en lugar de almacenarse en un array bidimensional,
se puede representar más eficientemente utilizando listas enlazadas donde los elementos nulos y filas nulas
no serán almacenadas.
Así, defina la clase MatrizDispersa en el espacio de nombres umalcc_aux que represente dicha abstracción
y proporcione las siguientes operaciones:
class Ma trizDi spersa {
public :
˜ Matriz Disper sa ();
// D e s t r u c t o r
Matr izDisp ersa ();
// C o n s t r u c t o r por D e f e c t o : m a t r i z v a c i a
Matr izDisp ersa ( int nfils , int ncols );
// C o n s t r u c t o r e s p e c i f i c o : c r e a m a t r i z de ( n f i l s ) x ( n c o l s ) con v a l o r e s 0
Matr izDisp ersa ( const Ma trizDi spersa & m );
// C o n s t r u c t o r de c o p i a
Matr izDisp ersa & operator = ( const Mat rizDis persa & m );
// Operador de A s i g n a c i o n
void clear ( int nfils , int ncols );
// Elimina t o d o s l o s e l e m e n t o s de l a m a t r i z a c t u a l , y a s i g n a
// a l a m a t r i z a c t u a l una m a t r i z de ( n f i l s ) x ( n c o l s ) con v a l o r e s 0
int nfils () const ;
// D e v u e l v e e l numero de f i l a s de l a m a t r i z a c t u a l
int ncols () const ;
// D e v u e l v e e l numero de columnas de l a m a t r i z a c t u a l
void poner ( int f , int c , double val );
// PRECOND: (0 <= f && f < n f i l s ( ) && 0 <= c && c < n c o l s ( ) )
// Asigna e l v a l o r ( v a l ) a l e l e m e n t o de l a f i l a ( f )
// y columna ( c ) de l a m a t r i z a c t u a l
double obtener ( int f , int c ) const ;
// PRECOND: ( f < n f i l s ( ) && c < n c o l s ( ) )
// D e v u e l v e e l v a l o r d e l e l e m e n t o de l a f i l a ( f )
// y columna ( c ) de l a m a t r i z a c t u a l
};
Utilice la siguiente estructura para implementar la matriz dispersa, donde hay una lista enlazada ordenada
(ascendentemente por número de fila) para acceder por filas, y a partir de un determinado nodo fila, se
puede acceder a la lista enlazada ordenada (ascendentemente por número de columna) donde se encuentran
los elementos pertenecientes a dicha fila y a la columna especificada en el nodo cuyo valor es distinto de
cero. Por ejemplo, la siguiente figura representa la siguiente matriz:
nfil
4
ncol
4
filas
0
0 40
1 60
3 90

40 60 0
 0 0 0

 50 0 75
0 67 83
2
0 50
2 75

90
0 

0 
0
3
1 67
2 83
con las siguientes especificaciones adicionales:
El método obtener permite conocer el valor de un determinado elemento de la matriz especificado
por su fila y columna correspondiente. En caso de que dicho elemento no esté almacenado, entonces
su valor es cero.
El método poner permite asignar un determinado valor a un determinado elemento de la matriz
especificado por su fila y columna correspondiente.
32
Solo se almacenarán los elementos de valor distinto de cero, considerando que si un elemento no
está almacenado, es que su valor es cero. Si una fila no tiene elementos distintos de cero, el nodo
correspondiente a dicha fila no será almacenado.
Si el valor a almacenar es distinto de cero, si el elemento ya existe, se reemplazará su valor, en otro
caso se añadirá a la estructura.
Si el valor a almacenar es cero, habrá que eliminar dicho elemento de la estructura si es que existe.
Si como consecuencia de ello, la fila queda sin elementos, habrá que eliminar el nodo correspondiente
de dicha lista.
2. Se debe diseñar e implementar el tipo abstracto de datos Matriz de números reales en el espacio de nombre
umalcc, junto con las operaciones necesarias para su gestión y tratamiento. Utilice para su implementación
la clase MatrizDispersa realizada en el ejercicio anterior.
class Matriz {
public :
˜ Matriz ();
// D e s t r u c t o r
Matriz ();
// C o n s t r u c t o r por D e f e c t o : m a t r i z v a c i a
Matriz ( int nfils , int ncols );
// C o n s t r u c t o r e s p e c i f i c o : c r e a m a t r i z de ( n f i l s ) x ( n c o l s ) con v a l o r e s 0
Matriz ( const Matriz & m );
// C o n s t r u c t o r de c o p i a
Matriz & operator = ( const Matriz & m );
// Operador de A s i g n a c i o n
void clear ( int nfils , int ncols );
// Elimina t o d o s l o s e l e m e n t o s de l a m a t r i z a c t u a l , y a s i g n a
// a l a m a t r i z a c t u a l una m a t r i z de ( n f i l s ) x ( n c o l s ) con v a l o r e s 0
int nfils () const ;
// D e v u e l v e e l numero de f i l a s de l a m a t r i z a c t u a l
int ncols () const ;
// D e v u e l v e e l numero de columnas de l a m a t r i z a c t u a l
void poner ( int f , int c , double val );
// PRECOND: (0 <= f && f < n f i l s ( ) && 0 <= c && c < n c o l s ( ) )
// Asigna e l v a l o r ( v a l ) a l e l e m e n t o de l a f i l a ( f )
// y columna ( c ) de l a m a t r i z a c t u a l
double obtener ( int f , int c ) const ;
// PRECOND: ( f < n f i l s ( ) && c < n c o l s ( ) )
// D e v u e l v e e l v a l o r d e l e l e m e n t o de l a f i l a ( f )
// y columna ( c ) de l a m a t r i z a c t u a l
void sumar ( const Matriz & a , const Matriz & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) ) ;
// Asigna a l a m a t r i z a c t u a l e l r e s u l t a d o de
// sumar l a s m a t r i c e s ( a ) y ( b )
void restar ( const Matriz & a , const Matriz & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) ) ;
// Asigna a l a m a t r i z a c t u a l e l r e s u l t a d o de
// r e s t a r l a s m a t r i c e s ( a ) y ( b )
void multiplicar ( const Matriz & a , const Matriz & b );
// PRECOND: ( ( t h i s != &a)&&( t h i s != &b ) ) ;
// Asigna a l a m a t r i z a c t u a l e l r e s u l t a d o de
// m u l t i p l i c a r l a s m a t r i c e s ( a ) y ( b )
void escribir () const ;
// Muestra en p a n t a l l a e l c o n t e n i d o de l a m a t r i z a c t u a l
void leer ();
// Lee de t e c l a d o e l v a l o r de l a m a t r i z a c t u a l ,
// Lee n f i l s , n c o l s y l o s v a l o r e s de l o s e l e m e n t o s
};
3. Implemente un programa principal que permita comprobar el funcionamiento del tipo abstracto de datos
Matriz definido previamente.
33
Práctica 8: Introducción a la Programación Orientada a Objetos
1. Se desea implementar una jerarquía de elementos gráficos como la de la siguiente figura:
ElementoGrafico
Punto
Linea
LineaVertical
Cuadrado
LineaHorizontal
La clase polimórfica ElementoGrafico representa un elemento gráfico que puede imprimirse por
pantalla. Tiene un constructor por defecto que permite crear el objeto y proporciona los siguientes
métodos públicos virtuales:
• clonar(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• pintar() pinta el objeto gráfico en pantalla. En esta clase, simplemente escribe la palabra ERROR
en pantalla. Las clases derivadas deberán redefinir este método para escribir correctamente el
elemento gráfico específico por pantalla.
La clase polimórfica Punto representa a un elemento gráfico especializado, en concreto a un punto
(carácter ’.’), que puede imprimirse en pantalla. Suministra un constructor por defecto para crear
el objeto, además de los siguientes métodos públicos virtuales:
• clonar(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• pintar(): escribe un carácter punto (’.’) y un salto de línea en pantalla.
La clase polimórfica Linea representa a un elemento gráfico especializado, en concreto una línea, que
puede imprimirse en pantalla. Suministra un constructor por defecto para crear la línea e inicializarla
con longitud cero y un constructor extendido que inicializa la línea con una determinada longitud.
Además, suministra los siguientes métodos públicos virtuales:
• clonar(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• pintar(): pinta una línea en pantalla. Las clases derivadas deberán redefinir este método para
escribir una línea específica por pantalla.
• consultar_longitud(): devuelve la longitud de la línea actual.
• cambiar_longitud(lng): almacena el nuevo valor de la longitud de la línea actual recibida como
parámetro.
La clase polimórfica LineaHorizontal representa a un elemento gráfico especializado, en concreto
una línea horizontal, que puede imprimirse en pantalla (como una secuencia de caracteres ’-’).
Suministra un constructor por defecto para crear la línea e inicializarla con longitud cero y un
constructor extendido que inicializa la línea con una determinada longitud. Además, suministra los
siguientes métodos públicos virtuales:
• clonar(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• pintar(): pinta una línea horizontal en pantalla de la longitud almacenada en su estado: se
deben escribir en pantalla tantos caracteres ’-’ como indique su longitud, seguidos por un salto
de línea.
La clase polimórfica LineaVertical representa a un elemento gráfico especializado, en concreto una
línea vertical, que puede imprimirse en pantalla (como una secuencia de caracteres ’|’ dispuestos
verticalmente, es decir, separados por un ’\n’). Suministra un constructor por defecto para crear
la línea e inicializarla con longitud cero y un constructor extendido que inicializa la línea con una
determinada longitud. Además, suministra los siguientes métodos públicos virtuales:
• clonar(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
34
• pintar(): pinta una línea vertical en pantalla de la longitud almacenada en su estado: se deben
escribir en pantalla tantos caracteres ’|’ seguidos de saltos de línea como indique su longitud.
La clase polimórfica Cuadrado representa a un elemento gráfico especializado, en concreto un cuadrado, que puede imprimirse en pantalla (empleando caracteres |, ’-’, ’ ’ y ’\n’ dispuestos en pantalla
de manera que simulen los lados de un cuadrado). Suministra un constructor por defecto para crear
el cuadrado e inicializar su lado a cero y un constructor extendido que inicializa el cuadrado con un
determinado tamaño de lado. Además, suministra los siguientes métodos públicos virtuales:
• clonar(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• pintar(): pinta un cuadrado en pantalla empleando los caracteres |, ’-’, ’ ’ y ’\n’.
• consultar_lado(): devuelve la longitud del lado del cuadrado actual.
• cambiar_lado(lng): almacena el nuevo valor de la longitud del lado del cuadrado actual recibida
como parámetro.
Desarrolle un programa principal que declare un array de objetos polimórficos de las distintas clases
polimórficas (Punto, LineaHorizontal, LineaVertical y Cuadrado) de la jerarquía de ElementoGrafico
y los escriba en pantalla invocando al método virtual pintar().
2. Se dispone de una clase base de tipo Robot, encargado, de forma general, de ensamblar piezas en un
determinado objeto. En este ejercicio, se pretende construir un “Androide”, y se dispone de robots especializados (clases derivadas de Robot) en el ensamblaje de cada una de las piezas que componen el Androide
final, tal como la cabeza, el tronco, los brazos y las piernas. Además, también se dispone de un robot que
permite ensamblar la identificación de cada androide ensamblado.
Robot
RIdent
RCabeza
RTronco
RBrazos
RPiernas
En este primer ejercicio, el soporte donde ensamblar el androide es un simple string, donde se pueden ir
añadiendo las piezas que ensambla cada robot. Así, con objeto de simplificar el proceso y enfocar en los
conceptos de POO (herencia, polimorfismo y vinculación dinamica), cada robot especializado ensambla
una determinada pieza, representada por el caracter de su inicial: C, T, B, P, de una forma muy simple
en el objeto a construir, añadiendo simplemente el carácter que representa a la pieza al objeto de tipo
string. Por ejemplo, los dos primeros androides completamente ensamblados serían dos string con los
valores "ID1:CTBP" e "ID2:CTBP", considerando que cada identificación irá incrementando el valor de un
número de serie (contador).
La clase polimórfica Robot representa un robot general de ensamblaje, tiene un constructor por
defecto que permite crear un objeto, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• ensamblar(o, ok): recibe el objeto que se está ensamblando, de tipo string, como parámetro
de entrada/salida, y un parámetro ok, de tipo bool, de salida, para devolver si la operación de
ensamblaje se ha realizado correctamente. Las clases derivadas deberán redefinir este método
para ensamblar las piezas adecuadamente en el objeto recibido como parámetro.
La clase polimórfica RIdent representa un robot especializado en ensamblar la identificación de un
androide (un símbolo inicial de identificación, seguido por un número de serie y del símbolo dospuntos), tiene un constructor que permite crear un objeto con el símbolo inicial de identificación
(string) recibido como parámetro, y proporciona los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• ensamblar(o, ok): recibe el objeto que se está ensamblando, de tipo string, como parámetro
de entrada/salida, y un parámetro ok, de tipo bool, de salida, para devolver si la operación de
ensamblaje se ha realizado correctamente. Este método asigna la secuencia de identificación (el
símbolo, el número de serie y los dos puntos) al objeto que se está ensamblando, y posteriormente
incrementará el número de serie.
La clase polimórfica RCabeza representa un robot especializado en ensamblar la cabeza de un androide
(un carácter C), tiene un constructor por defecto que permite crear un objeto, y proporciona los
siguientes métodos públicos virtuales:
35
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• ensamblar(o, ok): recibe el objeto que se está ensamblando, de tipo string, como parámetro
de entrada/salida, y un parámetro ok, de tipo bool, de salida, para devolver si la operación de
ensamblaje se ha realizado correctamente. Este método añade el símbolo de la cabeza (C) al
objeto que se está ensamblando.
La clase polimórfica RTronco representa un robot especializado en ensamblar el tronco de un androide
(un carácter T), tiene un constructor por defecto que permite crear un objeto, y proporciona los
siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• ensamblar(o, ok): recibe el objeto que se está ensamblando, de tipo string, como parámetro
de entrada/salida, y un parámetro ok, de tipo bool, de salida, para devolver si la operación de
ensamblaje se ha realizado correctamente. Este método añade el símbolo del tronco (T) al objeto
que se está ensamblando.
La clase polimórfica RBrazos representa un robot especializado en ensamblar los brazos de un androide (un carácter B), tiene un constructor por defecto que permite crear un objeto, y proporciona
los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• ensamblar(o, ok): recibe el objeto que se está ensamblando, de tipo string, como parámetro
de entrada/salida, y un parámetro ok, de tipo bool, de salida, para devolver si la operación de
ensamblaje se ha realizado correctamente. Este método añade el símbolo de los brazos (B) al
objeto que se está ensamblando.
La clase polimórfica RPiernas representa un robot especializado en ensamblar las piernas de un
androide (un carácter P), tiene un constructor por defecto que permite crear un objeto, y proporciona
los siguientes métodos públicos virtuales:
• clone(): devuelve un puntero a un nuevo objeto creado como copia del objeto actual.
• ensamblar(o, ok): recibe el objeto que se está ensamblando, de tipo string, como parámetro
de entrada/salida, y un parámetro ok, de tipo bool, de salida, para devolver si la operación de
ensamblaje se ha realizado correctamente. Este método añade el símbolo de las piernas (P) al
objeto que se está ensamblando.
Finalmente, el programa principal creará los 5 robots de ensamblaje especializados necesarios, los
almacenará en una cadena de montaje (un array de robots generales) en el siguiente orden (RIdent,
RCabeza, RTronco, RBrazos y RPiernas), y ensamblará adecuadamente una determinada cantidad
de androides, considerando que cada objeto a ensamblar se va pasando de robot en robot en la cadena
de montaje hasta finalizar el ensamblaje completo del androide.
3. En este segundo ejercicio, se pretende ensamblar un androide un poco más sofisticado, con la siguiente
apariencia en el objeto a construir de tipo string:
ID1
o
/|\
/ \
considerando que el salto de línea se puede representar con el carácter ’\n’ dentro de un string.
4. En este tercer ejercicio, se modifica el objeto a construir para facilitar y flexibilizar la construcción y
ensamblaje de objetos diversos. Así, se define la nueva clase no-polimórfica Objeto2D que permite el
ensamblaje de piezas (de tipo string) en determinadas posiciones (fila y columna) de un plano-2D (un
array de dos dimensiones donde cada elemento es de tipo char).
Lo robots, ahora, en vez de ensamblar las piezas en un objeto de tipo string, se encargarán de ensamblar
las piezas adecuadas en este Objeto2D, pudiéndose mostrar el resultado final.
ID1
o
/|\
/ \
36
La clase no-polimórfica Objeto2D representa una superficie 2D donde es posible ensamblar piezas
diversas (representadas como caracteres en un string) en determinadas posiciones de la superficie,
tiene un constructor por defecto que permite crear un objeto, inicializando la superfice vacía, y
proporciona los siguientes métodos públicos:
• Tanto el constructor de copia, como el operador de asignación serán generados automáticamente
por el compilador.
• id(i): recibe como parámetro y almacena el identificador asociado al objeto que se está ensamblando.
• id(): devuelve el identificador que ha sido previamente asignado al objeto actual.
• mostrar(): muestra en pantalla tanto el identificador del objeto, como la superficie con las piezas
ensambladas. Es deseable que el tamaño de la zona 2D a mostrar se restrinja al tamaño del objeto
ensamblado.
• ensamblar_pieza(f, c, p, ok): recibe la fila y columna indicando la posición donde ensamblar
la pieza (string) recibida como tercer parámetro, y si todo es correcto, situa cada carácter de la
pieza en el sitio especificado. Finalmente, un parámetro ok, de tipo bool, de salida, indicará si la
operación de ensamblaje se ha realizado correctamente. Además, es deseable que vaya registrando
el tamaño del objeto que se está ensamblando.
5. En este cuarto ejercicio, se debe definir una nueva clase no-polimórfica denominada Factoria que contiene
una cadena de robots de ensamblaje (en un array), los cuales construyen androides pasándose el Objeto2D
entre ellos a lo largo de la cadena, y finalmente el producto final se almacena en un determinado almacen
(array) de Objeto2D. Estos productos finales ya ensamblados pueden ser extraidos para su utilización
fuera de la factoría.
La clase no-polimórfica Factoria representa una factoría con un almacen de como máximo 10
Objeto2D ensamblados, y una cadena de Robot con los 5 robots de ensamblaje especializados, tiene
un constructor que permite crear un objeto con el símbolo inicial de identificación (string) recibido
como parámetro, de tal forma que todos los androides ensamblados tendrán el mismo símbolo base
de identificación y construye los 5 robots especializados en la cadena de ensamblaje. Además, la clase
proporciona los siguientes métodos públicos:
• Se debe implementar el constructor de copia y el operador de asignación, que permitan copiar
la estructura de datos de la clase.
• construir(ok): si hay espacio en el almacen, entonces ensambla completamente un nuevo androide y lo almacena adecuadamente. Si la operación es posible o no, entonces devuelve true o
false, respectivamente, en el parámetro de salida.
• mostrar_almacen(): muestra en pantalla a todos los androides ensamblados que se encuentran
en el almacen.
• obtener_nobjs(): devuelve la cantidad de androides ensamblados que se encuentran en el almacen.
• extraer(): extrae del almacen y devuelve un Objeto2D con un androide ensamblado que se
encuentre en el almacen. Si no hay ningún androide ensamblado en el almacen, entonces devuelve
un Objeto2D vacío.
El programa principal creará una factoría, y construirá adecuadamente una determinada cantidad de
androides, que serán almacenados en el almacen de la factoría. Finalmente, extraerá los androides
almacenados y los mostrara por pantalla.
37
Práctica 9: La Clase Vector de la Biblioteca Estándar
1. Defina e implemente la clase Hotel (dentro del espacio de nombres umalcc) que defina las siguientes estructuras de datos y proporcione los siguientes métodos públicos para gestionar la ocupación de habitaciones
y reservas de un hotel:
Estructura de Datos: La estructura que contendrá los datos de ocupación de las habitaciones se definirá
mediante un tipo vector de dos dimensiones, donde cada fila almacena los datos de cada planta del hotel,
cada planta contiene las habitaciones de esa planta, y donde cada habitación se compone del nombre del
cliente (cadena de caracteres) que se encuentra alojado.
Hotel
habitaciones
0
0
nombre
1
···
2
···
1
···
···
···
2
···
···
···
3
···
···
···
4
···
···
···
Métodos Públicos:
a) Hotel(⇓ n_plantas, ⇓ n_habs):
Inicializará la estructura de datos Hotel a un estado vacío, es decir, sin habitaciones ocupadas, de tal
forma que el vector que representa las habitaciones del hotel se creará con tantas filas como indique el
parámetro n_plantas, y tantos elementos por fila como indique el parámetro n_habs.
b) ~Hotel():
Liberará y destruirá todos los recursos asociados a la estructura de datos Hotel.
c) Mostrar():
Muestra en pantalla toda la información almacenada en las estructuras de datos del Hotel, en un formato
adecuadamente legible.
d ) Alojar(⇓ nombre, ⇑ planta, ⇑ n_hab, ⇑ ok):
Comprueba si hay habitaciones disponibles, en cuyo caso alojará adecuadamente a dicho cliente en alguna
habitación que no esté ocupada. ok tomará el valor true, y planta y n_hab tomarán el valor de la planta
y número de habitación donde se alojará el cliente.
Cuando se aloja un determinado cliente en una habitación (planta y número de habitación), ésta pasará
a estar ocupada por dicho cliente, almacenando su nombre.
Si todas las habitaciones están ocupadas, ok tomará el valor false.
e) Desalojar(⇓ nombre, ⇑ ok):
Busca el cliente cuyo nombre se especifica como parámetro, y en caso de encontrarlo en una determinada
habitación, lo desalojará de ella borrando su nombre (a ""), y ok tomará el valor true. En otro caso, ok
tomará el valor false.
Notas:
a)
b)
c)
d)
⇓ Especifica un parámetro de entrada y ⇑ especifica un parámetro de salida.
El parámetro nombre es de tipo “cadena de caracteres”.
El parámetro ok es de tipo “lógico” (“boolean”).
Los parámetros planta y n_hab representan la planta y número de habitación donde se alojará el cliente
en caso de que la operación haya sido correcta, y se representan mediante números de tipo “entero”.
2. Diseñe un programa que utilice la clase Hotel definida anteriormente y permita gestionar las habitaciones
de un hotel.
3. Añada de forma incremental los siguientes métodos públicos a la clase Hotel realizado anteriormente:
a) Anyadir_Planta(⇑ ok):
Si el hotel tiene al menos una planta, entonces añade una nueva planta al hotel actual, a
continuación de las ya existentes, con tantas habitaciones como haya en la última planta, y
ok tomará el valor true. En otro caso, ok tomará el valor false. Estas nuevas habitaciones
se crearán en estado disponible.
b) Eliminar_Planta(⇑ ok):
Si el hotel tiene más de una planta, y la última planta tiene todas sus habitaciones en estado
disponible, entonces elimina la última planta del hotel y ok tomará el valor true. En otro
caso, ok tomará el valor false.
38
c) Extender_Habitaciones(⇓ n_habs, ⇑ ok):
Si el hotel tiene al menos una planta, y el numero de habitaciones de la primera planta es
menor que el valor del parámetro n_habs, entonces añade a cada planta tantas habitaciones
como sea necesario para que cada planta tenga el nuevo número de habitaciones especificado
por el valor del parámetro n_habs, y ok tomará el valor true. En otro caso, ok tomará el
valor false. Estas nuevas habitaciones se crearán en estado disponible.
4. Modifique el programa que utiliza la clase Hotel especificado anteriormente para que utilice los nuevos
métodos añadidos anteriormente.
39
Prácticas de Autoevaluación
Gestión de Factoría de Androides
La clase Pieza
Defina e implemente en el espacio de nombres umalcc la clase Pieza (en los ficheros pieza.hpp y pieza.cpp),
que proporcione los métodos públicos especificados a continuación.
Estructura de datos interna de la clase
La estructura de datos interna de la clase Pieza almacena la información relativa a una
determinada pieza, compuesta por su código de pieza (de tipo int), y su identificador
(de tipo string). El código de la pieza hace referencia al tipo de pieza en concreto,
según los códigos representados en la tabla lateral.
cod
id
2
C-123-4567
Código
0
1
2
3
4
5
Descripción
Cabeza
Tronco
Brazo-Izq
Brazo-Dch
Pierna-Izq
Pierna-Dch
Métodos Públicos
~Pieza();
// Destructor
Destruye el objeto, liberando todos los recursos asociados al mismo.
Pieza();
// Constructor por defecto
Inicializa el código del objeto con valor -1 y el identificador con valor vacío ("").
Pieza(const int cod, const std::string& id);
Inicializa el objeto con los valores especificados en los parámetros. El primer parámetro especifica el código
de la pieza, y el segundo parámetro su identificador.
Tanto el constructor de copia como el operador de asignación serán generados por el compilador.
int obtener_codigo() const;
Devuelve el valor del atributo código del objeto actual.
std::string obtener_id() const;
Devuelve el valor del atributo identificador del objeto actual.
bool error() const;
Devuelve true si el valor del atributo código es inválido, o si el valor del atributo identificador es vacío
(""). En otro caso, devuelve false.
void mostrar() const;
Muestra en pantalla, según el siguiente formato, la información almacenada en el estado interno del objeto.
Si el objeto está en modo erróneo, añadirá la palabra ERROR al final de la línea. Por ejemplo:
2 C-123-4567
7 C-123-4567 ERROR
La clase Androide
Defina e implemente en el espacio de nombres umalcc la clase Androide (en los ficheros androide.hpp y
androide.cpp), que proporcione los métodos públicos especificados a continuación.
Estructura de datos interna de la clase
La estructura de datos interna de la clase Androide almacena la información relativa a
la composición de un determinado androide, compuesta por su identificador (de tipo
string), y de un array (piezas) de 6 cadenas de caracteres (string), de tal forma que
el identificador de cada pieza que compone el androide se almacenará en el índice que
se corresponde con el código de la pieza. Por ejemplo, el identificador de la cabeza se
almacenará en la posición 0 del array, el tronco en la posición 1, el brazo izquierdo en
la posición 2, y así sucesivamente, según la tabla de códigos especificada en la clase
Pieza.
40
id
piezas
0
1
2
3
4
5
B-987-6734
A-251-4245
C-251-9682
C-123-4567
C-123-2563
A-762-8230
C-762-8274
Métodos Públicos
~Androide();
// Destructor
Destruye el objeto, liberando todos los recursos asociados al mismo.
Androide();
// Constructor por defecto
Inicializa el identificador del objeto y de las piezas que lo componen con valor vacío ("").
Androide(const std::string& i);
Inicializa el identificador del objeto con el valor especificado en el parámetro, y el identificador de las
piezas que lo componen con valor vacío ("").
Tanto el constructor de copia como el operador de asignación serán generados por el compilador.
std::string obtener_id() const;
Devuelve el valor del atributo identificador del objeto actual.
void anyadir_pieza(const Pieza& p, bool& ok);
Añade la pieza especificada a los componentes del objeto actual en la posición adecuada (indicada por el
código de la pieza) y devuelve true en ok si la operación se realiza correctamente. La operación fallará,
y devolverá false en ok, si el objeto actual es erróneo, o si la pieza a añadir es errónea, o si ya existe ese
componente en el objeto (el identificador almacenado en la posición indicada por el código no es vacío).
void retirar_pieza(int codigo, Pieza& p);
Retira del objeto actual la pieza cuyo código se especifica como parámetro, y devuelve su valor adecuadamente (código e identificador ) en el parámetro de salida p. Si el código es erróneo, o el componente
especificado no existe, o el objeto actual es erróneo, entonces devolverá una pieza vacía (en modo erróneo).
bool error() const;
Devuelve true si el valor del atributo identificador es vacío (""). En otro caso, devuelve false.
void mostrar() const;
Muestra en pantalla, según el siguiente formato, la información almacenada en el estado interno del objeto.
Si el objeto está en modo erróneo, añadirá la palabra ERROR al final de la primera línea.
Androide B-987-6734
0 A-251-4245
1 C-251-9682
2 C-123-4567
3 C-123-2563
4 A-762-8230
5 C-762-8274
Androide ERROR
0
1
2
3
4
5
La clase Factoría
Defina e implemente en el espacio de nombres umalcc la clase Factoria (en los ficheros factoria.hpp y
factoria.cpp), que proporcione los métodos públicos especificados a continuación.
Estructura de datos interna de la clase
La estructura de datos interna de la clase Factoria almacena información para facilitar el ensamblaje de
androides utilizando las piezas adecuadas, para ello utiliza las clases Pieza y Androide definidas anteriormente.
Así, la clase Factoria contiene un primer atributo, androides, que almacena los androides ya ensamblados en
un vector1 de elementos de tipo Androide. Además, tiene un segundo atributo, piezas, que es un puntero a
nodos que se encuentran organizados como una lista enlazada, donde cada nodo de la lista enlazada contiene
un objeto de la clase Pieza. Por ejemplo:
el alumno lo desea, obteniendo menor puntuación, podrá utilizar una estructura de array-incompleto de máximo 10 elementos
en lugar del tipo vector.
1 Si
41
Factoria
androides
B-987-6734
A-765-9823
D-987-6734
A-251-4245
C-251-9682
C-123-4567
C-123-2563
A-762-8230
C-762-8274
A-232-8822
A-232-8923
A-213-1233
A-213-9232
A-621-9234
A-621-9213
A-251-2312
C-213-9213
F-912-2139
F-912-2138
F-912-8923
F-912-8213
piezas
•
•
-
-
3
F-732-2193
•
-
0
A-823-9332
•
-
1
A-824-8455
/
2
A-213-2372
Métodos Públicos
~Factoria();
// Destructor
Destruye el objeto, liberando todos los recursos asociados al mismo.
Factoria();
// Constructor por defecto
Inicializa el estado interno del objeto con el vector de androides vacío y
la lista de piezas vacía.
Factoria(const Factoria& obj);
Androide B-987-6734
0 A-251-4245
1 C-251-9682
2 C-123-4567
3 C-123-2563
4 A-762-8230
5 C-762-8274
Androide A-765-9823
0 A-232-8822
1 A-232-8923
2 A-213-1233
3 A-213-9232
4 A-621-9234
5 A-621-9213
Androide D-987-6734
0 A-251-2312
1 C-213-9213
2 F-912-2139
3 F-912-2138
4 F-912-8923
5 F-912-8213
Piezas
3 F-732-2193
0 A-823-9332
1 A-824-8455
2 A-213-2372
// Constructor de copia
Crea un nuevo objeto copiando (duplicando) el estado del objeto obj recibido como parámetro.
Nótese que NO se debe implementar el operador de asignación.
void mostrar() const;
Muestra en pantalla, según el formato mostrado en la figura superior, la información del estado interno
del objeto (el vector de androides desde el principio, y la lista de piezas según su orden secuencial).
void anyadir_pieza(const int cod, const std::string& id, bool& ok);
Si NO existe en la lista de piezas ninguna pieza con identificador igual al especificado por el parámetro
id, entonces crea una pieza con los parámetros especificados, y si la pieza creada NO es errónea, entonces
la inserta al final en la lista de piezas, y asigna true al parámetro ok. En otro caso, asigna false al
parámetro ok.
void eliminar_pieza(const std::string& id, bool& ok);
En caso de que exista, elimina de la lista de piezas aquella pieza cuyo identificador sea igual al valor del
parámetro id, y asigna true al parámetro ok. En otro caso, asigna false al parámetro ok.
void ensamblar_androide(const std::string& id, bool& ok);
Si NO existe en el sistema ningún androide con identificador igual al especificado por el parámetro id,
entonces crea un androide con el identificador especificado, y si el androide creado NO es erróneo, entonces:
1. Comprueba si hay piezas suficientes de los códigos adecuados para ensamblar un androide.
2. En caso afirmativo del paso anterior:
a) Retira las piezas de los códigos adecuados de la lista de piezas y las añade al androide hasta
completarlo.
b) Inserta el androide ensamblado al final en el vector de androides.
c) Finalmente asigna true al parámetro ok.
En el caso de que no se haya podido realizar la operación correctamente, entonces asigna false al parámetro ok.
void desmontar_androide(const std::string& id, bool& ok);
En caso de que exista, retira las piezas que componen el androide cuyo identificador sea igual al valor del
parámetro id, añadiéndolas a la lista de piezas, también elimina del vector de androides aquel androide
cuyo identificador sea igual al valor del parámetro id, y asigna true al parámetro ok. En otro caso, asigna
false al parámetro ok.
42
El programa Principal
Así mismo, el alumno puede utilizar el programa principal que se proporciona (en el fichero main_androides.cpp
que debe descargarse desde el Campus Virtual ) para poder comprobar adecuadamente la corrección de los métodos de las clases.
Además, se proporciona un fichero (salida_androides.txt) con la salida esperada para el programa principal que se suministra, de tal forma que el alumno pueda comprobar su corrección.
43
Gestión de Tareas Periódicas
La clase Tarea
Defina e implemente en el espacio de nombres umalcc la clase Tarea (en los ficheros tarea.hpp y tarea.cpp),
que proporcione los métodos públicos especificados a continuación.
Estructura de datos interna de la clase
La estructura de datos interna de la clase Tarea almacena la información relativa a una
determinada tarea periódica del sistema, tal como su identificador (de tipo string), el
código de la tarea (de tipo int), el periodo de ejecución (en segundos) de la tarea (de
tipo int), el límite de tiempo (en segundos) que indica el tiempo en el que finalizará
la ejecución de la tarea (de tipo int), la prioridad de la tarea, siendo un número entero
mayor o igual a cero, donde el valor cero indica la mayor prioridad (de tipo int), el tiempo
próximo de ejecución (en segundos) que indica cuando será ejecutada próximamente la
tarea (de tipo int) y un determinado valor que representa un número entero (de tipo
int).
Tarea
identificador
código
periodo
límite
prioridad
próximo
valor
Tarea-01
1
5
30
0
10
2
Métodos Públicos
~Tarea();
// Destructor
Destruye el objeto, liberando todos los recursos asociados al mismo.
Tarea();
// Constructor por defecto
Inicializa el objeto con identificador vacío (""), y con valor cero (0) el resto de atributos.
Tarea(const std::string& id, int cod, int per, int lim, int prio, int prox, int val);
Inicializa el objeto con los valores especificados en los parámetros. Los parámetros están especificados en
el mismo orden que los atributos en la figura anterior.
// Tarea(const Tarea& obj);
// Tarea& operator=(const Tarea& obj);
// Constructor de copia
// Operador de asignación
Tanto el constructor de copia como el operador de asignación serán generados por el compilador.
std::string obtener_id() const;
Devuelve el valor del atributo identificador del objeto actual.
int obtener_prioridad() const;
Devuelve el valor del atributo prioridad del objeto actual.
int obtener_proximo() const;
Devuelve el valor del atributo próximo del objeto actual.
bool esta_activa() const;
Devuelve true si el valor del atributo próximo es menor que el valor del atributo límite en el objeto
actual. En otro caso, devuelve false.
bool error() const;
Devuelve true si el valor del atributo identificador es vacío (""), o si el valor del atributo código o el valor
del atributo periodo es menor o igual a cero. En otro caso, devuelve false.
void mostrar_estado() const;
Muestra en pantalla, según el siguiente formato, la información almacenada en el estado interno del objeto.
Además, si la tarea NO está activa, entonces también mostrará dos asteriscos (**) al final de la línea.
Para el ejemplo de la figura anterior mostrará:
Tarea-01 1 5 30 0 10 2
void ejecutar(int tiempo_actual);
Si el valor del atributo próximo del objeto es igual al valor del parámetro tiempo_actual, entonces ejecuta
la tarea actual según lo especificado a continuación:
44
1. Incrementa el valor del atributo próximo con el valor del atributo periodo.
2. Si el valor del parámetro tiempo_actual es menor que el valor del atributo límite, entonces realiza
las siguientes acciones:
a) Si el valor del atributo código de la tarea es uno (1), entonces eleva al cuadrado el valor del
atributo valor y almacena el resultado en el atributo valor.
b) Si el valor del atributo código de la tarea es dos (2), entonces multiplica por dos el valor del
atributo valor y almacena el resultado en el atributo valor.
c) Si el valor del atributo código de la tarea es tres (3), entonces multiplica por tres el valor del
atributo valor y almacena el resultado en el atributo valor.
La clase Planificador
Defina e implemente en el espacio de nombres umalcc la clase Planificador (en los ficheros planificador.hpp
y planificador.cpp), que proporcione los métodos públicos especificados a continuación.
Estructura de datos interna de la clase
La estructura de datos interna de la clase Planificador almacena información para facilitar la gestión y
ejecución de tareas periódicas del sistema, para ello utiliza la clase Tarea definida anteriormente. Así, la clase
Planificador contiene un atributo que almacena el valor del tiempo_actual de la ejecución en segundos (de
tipo int). Además, tiene un segundo atributo, tareas, que es un vector de punteros a nodos que se encuentran
organizados como listas enlazadas ordenadas, donde cada nodo de la lista enlazada contiene un objeto de la
clase Tarea.
Cada tarea se encontrará en la lista enlazada que comienza en el elemento del vector cuyo índice es igual
a la prioridad de la tarea, y se encontrará ordenada según el valor de su atributo próximo. Por ejemplo:
Planificador
tiempo_actual
1
tareas
0
•
1
/
2
/
3
/
4
/
5
/
6
/
7
•
-
•
Tarea-07
···
prio: 0
prox: 1
-
•
Tarea-03
···
prio: 0
prox: 1
-
•
Tarea-05
···
prio: 7
prox: 1
-
/
Tarea-02
···
prio: 7
prox: 4
-
/
Tarea-09
···
prio: 0
prox: 3
Métodos Públicos
~Planificador();
// Destructor
Destruye el objeto, liberando todos los recursos asociados al mismo.
Planificador();
// Constructor por defecto
Inicializa el estado interno del objeto con el tiempo_actual a cero (0), y el vector de tareas vacío.
Planificador(const Planificador& obj);
// Constructor de copia
Crea un nuevo objeto copiando (duplicando) el estado del objeto obj recibido como parámetro.
Nótese que NO se debe implementar el operador de asignación.
void mostrar() const;
Muestra en pantalla, según el siguiente formato, la información del estado interno del objeto (el vector de
tareas desde el principio, y las listas de tareas según su ordenación). Si alguna lista de tareas está vacía,
entonces mostrará el mensaje Vacio. Para el ejemplo de la figura anterior, mostrará:
Tiempo actual: 1
Tarea-07 2 7 30
Tarea-03 1 3 30
Tarea-09 1 5
2
Vacio
Vacio
Vacio
Vacio
Vacio
Vacio
Tarea-05 1 4 30
Tarea-02 3 3
2
0
0
0
1
1
3
32
2
8 **
7
7
1
4
4
2 **
45
void anyadir_tarea(const std::string& id, int cod, int per, int lim,
int prio, int prox, int val, bool& ok);
Si el valor del parámetro prio es mayor o igual a cero, y el valor del parámetro prox es mayor o igual al valor
del atributo tiempo_actual, y NO existe en el sistema una tarea con identificador igual al especificado
por el parámetro id, entonces crea una tarea con los parámetros especificados, y si la tarea creada NO es
errónea, entonces la inserta de forma ordenada (según el valor del parámetro prox ) en la lista de tareas
cuyo índice es igual al valor del parámetro prio, y asigna true al parámetro ok. En otro caso, asigna false
al parámetro ok.
Nota: debe asegurarse de que la lista de tareas con la prioridad especificada ha sido creada adecuadamente
en el vector de tareas.
void eliminar_tarea(const std::string& id, bool& ok);
En caso de que exista, elimina del sistema aquella tarea cuyo identificador sea igual al valor del parámetro
id, y asigna true al parámetro ok. En otro caso, asigna false al parámetro ok.
Nota: en caso de que los últimos elementos del vector de tareas sean listas vacías, estos elementos deberán
ser eliminados, de tal forma que el último elemento del vector, si existe, debe ser una lista NO vacía.
bool hay_tareas_activas() const;
Devuelve true si hay tareas activas (véase el método esta_activa() de la clase Tarea) en el sistema, y
false en otro caso.
void eliminar_tareas_terminadas();
Elimina del sistema todas aquellas tareas que NO están activas (véase el método esta_activa() de la
clase Tarea).
Nota: en caso de que los últimos elementos del vector de tareas sean listas vacías, estos elementos deberán
ser eliminados, de tal forma que el último elemento del vector, si existe, debe ser una lista NO vacía.
void ejecutar_ciclo();
Ejecuta las tareas del sistema que están preparadas, según el orden de prioridad del vector de tareas (el
índice cero es el más prioritario), y el orden del tiempo próximo de ejecución de cada tarea. Al finalizar,
deberá incrementar en uno (1) el valor del atributo tiempo_actual del objeto.
Así, para cada tarea cuyo tiempo próximo de ejecución es igual al valor del atributo tiempo_actual, se
extrae la tarea del principio de la lista enlazada correspondiente, entonces se ejecuta la tarea (invocando
al método ejecutar() de la clase Tarea), y se vuelve a insertar de forma ordenada (según su nuevo tiempo
próximo de ejecución) en la lista correspondiente según su prioridad.
El programa Principal
Así mismo, el alumno puede utilizar el programa principal que se proporciona (en el fichero main_tareas.cpp
que debe descargarse desde el Campus Virtual ) para poder comprobar adecuadamente la corrección de los
métodos de las clases.
Además, se proporciona un fichero (salida_tareas.txt) con la salida esperada para el programa principal
que se suministra, de tal forma que el alumno pueda comprobar su corrección.
46
Descargar