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