Anuncios

Bienvenidos sean a este post, hoy veremos una particularidad de template.

Anuncios

En el post anterior vimos como es un template o plantilla, y como nos brinda una gran solucion para que una clase pueda manejar varios tipos de datos sin necesidad de sobrecargar cada metodo para poder manejarlos. Pero tambien tenemos una tecnica para poder tener metodos amigos en nuestras plantillas, estas pueden ser de tres tipos:

  • Clases y metodos amigos no perteneciente a la plantilla
  • Clases y metodos amigos de la plantilla general
  • Clases y metodos amigos de plantilla de tipo especificos
Anuncios

Vamos a analizar un ejemplo del primer tipo, para ello tomaremos el ejemplo que vimos en el post anterior y haremos las siguientes modificaciones:

#include <iostream>

const int tamanoPredet = 5;

class Animal
{
public:
        Animal(int peso): suPeso(peso) {}
        Animal(): suPeso(0) {}
        ~Animal() {}
        int GetPeso() const { return suPeso; }
        void mostrar() const { std::cout << suPeso; }
private:
        int suPeso;
};

template <class T>
class Arreglo
{
public:
        Arreglo(int tamano = tamanoPredet);
        Arreglo(const Arreglo & rhs);
        ~Arreglo() { delete [] pTipo; }
        Arreglo & operator= (const Arreglo &);
        T & operator[] (int pos) { return pTipo[ pos ]; }
        const T & operator[] (int pos) const { return pTipo[ pos ]; }
        int largo() const { return tamano; }
        friend void intruso(Arreglo< int >);
private:
        T * pTipo;
        int tamano;
};

void intruso(Arreglo < int > arreglo)
{
        std::cout << "\n ++++ Intruso ++++ \n";
        for(int i=0; i < arreglo.tamano; i++)
        {
                std::cout << "i: " << arreglo.pTipo[i] << std::endl;
        }
        std::cout << std::endl;
}

template <class T>
Arreglo< T >::Arreglo(int tam): tamano(tam)
{
        pTipo = new T[ tam ];
        for(int i=0; i < tam; i++)
                pTipo[i] = 0;
}

template <class T>
Arreglo< T >::Arreglo(const Arreglo & rhs)
{
        tamano = rhs.largo();
        pTipo = new T[tamano];
        for(int i=0; i < tamano; i++)
                pTipo[i] = rhs[i];
}

template <class T>
Arreglo< T > & Arreglo< T >::operator=(const Arreglo & rhs)
{
        if (this == &rhs)
                return *this;
        delete [] pTipo;
        tamano = rhs.largo();
        pTipo = new T[ tamano ];
        for(int i=0; i < tamano; i++)
                pTipo[i] = rhs[i];
        return *this;
}

int main()
{
        Arreglo< int > numerico(10);
        Arreglo< Animal > zoo;
        Animal* pAnimal;
        for(int i = 0; i < numerico.largo(); i++)
        {
                numerico[i] = i * 2;
                pAnimal = new Animal(i * 3);
                zoo[i] = *pAnimal;
        }
        for(int i = 0; i < numerico.largo(); i++)
        {
                std::cout << "zoo[" << i << "]:\t ";
                zoo[i].mostrar();
                std::cout  << std::endl;
        }
        std::cout << "Veamos al Arreglo <int> con el metodo intruso:\n";
        intruso(numerico);
        std::cout << "Listo." << std::endl;
        return 0;
}
Anuncios

Hablemos muy superficialmente del codigo, para mayor explicacion les recomiendo el post anterior. Primero tenemos una clase llamada Animal que la usaremos para aplicarla a la plantilla. Luego tenemos la plantilla con la clase Arreglo, que creara «arrays» de distintos tipos. Analicemos como es esta clase ahora:

template <class T>
class Arreglo
{
public:
        Arreglo(int tamano = tamanoPredet);
        Arreglo(const Arreglo & rhs);
        ~Arreglo() { delete [] pTipo; }
        Arreglo & operator= (const Arreglo &);
        T & operator[] (int pos) { return pTipo[ pos ]; }
        const T & operator[] (int pos) const { return pTipo[ pos ]; }
        int largo() const { return tamano; }
        friend void intruso(Arreglo< int >);
private:
        T * pTipo;
        int tamano;
};
Anuncios
Anuncios

Primero tendremos dos constructores que definiremos luego, un destructor para eliminar el puntero del tipo de la plantilla, esta en la parte privada, y una sobrecarga del operador =. Luego tenemos dos sobrecargas del operador [] para devolver los elementos de un array. Siendo el primero para uso normal y el segundo es lo mismo pero para constantes. Lo siguiente es un metodo para devolver el tamaño del array y por ultimo lo que vinimos a ver en este post. Mediante la palabra friend le informamos a la plantilla que el metodo asociado a este puede ser acceder a la misma. En este caso en particular solamente recibira elementos del tipo de la plantilla que manejan tipo int. Lo siguiente es definir a este metodo:

void intruso(Arreglo < int > arreglo)
{
        std::cout << "\n ++++ Intruso ++++ \n";
        for(int i=0; i < arreglo.tamano; i++)
        {
                std::cout << "i: " << arreglo.pTipo[i] << std::endl;
        }
        std::cout << std::endl;
}
Anuncios
Anuncios

Noten que no debemos asociarlo con la clase de la plantilla asi como tampoco lleva ninguna referencia a la clase sino que simplemente la definimos. Esta recibira el tipo de dato comentado anteriormente. Dentro de este usaremos un bucle para pasar por todos los elementos del «array» recibido. Observen que podemos acceder a la propiedad del tamaño del array, a pesar de ser privada, lo mismo sucede con el puntero del mismo. Luego tenemos la definicion de los metodos que solo eran prototipos de la clase. Siendo estos dos constructores para trabajar sin valores y con valores, y finalmente la sobrecarga del operador =.

