Índice
Nociones generales Plantillas para funciones Plantillas para clases
Nociones generales de plantillas Mecanismo mediante el cual un tipo puede ser usado como
parámetro en la definición de una clase o función: GENERALIZACIÓN
Se pueden generalizar tipos de datos base o de usuario La instanciación concreta de la plantilla con un tipo concreto se
denomina especialización Puede verse como un polimorfismo de tipo (ver tema anterior)
pero en tiempo de compilación
Ejemplo de prototipo de plantilla de FUNCIÓN
template <class T> void foo (T& ref);
Ejemplo de plantilla de CLASE template <class T> class C{ public: T atributo; };
Ejemplo de definición de una plantilla de FUNCIÓN (*.cpp)
template <class T> T min(T a, T b){ return (a<b)? a:b; }
PLANTILLAS PARA FUNCIONES
Consideraciones generales
La especialización de la plantilla de función se produce en tiempo de compilación, en la instrucción de llamada Si no se llama a la función, no se incorpora al binario el código
de la plantilla Pueden tener múltiples parámetros <class T, class U, …>
template <class T> T min(T a, T b){ return (a<b)? a:b; } void main(){ float a=3.18, b=4.7; cout<<min(a,b)<<endl; //o min<float>(a, b) }
Instanciación por el compilador en la llamada
CUESTIÓN Implemente una plantilla de función que intercambie los valores de dos tipos base cualesquiera
template <class T> void swap(T& a, T& b){ T temp = a; a=b; b=temp; }
Sintaxis
Identificadores de tipo (p.ej. ‘T’), precedidos de la palabra clave class ó typename
template <class T, class U> void foo( T&, U&, int n=10 );
template <class T, class U, int n > void foo(T&, U& );
Tipos base: especialización siempre como constante
Parámetros múltiples
template <class | typename <id> [,…]> <tipo de retorno> <identificador> (<lista de parametros>) ;
Ejemplo I
template <class T, int n> void foo(T& ref ){ cout<<"el argumento es:"<<ref+n<<endl; } template <class T> void foo(T& ref, int n ){ cout<<"el argumento es:"<<ref+n<<endl; }
CUESTION Indique cuales de estos casos de uso de las funciones genéricas son correctos:
void main(){ int k=0; foo<int, 3>(k); //A foo<int>(k,3); //B foo(k,3); //C foo(k); //D }
Ejemplo II: Especialización
template <class T> bool isEqual(T a, T b){ return (a==b)? 1:0; } void main(){ double a=3.18, b=3.19; if(isEqual(a,b)) cout<<"son iguales"<<endl; }
bool isEqual(double a, double b){ return (abs(a-b)<=0.1)? 1:0; }
CUESTION I. Especialice la función genérica isEqual para que defina la relación de igualdad para números en coma flotante de doble precisión con una tolerancia de 0,1. II. Compruebe su correcto funcionamiento.
Consideraciones para el compilador La definición y declaración de una plantilla genérica pueden ir
separadas en fichero de cabecera (.h) y fichero fuente (.cpp). Sin embargo, el compilador necesita conocer la definición de la
plantilla para especializarla. Si se pretende especializar una función en un fichero fuente distinto
a donde está definida, lo más simple es incluir la definición de la plantilla en el propio fichero de encabezado Fichero: plantilla.h inline template <class T> bool isEqual(T a, T b){ return (a==b)? 1:0; }
#include "plantilla.h" void main(){ double a=3.18, b=4.7; if(isEqual(a,b)) cout<<"son iguales"<<endl; }
Si la definición de isEqual se encuentra en plantilla.cpp no puede enlazar
Otras consideraciones
Una especialización manual de una plantilla función es una definición de función ordinaria
Es posible sobrecargar las plantillas de funciones mediante el mecanismo habitual de tipo
Pueden ser métodos de clases
Fichero: punto.h class Punto{ double x; double y; public: Punto(double x, double y):x(x), y(y){} void print(){ cout<<"x:"<<x<<","<<"y:"<<y<<endl; } template <class U> void print() { cout<<"x:"<<(U)x<<","<<"y:"<<(U)y<<endl; } };
PLANTILLAS PARA CLASES
Generalidades
Generalización extendida al caso de clases EJERCICIO Implemente una clase Punto genérica para 2D y cualquier tipo base numérico de coordenadas. Incluya un constructor apropiado y una función que muestre en pantalla ambas coordenadas
Declaración (punto.h) template <class T> class Punto { T x, y; public: Punto(T x, T y); void print(){cout<<“(“<<x<<“:”<<y<<“)”<<endl;} };
Definición del constructor (punto.cpp) template <class T> Punto<T>::Punto(T x, T y){ this->x=x; this->y=y; } Test unitario(main.cpp)
void main(){ Punto<int> miPunto(10, 20); miPunto.print(); }
Sintaxis obligatoria
Aspectos de compilación En principio NO es posible pasar al usuario de una plantilla de clase
solamente el fichero cabecera con la declaración de la clase genérica (y el fuente precompilado)
Es necesario conocer también las definiciones/especializaciones del tipo para que el compilador pueda instanciar la clase.
Soluciones: A. Las definiciones se colocan en el fichero de cabecera B. Se coloca una declaración de especialización genérica en el fichero fuente para
cada función miembro específica que va a ser instanciada
Fichero cabecera (punto.h) template<class T>class Punto{ T x;T y; public: Punto(T x, T y); void print(){cout<<x<<“:”<<y<<endl;} };
Fichero de definición (punto.cpp) template <class T> Punto<T>::Punto(T x, T y){ this->x = x; this->y = y; } //declaración de especialización template Punto<int>::Punto(int, int);
Test Unitario (main.cpp) #include “punto.h” void main(){ Punto<int> miPunto(10, 20); miPunto.print(); }
Otras consideraciones generales No es posible sobrecargar una plantilla de clase Parámetros múltiples posibles Parámetros de tipo base (parámetros de expresión) Pueden usarse tantos como se quiera y aparecer en cualquier lugar
de la declaración de la clase siempre que se puedan sustituir por una constante
template<typename T, int n> class Array { //… };
Ejercicio
EJERCICIO Implemente una clase genérica Array con parámetros tipo de datos y número de elementos (incluya métodos públicos push_back y print). Programe un caso de uso de prueba.
template<class T, int n> class Array { T x[n]; int nElem; public: Array(){nElem =0;} void push_back(int dato){ if(nElem <n){ x[nElem++]=dato; } } ostream& print(ostream & o =cout){ for (int i=0; i<nElem; i++){ o<<x[i]<<" "; } o<<endl; return o; } };
Especialización de funciones miembro
template <class type_t, int N> class ARRAY{ //… public: ostream& print(ostream& o=cout); }; template <> ostream& ARRAY<int, 3>::print(ostream& o){ for (int i=0; i<l.size(); i++){ o<<l[i]<<" "; } o<<endl; return o; }
Funciones miembro como plantilla
template <class type_t, int N> class ARRAY{ //… public: template<class U> ostream& print(ostream& o=cout); }; template <class type_t, int N> template<class U> ostream& ARRAY<type_t, N>::print(ostream& o){ for (int i=0; i<l.size(); i++){ o<<static_cast<U>(l[i])<<" "; } o<<endl; return o; }
Ejercicio: plantillas + herencia
EJERCICIO Generalice la implementación de la jerarquía vista para gestionar de manera cómoda diferentes implementaciones de la serie Fibonacci al caso de una función con 2 argumentos genéricos y que devuelva un tercero
<<interface>> IAlg
+ virtual ret_t solve(p1_t, p2_t)=0
Alg_B
+ ret_t solve(p1_t,p2_t)
Alg
-IAlg* +solve(p1_t, p2_t)
Alg_A
+ ret_t solve(p1_t,p2_t)
Clases de la librería estándar para colecciones (STL)
Cadenas de caracteres string
Colecciones (iterables) vector: elementos en posiciones consecutivas en memoria
acceso eficiente
list : lista de elementos enlazados borrado e inserción eficiente
set : colección de elementos distintos en estricto orden de precedencia
map: diccionario de elementos (asociación llavedato) Acceso a través de la llave Elementos ordenados por estricto orden de precedencia
String
#include <string>
typedef basic_string<char> string
void main(){ string str("un saludo"); str+= " a todos"; cout<<str<<endl; str.append(" los alumnos en clase"); cout<<str<<endl; str.insert(9," muy efusivo"); cout<<str<<endl; }
Sobrecarga del operador texto para la clase string como función independiente
Sobrecarga del operador suma para concatenar cadenas
Vector
#include <vector>
template <class T, class Allocator = allocator<T> > class vector
void main(){ vector<int> v(NUM_ELEM); cout<<"La capacidad actual es: "<<v.size(); for(int i=0; i<NUM_ELEM; i++){ v[i]=i; } cout<<endl; //añadir elementos al final (amplían la capacidad actual) v.push_back(100); for(int i=0; i<v.size(); i++){ cout<<v[i]<<" "; } cout<<endl; //borrado de elementos de la colección //último v.pop_back(); for(int i=0; i<v.size(); i++){ cout<<v[i]<<" "; } cout<<endl; //eliminar elemento en la posición i-ésima int i=5; v.erase(v.begin()+(i-1)); for(int i=0; i<v.size(); i++){ cout<<v[i]<<" "; } cout<<endl;
}
sobrecarga del operador [ ] (acceso aleatorio)
Ejercicio de algoritmia con la STL
Para un clase que encapsule un punto 2D Cree una colección de puntos Cree un vector (STL) de punteros a los elementos de dicha
colección Manipule dicho vector de diferentes formas
Ordene el vector por algún criterio Encuentre un punto en la colección Imprima el vector de diferentes maneras Borre el vector Etc…
Resumen I. Concepto de programación genérica II. Funciones con plantillas, especialización III. Clases con plantillas, especialización IV. Casos prácticos
¿PREGUNTAS?