Plantillas en C++

Iniciado por Dharok, Abril 21, 2010, 04:45:13 PM

Tema anterior - Siguiente tema

0 Miembros y 2 Visitantes están viendo este tema.

Abril 21, 2010, 04:45:13 PM Ultima modificación: Febrero 08, 2014, 05:21:20 PM por Expermicid
Plantillas en C++

Las plantillas nos permiten especificar, con un solo segmento de código, un rango completo de funciones relacionadas (sobrecargadas), llamadas funciones de plantilla, o un rango completo de clases relacionadas, llamadas clases de plantilla.

Podríamos escribir una sola plantilla de función para una función de ordenamiento de arreglos y luego hacer que C++ generara automáticamente funciones de plantilla separadas que ordenaran un arreglo de int, un arreglo de float, un arreglo de string, etc.

Podríamos escribir una sola plantilla de clase de pila y luego hacer que C++ generara automáticamente clases de plantillas separadas, tales como una clase de pila de int, una clase de pila de float, una clase de pila de string, etc.

Hay que observar la diferencia entre las plantillas de función y las funciones de plantilla: las plantillas de función y las plantillas de clase son como plantillas con las cuales trazamos formas, y las funciones de plantilla y las clases de plantilla son como los trazos separados, que tienen la misma forma pero pueden trazarse en colores diferentes, por ejemplo.

Plantillas de función

Las funciones sobrecargadas se utilizan normalmente para realizar operaciones similares sobre diferentes tipos de datos. Si las operaciones son idénticas para cada tipo, esto puede realizarse en forma más compacta y conveniente mediante el uso de plantillas de función. Basándose en los tipos de argumento que se proporcionan en las llamadas a esa función, el compilador genera automáticamente funciones de código objeto separadas para manejar adecuadamente cada tipo de llamada. En C esta tarea se puede realizar mediante macros creadas con la directiva de preprocesador #define.

Sin embargo las macros presentan la posibilidad de serios efectos secundarios y no permiten que el compilador realice revisión de tipo. Las plantillas de función proporcionan una solución compacta similar a la de las macros, pero permiten una revisión de tipo completa. Todas las definiciones de plantillas de función comienzan con la palabra clave témplate, seguida de una lista de parámetros formales para dicha plantilla encerrados entre paréntesis angulares (< y >), cada parámetro formal que representa un tipo debe estar precedido por la palabra clave class, como en:
Código: php
template <class T>
template <class Element Type>
template <class BorderType, class FillType>

Los parámetros formales de una definición de plantilla se utilizan (como sucedería con los argumentos de tipos integrados o de tipos definidos por el usuario) para especificar los tipos de argumentos de la función, para especificar el tipo de devolución de la función y para declarar variables en el interior de la función.

Para definir una plantillas de clase, se puede usar el siguiente formato:
Código: php
template <class T>
class Nombre { . . .uso del parámetro T en funciones o datos miembro.. }


Primer programa


El siguiente programa hace uso de plantillas para determinar el mínimo y máximo valor de un arreglo de elementos dado.
La primera función recibe tiene como parámetros un puntero al tipo de elemento dado y el número de elementos y retorna el menor de los elementos que se encuentran en el arreglo.
La segunda función recibe los mismos parámetros y retorna el mayor de los elementos presentes en el arreglo.
Finalmente en la función main, se hace una prueba de estas funciones, con arreglos de enteros y flotantes.
Código: cpp
#include <iostream.h> 
#include <conio.h>

template <class T>
T minimo (T * Array, int num_elemen)
{
   T min= Array [0] ;

   for (int i=1; i< num_elemen; i++)
        if( Array[ i ] < min)
            min = Array [ i ];

   return min;
} ;

template <class T>
T maximo (T * Array, int num_elemen)
{
   T max= Array [0];

   for (int i=1; i< num_elemen; i++)
        if( Array[ i ] > max)
            max = Array [ i ];

   return max;
};