Anuncios

En el main, si lo comparan con el post anterior veran que tenemos unas pocas diferencias. La primera es que ahora al momento de crear el array llamado numerico le definimos un tamaño. La siguiente es que en el bucle donde recuperaabamos la informacion de los dos arrays, ahora solamente lo hace con zoo. Para luego llamar a la nueva funcion/metodo para obtener los valores de numerico. Compilemos y veamos como es la salida:

$ ./plantilla
zoo[0]:  0
zoo[1]:  3
zoo[2]:  6
zoo[3]:  9
zoo[4]:  12
zoo[5]:  15
zoo[6]:  18
zoo[7]:  21
zoo[8]:  24
zoo[9]:  27
Veamos al Arreglo <int> con el metodo intruso:

 ++++ Intruso ++++
i: 0
i: 2
i: 4
i: 6
i: 8
i: 10
i: 12
i: 14
i: 16
i: 18

Listo.
$
Anuncios

No solamente funciono perfectamente sino que tambien cada vez que generemos un array para manejar tipos int de esta clase podran ser accedidos mediante intruso. Pasemos a ver el segundo tipo de metodo amigo, para ello tomaremos el codigo anterior y primero modificaremos al template de la siguiente manera:

template <class T>
class Arreglo
{
public:
        Arreglo(int tamano = tamanoPredet);
        Arreglo(const Arreglo & rhs);
        ~Arreglo() { delete [] pTipo; }
        Arreglo & operator= (const Arreglo &);
        T & operator[] (int pos) { return pTipo[ pos ]; }
        const T & operator[] (int pos) const { return pTipo[ pos ]; }
        int largo() const { return tamano; }
        friend std::ostream & operator<< (std::ostream & salida,
                                        Arreglo< T > & arreglo) {
                for(int i=0; i < arreglo.largo(); i++)
                        salida << "[" << i << "]" << arreglo[i] << "\n";
                return salida;
        }
private:
        T * pTipo;
        int tamano;
};
Anuncios
Anuncios

Esta clase es muy similar al anterior, y la unica modificacion fue la eliminacion del prototipo del metodo amigo intruso y agregamos un nuevo metodo amigo. Este es una sobrecarga del operador <<. Este es del tipo ostream y tendra dos argumentos. El primero sera para indicar por donde la enviaremos y el otro sera para el objeto que manejaremos, en este caso uno de la clase Arreglo pero de cualquier tipo (a diferencia del anterior). En el bloque mostraremos el contenido de este objeto mediante un bucle. Es decir, a partir de ahora cualquier objeto de la clase Arreglo sera manipulado por esta sobrecarga. Les recomiendo eliminar la definicion de la funcion intruso porque ya no solo no es amiga del template sino que devolvera un error por esto al momento de compilarlo. Nuestro siguiente paso sera ir al main y modificarlo de la siguiente manera:

int main()
{
        bool detener = false;
        int pos, valor;
        Arreglo< int > numerico(10);
        while(!detener)
        {
                std::cout << "Escriba la posicion [0-9]";
                std::cout << " y un valor. (-1 para detener): ";
                std::cin >> pos >> valor;
                if (pos < 0)
                        break;
                if (pos > 9)
                {
                        std::cout << "+++ Use valores entre 0 y 9+++\n";
                        continue;
                }
                numerico[pos] = valor;
        }
        std::cout << "Aca estan todos los valores" << std::endl;
        std::cout << numerico;

        return 0;
}
Anuncios
Anuncios

Primero definiremos una variable para controlar al bucle que usaremos, luego declaramos dos variables y por ultimo, un objeto de tipo Arreglo. En este caso maneja tipo int. El bucle se hara mientras detener sea falso, como es actualmente. En el bloque mostraremos un mensaje indicando como deben ingresar los valores para nuestro «array». En este caso siempre sera primero la posicion y luego el valor a asignar en esa posicion. Y para salir debemos pasar el valor de -1 en la posicion. Tendremos dos condicionales, siendo el primero para salir del bucle mediante el break y el segundo para cuando pasemos una posicion por fuera del rango permitido y aqui usaremos a continue para saltar esta pasada del bucle pero sin salir. Si no se cumple ninguna condicion procede a asignar el valor en la posicion informda. Para finalmente mostrar todos los valores ingresados mediante la sobrecarga nueva que agregamos. Compilemos y veamos como trabaja:

$ ./plantilla
Escriba la posicion [0-9] y un valor. (-1 para detener): 8 100
Escriba la posicion [0-9] y un valor. (-1 para detener): 1 10
Escriba la posicion [0-9] y un valor. (-1 para detener): 6 25
Escriba la posicion [0-9] y un valor. (-1 para detener): 4 600
Escriba la posicion [0-9] y un valor. (-1 para detener): 9 1
Escriba la posicion [0-9] y un valor. (-1 para detener): -1 0
Aca estan todos los valores
[0]0
[1]10
[2]0
[3]0
[4]600
[5]0
[6]25
[7]0
[8]100
[9]1
$
Anuncios

Ingresamos algunas posiciones con algunos valores y lo finalizamos como corresponde. Observen que nos devolvio todas las posiciones del array pero en las posiciones donde no pasamos un valor le asigno el valor de 0. Esto es asi porque es la conducta predeterminada del constructor de la clase. Con esto tenemos los dos primeros tipos de template y friend para ver como se manipula la informacion entre ellas.

Anuncios

En resumen, hoy hemos visto friend, que es, para que sirve, como se utiliza, y un par de ejemplos practicos para ver como se pone en practica. Espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Donatión

It’s for site maintenance, thanks!

$1.50