Practica3

= = =Practica 3: Plantillas=

**1. Introducción**
Las plantillas son una de las herramientas más potentes de C++. Con ellas, podemos definir clases y funciones cuyas variables no están definidas en tiempo de diseño, sino que se definen en tiempo de compilación. Veamos un ejemplo de función genérica para intercambiar valores:

code format="cpp" using namespace std;
 * 1) include

template void intercambio(Type &x,Type & y) { Type aux=x; x=y; y=aux; }

int main {   int ia=10,ib=15; intercambio(ia,ib); cout<. Esta es la forma de indicar al compilador de C++ que vamos a generar una clase template. Además, le estamos diciendo que vamos a definir un tipo genérico llamado Type. El elemento Type, es una variable genérica que no se define en tiempo de diseño, sino que se define en tiempo de compilación. ¿Esto que significa? Que antes de llamar a la función no sabemos qué será Type, podra ser un int, double, float o lo que sea, pero aun no está definido. Después, vemos como en el main, llamamos a la misma función dos veces con parámetros distintos. Lo bueno de esto es que la función swap sólo la hemos definido una vez, pero podemos usarla con distintos tipos de datos.

Si queremos definir para una función mas de un tipo genérico es muy simple:

code format="cpp" template void printValues(Type1 a,Type2 b) { cout<<a<<" "<<b<<endl; }

int main { float a=0,b=10; int i=1,i2=130; printValues(a,b); printValues(a,i); printValues(i2,i); printValues(i2,b); }

code

2. Clases genéricas
El concepto de plantilla se extiende a las clases. Esto se claramente con el ejemplo de los números. Para los distintos números Integer, Float y Double, hemos tenido que reimplementar la misma funcionalidad hasta tres veces haciendo cambios mínimos. Con C++, es posible evitar esto definiendo una plantilla. Vamos a crear la clase Number para demostrar esto. Creamos number.h

code format="cpp"
 * 1) ifndef _Number_H_
 * 2) define _Number_H_

namespace values { /**\brief This class represents a generic number template class Number { public: /**Empty constructor */   Number  {_value=0;} /**Parametrized constructor */   Number(Type v)  {_value=v;} /**Sets the current value */   void setValue(Type v){ _value=v;} /**Returns the current value */   Type getValue(void){ return _value;} private: Type _value; }; }; code
 * 1) endif

Obsérvese la sintaxis. Vemos que hay cosas nuevas aquí. Lo primero es el uso de la palabra clave template. Esto es para decir que estamos creando una función genérica. Pero después, vemos que hemos incluido el código directamente en la declaración. Esto es algo nuevo que veremos dentro de un momento en mayor profundidad. Por ahora, decir solamente que también funciona :)

Ahora, el main.cpp

code format="cpp" using namespace std; using namespace values;
 * 1) include
 * 2) include 

int main { Number MyInteger(10); Number MyFloat(11.1); Number MyDouble(111.1); Number MyLInt(1111); Number MyUInt(1111); }

code

Al declarar en main.cpp Number MyInteger(10) creamos un objeto de la clase Number, donde Type se sustituye por int. Más abajo, lo sustituimos por float. Y así tanto como nos de la gana. El caso es que para MyInteger, Type es int pero par MyFloat es float. Es útil ¿verdad?

El caso, es que las clases plantillas tienes ciertas particularidades a la hora de codificarlas. La más importante es que **todo el código debe ir en el .h y nada en el .cpp**. Esto se debe a que la clase no se compila hasta que no se instancia un objeto de ella.

**3. Programación en línea**
Aun quedan cosas por aclarar. Como podemos ver no hay .cpp. Además, he escrito el código en el .h. Bueno, a esto se le llama //programación en línea.// La programación en linea consiste en realizar la implementación en el .h y no en un .cpp, y se usa la palabra clave **inline**. Ejemplo rápido:

code format="cpp"
 * 1) ifndef _FLOAT_H_
 * 2) define _FLOAT_H_

namespace values { /**\brief This class represents a generic number class Float {   public: /**   */    inline Float; /**   */    inline float getValue; private: float _fvalue; };

Float::Float { _fvalue=0; }

float Float::getValue { return _fvalue; }

} code
 * 1) endif

En este caso, indicamos con la palabra **inline**, que la implementación de la función estará más abajo en el fichero. ¿Para qué sirve esto? Permite realizar optimizaciones de código. Cuando se compila con opción de optimización, el compilador puede sustituir la llamada a la función por el código mismo de la función. Claramente, esto supone una mejora en el rendimiento ya que nos evitamos copiar datos, la pila, la llamada..., en fin, todo el follón que implica llamar a una función. Sin embargo, he de advertir que el compilador sólo hace esto en caso de que la función sea realmente pequeña (como un return o una asignación).

