Programación C++ Tipos Parametrizados Dr. Oldemar Rodríguez Rojas Escuela de Informática Universidad Nacional Tipos (clases) parametrizados “Templates” La noción de "templates" provee los llamados algunas veces Tipos Parametrizados o Genéricos. Para esto se reincorporan en C++ conceptos que estaban presentes en lenguajes como Clu y Ada. [Stroustrup-2] ¿Para qué sirven los tipos parámetrizados? Es muy común en programación implementar una misma estructura de datos varias veces, esto debido a que el tipo del nodo es diferente. Por ejemplo, implementar una lista de Personas y luego implementar una una lista de Libros ambas con los mismos algoritmos. evitar esta redundancia de código, Bjarne Stroustrup introduce en C++ la idea de que una clase pueda recibir un tipo o varios tipos como parámetro. Para Sintaxis los tipos parámetrizados Antes de presentar la nueva implementación de la lista de “Personas”, dedicaremos esta sección a explicar con detalle la sintaxis y la semántica de los tipos parametrizados. Para ilustrar este nuevo concepto implementaremos una Pila Parametrizada mediante un arreglo. Plantillas Cuando se crea una función o una clase, se debe especificar el tipo de dato que recibe como parámetros y los que retorna, esto para el caso de las funciones, y los tipos de datos que contiene la clase como atributos y los tipos de datos con que operan sus métodos. Plantillas Cuando se desea ingresar un tipo de dato con el cual no fue definida la función o clase, se debe sobrescribir para poder operar sobre esta, esto implica una definición diferente por cada tipo de dato. Esto se hace difícil de mantener en la práctica, pues si se desea hacer un corrección, o incluir alguna mejora se debe hacer sobre la totalidad de las definiciones, además que el código sería muy largo y difícil de mantener. Plantillas Las plantillas (templates) resuelven este problema, estas nos permiten parametrizar estas clases o funciones para adaptarlas a cualquier tipo de dato. Tipos parametrizables Las plantillas nos permiten decirle al compilador que haga el trabajo pesado por nosotros, y que sea el quien genere una declaración de una función o clase para que estas puedan operar o tratar con un tipo de dato en concreto, sin nosotros entrar a implementar una declaración por cada tipo existente. Cómo crear una plantilla? C++ permite crear plantillas de funciones y plantillas de clases. La palabra clave “template” es utilizada al principio de cada declaración y definición de una clase o función plantilla. Los parámetros de la plantilla se escriben dentro de los operadores “<>”, estos corresponden a las cosas que pueden cambiar con cada instancia. Clases Plantilla La sintaxis para declarar una clase plantilla es parecida a la de cualquier otra clase, pero nuevamente se debe añadir al principio una presentación de la clase que se usará como referencia en la plantilla: template<class id> class nombreClase { // Declaración de funciones // y datos miembro de la plantilla }; template <class TIPO> class Pila { TIPO* Datos; int Top; public: Pila() { Datos = new TIPO[MAX]; Top = VACIO; } ~Pila() {delete [] Datos; } void Insertar(TIPO c); TIPO Sacar(); bool Vacia(); bool Llena(); }; UNA PILA PARAMETRIZADA VER <EJ1.CPP> Compatibilidad entre tipos parametrizados La compatibilidad entre tipos parametrizados no es tan clara como en los tipos usuales de C++, debido a que el compilador de C++ no puede hacer una conversión automática de tipos. Suponga que en el ejemplo anterior cambiamos el programa principal por el siguiente: int main() { Pila<int> x_int1; x_int1.Insertar(1); x_int1.Insertar(2); Pila<int> x_int2; x_int2.Insertar(3); x_int2.Insertar(-2); Pila<float> x_float; x_float.Insertar(1.3); x_float.Insertar(5.1); x_int1=x_int2; x_int1=x_float; // Esta línea genera // error return 0; } En el anterior fragmento de código se declaran tres pilas: dos pilas de tipo entero y la tercera de tipo flotante. La última línea de este programa genera error debido a que C++ no puede convertir en forma automática la pila de flotantes en una pila de enteros. Es posible asignar una pila de enteros a otra pila de enteros, como se hace con la instrucción: x_int1=x_int2; Los argumentos en clases parametrizadas Los argumentos de un tipo (clase) parametrizado no están restringidos a clases, aunque esto es lo más común. Los parámetros de un tipo parametrizado pueden ser enteros, flotantes, nombres de funciones o incluso expresiones constantes. Por ejemplo en el siguiente fragmento de código se define una clase parametrizada Vector, en la que son parámetro el tipo de la pila y el tamaño del arreglo que almacenará el vector. template <class TIPO, int n> class Vector { TIPO Datos[n]; ......... } Dos instancias de clase son compatibles si sus argumentos en el parámetro TIPO son los mismos y si sus argumentos en n tienen el mismo valor, por ejemplo las siguientes instancias son compatibles: Vector<float,100> V1; Vector<float, 2*50> V2; En el siguiente ejemplo mejoramos la implementación de la pila del ejemplo anterior, para esto incorporamos un nuevo parámetro para el tamaño máximo de la pila. template <class TIPO, int Max> class Pila { TIPO* Datos; int Top; public: Pila() { Datos = new TIPO[Max]; Top = VACIO; } ~Pila() {delete [] Datos; } void Insertar(TIPO c); TIPO Sacar(); bool Vacia(); bool Llena(); }; template <class TIPO,int Max> void Pila<TIPO,Max>::Insertar(TIPO c) { Datos[++Top] = c; } template <class TIPO,int Max> TIPO Pila<TIPO,Max>::Sacar() { return (Datos[Top--]); } template <class TIPO,int Max> bool Pila<TIPO,Max>::Vacia() { return (Top==VACIO); } template <class TIPO,int Max> bool Pila<TIPO,Max>::Llena() { return (Top==Max-1); } Funciones con tipo parametrizado Además de clases parametrizadas, C++ permite definir funciones con tipo parametrizado de manera muy similar. Una función parametrizada (template function) define en realidad una familia de funciones; cada una con un tipo particular, los parámetros con que la función es invocada determinan cual versión de la función será ejecutada. En el siguiente fragmento de código se presenta una función parametrizada que calcula el mínimo entre dos elementos cuyo tipo es desconocido cuando la función es implementada. include <iostream> // VER <EJ2.CPP> using std::endl; using std::cin; using std::cout; template<class TIPO> TIPO Min(TIPO a, TIPO b) { if(a<b) return a; else return b; } int main() { char Ch1='O', Ch2='F', Res; int N1=100, N2=200, R; Res=Min(Ch1,Ch2); cout << " El caracter menor es: " << Res; R=Min(N1,N2); cout << "\n El entero menor es " << R; cout << "\n Fin \n"; system("pause"); system("cls"); return 0; } ¿Cuál es la diferencia entre tipos parametrizados y polimorfismo? ¿Puede uno sustituir al otro? ¿Cuál concepto es mejor o más potente? Las principales diferencias en C++ entre una función polimórfica y una función parametrizada son las siguientes: 1. Las funciones parametrizadas pueden trabajar con tipos aritméticos. 2. Las funciones polimórficas deben usar punteros. 3. La generalidad del polimorfismo se restringe a una jerarquía de clases. 4. La generalidad de las funciones parametrizadas es ilimitada. 5. Las funciones parametrizadas tienden a generar un archivo ejecutable más grande. La STL - “Standard Template Library” El mes de abril de 1995, los comités ANSI e ISO C++ publicaron su primer documento oficial, el Committe Draft (CD) para estudio y revisión de la comunidad de desarrollo informática internacional. La biblioteca C++ Standard, conocida como Standard Template Library (STL) es una biblioteca que incluye clases contenedoras y algoritmos basados en plantillas. STL es una biblioteca muy importante para el desarrollo de programas profesionales dado que facilitará considerablemente el diseño y construcción de aplicaciones basadas en estructuras de datos tales como vectores, listas, conjuntos y algoritmos. La librería estándar de plantillas (STL -The Standard Template Library) 1 • STL proporciona un conjunto de estructuras de datos y de algoritmos que operan sobre aquéllas. 2 • Es un entorno conceptual que facilita a los usuarios la adición de sus propios algoritmos y estructuras de datos. 3 • Los nuevos algoritmos funcionarán con todas las estructuras de datos (contenedores) existentes, a la vez que sobre las nuevas estructuras de datos se podrán aplicar todos los algoritmos existentes Algoritmo • Define el procedimiento computacional Contenedor • Gestiona un conjunto de localizaciones en memoria Iterador • Provee un medio para que un algoritmo recorra un contenedor ObjetoFunción • Objeto reutilizable por otros componentes Adaptador • Adapta un componente a una interfaz distinta La librería estándar de plantillas (STL -The Standard Template Library) El núcleo de la STL son los algoritmos, que utilizan iteradores para acceder a contenedores. Características: proveen un fuerte chequeo de tipos sus iteradores son generalizaciones de los punteros permiten el uso en algoritmos, de funciones encapsuladas en objetos los componentes de la STL son al menos tan eficientes como los codificados a mano Contenedores Es un objeto que contiene otros objetos. Estos contenedores son implementados como clases plantillas, que permiten gran flexibilidad en los tipos soportados por los elementos. Un contenedor maneja los espacios de almacenamientos para sus elementos, y provee funciones miembro para el acceso a estos directamente o a través de los iteradores (objetos de referencia que tienen un comportamiento similar a los punteros) Categorías de contenedores Secuencial • Almacenamiento de ítems es lineal, con inserciones rápidas al final • Vector Asociativo • Para búsquedas asociativas rápidas • Conjuntos Adaptador • Interfaces a otros tipos de colecciones • Colas, pilas Contenedores secuenciales Estas clases son diseñadas para brindar acceso secuencial y aleatorio a sus miembros o elementos. Provee un acceso secuencial eficiente a la lista de objetos que contiene. La librería estándar de C++ provee tres tipos diferentes de contenedores de este tipo: Vector List Deque (Doble vector, inserción adelante y atrás) Contenedores asociativos Son contenedores optimizados para tener acceso a sus elementos mediante valores claves. Son optimizados internamente C++ provee cuatro tipos diferentes de contenedores de este tipo: Map Multimap Set Multiset Contenedores adaptadores Los adaptadores son en esencia, plantillas que procuran un interfaz especial para los otros tipos de contenedores. Los adaptadores “adaptan” o “adecúan” un determinado contenedor para que adopte un nuevo comportamiento, restringiendo ciertas características y cambiando otras. Stack queue priority_queue ¿Qué es un Iterador? 1 2 3 • Un iterador es algo que permite un conjunto de operaciones en una estructura de datos sin tener en cuenta los tipos de datos o la naturaleza de tal estructura. • Un iterador es un mediador independiente entre los datos y el algoritmo o proceso a realizar sobre estos. • Un iterador es un objeto que permite recorrer los elementos de una colección, recorriéndola (o “atravesándola”) con distintas estrategias, para cada estrategia quizás haya que procurar un iterador distinto. #include <iostream> #include <vector> using namespace std; Ejemplo de Iterator: ver <Ej2-1.cpp> int main () { vector<int> myvector; for (int i=1; i<=5; i++) myvector.push_back(i); vector<int>::iterator it; cout << "myvector contains:"; for(it=myvector.begin();it<myvector.end();it++) cout << " " << *it; cout << endl; return 0; } #include <iostream> #include <vector> using namespace std; Ejemplo de Iterator Reverso: ver <Ej2-2.cpp> int main () { vector<int> myvector; for (int i=1; i<=5; i++) myvector.push_back(i); cout << "myvector contains:"; vector<int>::reverse_iterator rit; for (rit=myvector.rbegin();rit<myvector.rend();++rit ) cout << " " << *rit; cout << endl; return 0; } Algoritmos Cada contenedor define operaciones para manipular así mismo y a sus elementos, implementar todas estas operaciones por cuenta propia, puede ser muy engorroso y posiblemente se comentan errores en el proceso. Debido a que casi todas estas operaciones son similares para cualquier tipo de elementos almacenados, un conjunto de algoritmos genéricos ya incluidos en STL pueden ser utilizados directamente. Algunos de los algoritmos más comúnmente utilizados: Ejemplo: Uso de la clase list Ver <EJ3.CPP> Lista de Personas mediante la reutilización de la clase list Ver <Ej4.cpp> Tarea: Estudiar todos los Ejemplos del Capítulo 22 del libro C++ Cómo Programar de Deitel y entregarlos en un CD con los respectivos proyectos en Visual C++ y con todos los comentarios en español. MUCHAS GRACIAS….