void main(void)
{
   int ArrayInt [3] = { 2, 8, 6};
   float ArrayFloat [3] = { 12.1, 8.7, 5.6 };
   int i;
   
   cout<<"Arreglo de enteros: ";
   for (i=0; i<3; i++)
         cout << ArrayInt[ i ] << " ";

   cout << endl << "Numero minimo: " << minimo (ArrayInt, 3) << endl
         <<"Numero maximo: " << maximo (ArrayInt, 3) << endl << endl;

   cout << "Arreglo de flotantes: ";
   for (i=0; i<3; i++)
        cout << ArrayFloat[ i ] << " ";
        cout << endl <<"Numero minimo: " << minimo (ArrayFloat, 3) << endl
               <<"Numero maximo: " << maximo (ArrayFloat, 3);

   getch();
}

Segundo programa

En este programa se hace uso de plantillas, para elaborar una función que permita ordenar los elementos de un arreglo.
Esta función recibe tiene como parámetros, un puntero al tipo de elemento dado, y dos enteros que indican los índices del primero y último elemento.
Aquí se hace uso del algoritmo OrdenarShell para llevar a cabo la tarea. En la función principal se prueba esta plantilla con arreglos de enteros y flotantes.
Código: cpp
#include <iostream.h>
#include <conio.h>

template <class T>
void Ordenar ( T * a, int st, int fn)
{
   int i, j , k;
   T item_a_ordenar;

   k = 1 ;

   do
   {
       k= 3 * k + 1;
    } while (k < fn - st + 1) ;

   do
   {
       k /= 3 ;

       for (i= st+k; i<=fn; i++)
       {
             item_a_ordenar = a [ i ] ;
             j = i ;

            while (item_a_ordenar < a [ j-k])
            {
               a [ j] =a [ j-k] ;
               j -=k;

               if(j < st+k)
                 break;
            }

            a [j]=item_a_ordenar;
        }
   } while(k > 1);
}

void main(void)
{
    int Arraylnt[3] = { 2, 3, 1 } ;
    float ArrayFloat[3] = {25.0, 3.0, 45.2 } ;
    int i ;

    cout << "Enteros: " << endl
          << "Arreglo original: ";

    for (i=0; i<3; i++)
          cout << Arraylnt[ i ] << " ";

    Ordenar(Arraylnt, 0, 2);
    cout << endl << "Luego de ordenar: ";

    for (i=0; i<3; i++)
          cout << Arraylnt[ i ] << " ";


    cout << endl << endl
          << "Flotantes: " << endl
          << "Arreglo original: ";

    for (i=0; i<3; i++)
          cout << ArrayFloat[ i ] << " ";

    Ordenar(ArrayFloat, 0, 2);
    cout << endl << "Luego de ordenar: ";

    for (i=0; i<3; i++)
          cout << ArrayFloat[ i ] << " ";


    getch() ;
}

Tercer programa

En este programa se implementa, mediante el uso de plantillas la clase NuevaPila, que consiste en una Pila, en la que se pueden llevar a cabo las operaciones como insertar y eliminar datos de la misma, mostrar en pantalla los datos de la pila.

Esta es una pila estática, con un número predefinido de 10 elementos.
En al función principal, se usa una pila de enteros, flotantes y
caracteres para poder llevar a cabo una prueba de la plantilla creada.
Código: cpp
#include <iostream.h>
#include <conio.h>

enum estado_pila { OK, LLENA, VACIA };

template <class T>
class NuevaPila
{
   int tamanyo;
   T *tabla;
   int cima;
   estado_pila estado;

   public:
             NuevaPila(int =10);
             ~NuevaPila() { delete [] tabla; }
             void meter (T);
             T sacar ();
             void visualizar ();
             int num_elementos ();
             int leer_tamanyo () { return tamanyo; }
}

template <class T>
NuevaPila <T> :: NuevaPila (int tam)
{
   tamanyo=tam;
   tabla= new T [tamanyo] ;
   cima=0;
   estado=VACIA;
}

template <class T>
void NuevaPila <T> :: meter (T elemento)
{
   if( estado!=LLENA)
       tabla [cima++]=elemento;
    else cout << "*** Pila llena ***";

    if(cima>=tamanyo)
       estado=LLENA;
    else
       estado=OK;
}