Bueno, pues ahora, tenemos también la programación en linea implicita. En este caso, no pongo la palabra inline, sino que directamente abro paréntesis y escribo. O sea, la clase anterior sería equivalente a code format="cpp"
 * 1) ifndef _Number_H_
 * 2) define _Number_H_

namespace values { /**\brief This class represents a generic number class Float {   public: /**   */    Float{_fvalue=0;} /**   */    float getValue{return _fvalue;} private: float _fvalue; };

code
 * 1) endif

Rápido verdad. El inconveniente que tiene este método, es que la clase no se compila en un .cpp, sino que se compila cada vez que yo hago un #include . Es decir, que si tengo este include en 14 ficheros, estoy recompilando la clase 14 veces. Por tanto, es más lento.

Otra desventaja. Como no compilamos la clase hasta que la usamos, no podemos estar seguros hasta que la usamos de que nuestro código compila correctamente. Además, el compilador solo compilará aquellas funciones que se usen. Por tanto, puedo tener funciones mal escritas que no sabré si compilan o no hasta que no las llame en mi programa.

Ahora por tanto podremos comprender un poco mas el ejemplo de number.h. La clase no es cada vez que se crea un objeto con un Type distinto. Internamente, el compilador crea una instancia de la clase cada para cada Type. Es algo más lento en la compilación, pero es muy útil y eficiente.

**4. Ampliando la clase**
code format="cpp"
 * Ejercicio:** Bueno, ahora que ya sabemos como funciona la clase, vamos a añadirle todos los elementos que añadimos a las clases anteriores: operadores y todos los constructores.
 * 1) ifndef _Number_H_
 * 2) define _Number_H_

namespace values { /**\brief This class represents a generic number template class Number { public: /**Empty constructor */   Number  {_value=0;} /**Parametrized constructor */   Number(Type v)  {_value=v;} /**Copy constructor */   Number(const Number &N)  {_value=N._value;} /**Sets the current value */   void setValue(Type v){ _value=v;} /**Returns the current value */   inline Type getValue(void);

/**Returns the current value */   Number  & operator=(const Number &N){ _value=N._value;return *this;}

.   .    . Rellenad vosotros!!!!!!!!!!! .   . private: Type _value; };

//That is the way you have to declare a function when it is a template class. //It is a bit more tricky template Type Number::getValue(void){ return _value; } };
 * 1) endif

code Para probar vuestra clase, usad el siguiente programa de prueba pruebanumber.cpp code format="cpp" using namespace std; using namespace values;
 * 1) include 
 * 2) include
 * 3) include

int main { Number Int1(10);

Number Float1=20; Number Int2=Int1; Number Int3(Int2);

assert( Int1==Int2 && Int2==Int3); Int2=20; Int3=Int1*Int2; assert(Int3>=Int2); assert(Int2Int2); Int3=Int2/Int1; assert(Int3==2);

Int3=Int1*20; assert(Int3>=Int2); assert(Int2Int2); Int3=Int2/Int1; assert(Int3==2);

Int1++; Int1--; assert(Int1==10); cout<<"Perfect"< aux; aux._value=_value+N._value;return aux;}

code

Esto es porque al estar dentro de la clase, el compilador ya sabe el tipo y no es necesario repetirlo. Sin embargo, el código anterior también es correcto.

5.1 Suma genérica
Ahora que sabemos crear funciones plantilla, quiero que creeís una función generica que tome como argumentos: a) un puntero a un vector genérico b) el número de elementos del vector

La función debe retornar el valor de sumar todos los elementos del vector.

Ayuda: code format="cpp" template T sum(T *pointer,int nElements){ }

code

Debéis crear la función main para probar vuestra función.


 * IMPORANTE:** En vuestro main, debereis crear el vector con el que vais a realizar la prueba usando memoria dinámica de C.

5.2 Ordenación genérica
Debeis crear una función que realice una ordenación generica de un vector. La función recibe un puntero a vector y el número de elementos, y al finalizar la ejecución, el vector debe quedar ordenado en forma ascendente.

5.3 La clase Pair
Debereis crear la clase Pair en la clase utils. La clase pareja almacena una pareja de valores de cualquier tipo. Al primer elemento se le llama "primero" y al segundo "segundo". Basta con que hagais que funcione el siguiente código

code format="cpp" using namespace std; using namespace utils; template void print(Pair<T1,T2> &P) { cout<<P.primero<<" "<<P.segundo<<endl; } int main { Pair<int,int> P1(10,30); Pair<float,double> P2(40,60); Pair<int,int> P3(P1); print(P1); print(P2); print(P3); }
 * 1) include <pair.h>
 * 2) include

code

5.4 Repasar memoria dinámica en C
Para la semana que viene, debereis haber repasado como se utiliza la memoria dinámica en C de los apuntes de primero.