template <class T>
T NuevaPila <T> :: sacar ()
{
   T elemento=0;

   if(estado!=VACIA)
      elemento=tabla[--cima];
   else cout<<"*** Pila vac¡a ***";

   if(cima<=0)
      estado=VACIA;
   else
      estado=OK;

   return elemento;
}

template <class T>
void NuevaPila <T> :: visualizar ()
{
   for (int i=cima-1; i >=0; i--)
        cout << tabla[ i ] <<" ";
}

template <class T>
int NuevaPila <T> :: num_elementos ()
{
   return cima;
}

void main()
{
   cout << "Probando pila de enteros";

   NuevaPila <int> s1 (5);
   s1.meter(5);
   s1.meter(10);
   s1.meter(15);

   cout << endl << "Numero de elementos: " << s1.num_elementos() << endl <<"Pila: "; 
   s1.visualizar();

   cout << endl << "Sacando elementos : "
          << s1.sacar() <<" " << s1.sacar() << " " << s1.sacar() << endl;
   s1.sacar();

   cout << endl << endl << "Probando pila de flotantes";
   NuevaPila <float> s2 (5);
   s2.meter(7.5);
   s2.meter(10.2);
   s2.meter(15.3);

   cout << endl << "Numero de elementos: " << s2.num_elementos() << endl
         <<"Pila: ";
   s2.visualizar();
   cout << endl << "Sacando elementos : "
           << s2.sacar() << " " << s2.sacar() << " " << s2.sacar() << endl;
   s2.sacar ();

   cout << endl << endl << "Probando pila de caracteres";
   NuevaPila <char> s3 (5);
   s3.meter('m');
   s3.meter('p');
   s3.meter('s');

   cout << endl << "Numero de elementos: " << s3.num_elementos() << endl <<"Pila: ";
    s3.visualizar();
    cout << endl << "Sacando elementos : "
          << s3.sacar () << " " << s3.sacar() << " " << s3.sacar() << endl;
   s3.sacar();


   getch();
}

Cuarto ejemplo

Mediante el uso de plantillas cree una clase Cola, en la que se puedan llevar a cabo operaciones como: encolar, decolar e imprimir los datos miembro. Realice una función controladora para probar el uso de esta clase.
La clase NodoCola, tiene como amiga a la clase Cola.
Se presenta además una función para las opciones del usuario, y que se encarga de realizar las llamadas a las funciones de las clases. Esta
función es llamada desde main.
En la función main se ha usado como ejemplo una cola de enteros, aunque también se pudo haber usado otro tipo de datos como: char, double, y otros.
Código: cpp
#include <iostream.h>
#include <conio.h>
#include <assert.h>

template <class TIPONODO>
class Cola;

///////////////////////////////////
// definicion clase NodoCola /////
//////////////////////////////////

template <class TIPONODO>
class NodoCola {
      TIPONODO dato;                         // dato
      NodoCola <TIPONODO> * sig;             // puntero siguiente nodo
      public:
         NodoCola (const TIPONODO &);    // constructor
         TIPONODO getDato() const;       // devuelve dato del nodo

      friend class Cola <TIPONODO>;          // hace que Cola sea friend
};

// constructor
template <class TIPONODO>
NodoCola <TIPONODO> :: NodoCola (const TIPONODO & info)
     : dato(info), sig (0) { }

// devuelve una copia del dato que esta en el nodo
template <class TIPONODO>
TIPONODO NodoCola <TIPONODO> :: getDato() const {
     return dato;
}

// definicion enumeracion //
enum bool { false, true };

//////////////////////////////////
// definicion clase Cola /////////
//////////////////////////////////

template <class TIPONODO>
class Cola {
      NodoCola <TIPONODO> * primero;          // puntero al primer nodo
      NodoCola <TIPONODO> * ultimo;           // puntero al ultimo nodo
      public:
         Cola ();                         // constructor
         ~Cola();                         // destructor
         void encolar (const TIPONODO &); // permite insertar nodo
         bool decolar (TIPONODO &);       // permite eliminar nodo
         bool estaVacia() const;          // verifica si la cola esta vacia
         void imprimir() const;              // imprime datos de la cola

      // funci¢n de utileria para asignar un nuevo nodo
      NodoCola <TIPONODO> * getNuevoNodo (const TIPONODO &);
};

// constructor predeterminado
template <class TIPONODO>
Cola <TIPONODO> :: Cola (): primero(0), ultimo(0) { }

// destructor
template <class TIPONODO>
Cola <TIPONODO> :: ~Cola () {
     if( !estaVacia() ) {         // ingresa si la cola no esta vacia
    cout<<"Eliminando nodos ..." << endl;

    NodoCola <TIPONODO> * actual = primero, *temporal;

    while (actual != 0) {     // borra los nodos restantes
           temporal = actual;
           cout<< temporal->dato << endl;
           actual = actual->sig;
           delete temporal;
    }
     }
     cout << "Todos los nodos han sido eliminados" << endl << endl;
}

// inserta un nodo
template <class TIPONODO>
void Cola <TIPONODO> :: encolar (const TIPONODO & valor) {
     NodoCola <TIPONODO> * nuevo = getNuevoNodo (valor);

     if ( estaVacia() )                // si la cola esta vacia
    primero=ultimo=nuevo;

     else {                           // si la cola no esta vacia
       ultimo->sig=nuevo;
       ultimo=nuevo;
     }
}

// elimina un nodo
template <class TIPONODO>
bool Cola <TIPONODO> :: decolar (TIPONODO & valor) {
     if( estaVacia() )                // la cola esta vacia
     return false;                // eliminacion no satisfactoria

     NodoCola <TIPONODO> *temporal = primero;

     if(primero==ultimo)
    primero=ultimo=0;

     else
      primero=temporal->sig;

     valor=temporal->dato;            // dato que se esta eliminando
     delete temporal;
     return true;                     // eliminacion satisfactoria
}

// verifica si la cola esta vacia
template <class TIPONODO>
bool Cola <TIPONODO> :: estaVacia () const {
     if (primero==0) return true;
     return false;
}

// imprime el contenido de la cola
template <class TIPONODO>
void Cola <TIPONODO> :: imprimir() const {
     if ( estaVacia() ) {
     cout<<"La lista esta vacia" << endl << endl;
     return;
     }

     NodoCola <TIPONODO> *actual = primero;

     cout<<"La cola es: ";

     while (actual!=0) {
        cout<< actual->dato <<" ";
        actual=actual->sig;
     }

     cout << endl << endl;
}

// funcion de utileria: devuelve un apuntador a un nodo recientemente asignado
template <class TIPONODO>
NodoCola <TIPONODO> * Cola <TIPONODO> :: getNuevoNodo (const TIPONODO & valor) {
     NodoCola <TIPONODO> * nuevo = new NodoCola <TIPONODO> (valor);
     assert(nuevo!=0);
     return nuevo;
}

/////////////////////////////////////////////////////////////////////////////

// funcion que prueba una cola
template <class T>
void probarCola ( Cola <T> & ObjetoCola) {

     T valor;
     int opcion;

     for (;;){
           cout<<endl<<"(1) Insertar, (2) Eliminar, (3) Imprimir, (4) Salir"<<endl;
           cout<<"Seleccion: ";
           cin>>opcion;

           switch( opcion ){
              case 1:
                 cout<<"Ingrese dato: ";
                 cin>> valor;
                 ObjetoCola.encolar(valor);
                 break;

              case 2:
                 if(ObjetoCola.decolar(valor))
                cout<<"Dato " << valor <<" eliminado" << endl << endl;

                 else cout<<"No puede eliminar : la cola esta vacia" << endl << endl;

                 break;

              case 3:
                 ObjetoCola.imprimir();
                 break;
           }
           if(opcion==4) break;
     }
}

// funcion principal
void main(){
     clrscr();
     Cola <int> ColaEnteros;
     probarCola (ColaEnteros);

}
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta