Anuncios

Bienvenidos sean a este post, hoy veremos la fusion de dos conceptos.

Anuncios

Sabemos que una funcion nos permite ejecutar una serie de instrucciones todas las veces que necesitemos. Por otro lado, los punteros son una representacion simbolica de una direccion de memoria y nos permite realizar llamados por referencia. Pero como indica el titulo podemos crear punteros para hacer llamado a funciones. Si bien no parece de gran utilidad, este tiene un uso muy interesante. Primero veamos como es su sintaxis:

tipo_dato (*nombre_puntero) (argumentos);
Anuncios

Al igual que las funciones primero debemos pasar el tipo de dato que devolvera esta funcion. Entre parentesis pasaremos el nombre del puntero, el cual usaremos para asignarle la funcion que apuntaremos, y los argumentos que debemos pasar a la funcion. Tal como en las funciones de manera opcional podemos pasar argumentos. Esta es la forma de declararlo pero luego se utiliza de otra manera. Pero para ello analizaremos el siguiente ejemplo:

#include <iostream>

void MostrarValores(int & v1, int & v2) {
        std::cout << "vUno: " << v1 << " | vDos: " << v2 << std::endl;
}

void Cuadrado(int & rX, int & rY) {
        rX *= rX;
        rY *= rY;
}

void Intercambiar(int & rX, int & rY) {
        int tmp;
        tmp = rX;
        rX = rY;
        rY = tmp;
}

void CambiarValores(int & rVuno, int & rVdos) {
        std::cout << "Nuevo valor para vUno: ";
        std::cin >> rVuno;
        std::cout << "Nuevo valor para vDos: ";
        std::cin >> rVdos;
}

int main()
{
        bool fSalir = false;
        int vUno = 4, vDos = 5;
        int opcion;
        while (fSalir == false) {
                std::cout << "(0) salir (1) cuadrado (2) intercambiar ";
                std::cout << "(3) Cambiar valores: ";
                std::cin >> opcion;
                switch(opcion)
                {
                        case 1:
                                MostrarValores(vUno, vDos);
                                Cuadrado(vUno, vDos);
                                MostrarValores(vUno, vDos);
                                break;
                        case 2:
                                MostrarValores(vUno, vDos);
                                Intercambiar(vUno, vDos);
                                MostrarValores(vUno, vDos);
                                break;
                        case 3:
                                CambiarValores(vUno, vDos);
                                MostrarValores(vUno, vDos);
                                break;
                        default:
                                fSalir = true;
                                break;
                }
        }

        return 0;
}
Anuncios
Anuncios

Primero definiremos cuatro funciones para manejar informacion en el main. La primera se encarga de mostrar los valores recibidos con un texto a cual pertenecen. La siguiente funcion calcula el cuadrado de los dos valores recibidos pero recibe los valores por referencia. Por lo tanto, al efectuar el calculo modificara los valores en las variables pasadas como argumentos. La funcion que sigue sera para intercambiar los valores entre las dos variables recibidas. Al igual que en el caso anterior volvemos a recibirlos por referencias. Primero declaramos una variable que sera la temporal. A esta le asignamos al primer argumento, rX, y luego a esta le asignamos el segundo argumento (rY). Para finalmente asignar la variable temporal a rY, provocando el intercambio de valores. La ultima funcion es para asignar nuevos valores a las variables recibidas. Seguimos recibiendolas como referencias para manejar las originales, y luego mediante cin le asignaremos un nuevo valor a cada una.

Anuncios
Anuncios

Con nuestras funciones definidas pasemos al main. En este definiremos tres variables. La primera es para controlar a nuestro bucle. Las siguientes dos seran los valores que usaremos para manipular mediante las funciones anteriores y por ultimo declaramos una variable que servira para recibir la opcion ingresada por el usuario. Lo siguiente es un bucle while para solicitar el ingreso de una opcion y llamar a alguna de las funciones anteriores. Para ello mostraremos el valor que deben ingresar para ejecutarla. Este valor sera recibido por un switch donde habra un case para cada opcion. Observen que son todos muy similares, salvo para la opcion 3, donde siempre mostraremos primero los valores de las variables, ejecutamos las accion correspondiente y volvamos a mostrar los valores para ver los cambios realizados. En el caso de la opcion 3 no mostramos los valores iniciales sino que pedimos el ingreso de los nuevos y los mostramos. El default lo usaremos para la opcion 0, salir, y la unica accion que hara es establecer a fSalir como true para que la condicion del bucle no se cumpla y termine con el programa. Compilemos y veamos como trabaja:

$ ./apuntar
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 1
vUno: 4 | vDos: 5
vUno: 16 | vDos: 25
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 2
vUno: 16 | vDos: 25
vUno: 25 | vDos: 16
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 3
Nuevo valor para vUno: 100
Nuevo valor para vDos: 110
vUno: 100 | vDos: 110
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 2
vUno: 100 | vDos: 110
vUno: 110 | vDos: 100
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 0
$
Anuncios

Observen como a medida que ingresamos las opciones se van realizando sus respectivas acciones y se modifican los valores de las variables. Este codigo funciona correctamente pero tenemos una repeticion a unas llamadas de manera innecesaria. Vamos a remediar y para ello volveremos al codigo anterior y modificaremos el main de la siguiente manera:

int main()
{
        void (*pFunc)(int &, int &);
        bool fSalir = false;
        int vUno = 4, vDos = 5;
        int opcion;
        while (fSalir == false) {
                std::cout << "(0) salir (1) cuadrado (2) intercambiar ";
                std::cout << "(3) Cambiar valores: ";
                std::cin >> opcion;
                switch(opcion)
                {
                        case 1: pFunc = Cuadrado; break;
                        case 2: pFunc = Intercambiar; break;
                        case 3: pFunc = CambiarValores; break;
                        default:
                                fSalir = true;
                                continue;
                                break;
                }
                MostrarValores(vUno, vDos);
                pFunc(vUno, vDos);
                MostrarValores(vUno, vDos);
        }

        return 0;
}
Anuncios
Anuncios

Primero agregaremos la declaracion de un puntero de funcion con el nombre de pFunc y este recibira dos argumentos. El resto del codigo sigue siendo el mismo al anterior pero la siguiente modificacion sera en el switch, mas exactamente en los case. En lugar de llamar a cada funcion como hacia antes, ahora le assigna la funcion al nuevo puntero. Esto mantendra los llamados pertinentes y en el default seguimos cambiando el valor de fSalir para terminar el programa pero ahora agregamos un continue. Esta palabra clave es similar a break porque tambien nos saca del bucle pero no lo termina sino que simplemente omite todo codigo siguiente y vuelve al inicio del bucle pero sin terminarlo, a diferencia de break, y por ultimo haremos los tres llamados anteriores. Donde primero mostraremos los valores, ejecutamos la funcion para modificarlos y volvemos a mostrarlos. La diferencia radica en que llamamos al puntero y este llamara a la funcion que le asignamos. Por lo tanto, no necesitamos tener una linea para cada llamada sino simplemente modificando a donde debe apuntar el puntero. Compilemos y veamos como trabaja ahora:

$ ./apuntar
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 1
vUno: 4 | vDos: 5
vUno: 16 | vDos: 25
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 3
vUno: 16 | vDos: 25
Nuevo valor para vUno: 100
Nuevo valor para vDos: 150
vUno: 100 | vDos: 150
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 2
vUno: 100 | vDos: 150
vUno: 150 | vDos: 100
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 0
$
Anuncios

Sigue trabajando de la misma forma pero ahora tenemos un codigo mas simple y un poco mas practico a la hora de manejar los llamados a las funciones y un mejor uso de la memoria. Aprendan bien esto porque es una practica muy habitual para realizar distintos llamados desde un mismo punto. Antes de finalizar veamos como es su uso, primero siempre la declaramos o establecemos un prototipo:

void (*pFunc)(int &, int &);
Anuncios

En este caso usamos a void pero ahi debe ir el tipo que devolvera la funcion que apuntaremos. Lo mismo para los argumentos. Lo siguiente es apuntar a la funcion y para ello la asignaremos:

pFunc = Cuadrado
Anuncios

Observen que solo pasamos el nombre porque es la unica referencia que necesita para saber a donde apuntar. Recuerden que todo esta en memoria: variables, clases, constantes, y obviamente las funciones, y cada uno tiene su direccion de memoria y es adonde apuntamos. Para llamar a esta funcion simplemente debemos usar al puntero como nombre de la funcion y le pasaremos los argumentos como si fuera la misma:

pFunc(vUno, vDos);
Anuncios

Recuerden que el compilador lo sabe todo, siempre y cuando tenga como minimo un prototipo para su referencia y/o este definido. Pero otra particularidad de estos punteros es que nos permite pasar funciones como argumentos. Tomemos el codigo anterior y realicemos la siguiente modificacion:

#include <iostream>

void MostrarValores(void (*pFunc)(int &, int &),int & v1, int & v2) {
        std::cout << "vUno: " << v1 << " | vDos: " << v2 << std::endl;
        pFunc(v1, v2);
        std::cout << "vUno: " << v1 << " | vDos: " << v2 << std::endl;
}

void Cuadrado(int & rX, int & rY) {
        rX *= rX;
        rY *= rY;
}

void Intercambiar(int & rX, int & rY) {
        int tmp;
        tmp = rX;
        rX = rY;
        rY = tmp;
}

void CambiarValores(int & rVuno, int & rVdos) {
        std::cout << "Nuevo valor para vUno: ";
        std::cin >> rVuno;
        std::cout << "Nuevo valor para vDos: ";
        std::cin >> rVdos;
}

int main()
{
        bool fSalir = false;
        int vUno = 4, vDos = 5;
        int opcion;
        while (fSalir == false) {
                std::cout << "(0) salir (1) cuadrado (2) intercambiar ";
                std::cout << "(3) Cambiar valores: ";
                std::cin >> opcion;
                switch(opcion)
                {
                        case 1: MostrarValores(Cuadrado,vUno,vDos); break;
                        case 2: MostrarValores(Intercambiar,vUno,vDos); break;
                        case 3: MostrarValores(CambiarValores,vUno,vDos); break;
                        default:
                                fSalir = true;
                                continue;
                                break;
                }
        }

        return 0;
}
Anuncios

Es muy similar a lo visto anteriormente pero los cambios los aplicamos en una funcion y luego en el main. Analicemos la funcion que modificamos:

void MostrarValores(void (*pFunc)(int &, int &),int & v1, int & v2) {
        std::cout << "vUno: " << v1 << " | vDos: " << v2 << std::endl;
        pFunc(v1, v2);
        std::cout << "vUno: " << v1 << " | vDos: " << v2 << std::endl;
}
Anuncios
Anuncios

Agregamos un nuevo argumento, en este caso un puntero. Observen que es el mismo que usamos en el main del codigo anterior. Donde tendremos un nombre para apuntar y recibira dos argumentos. Luego tenemos los mismos argumentos que anteriormente. Ahora en el bloque de instrucciones haremos la accion que haciamos antes. Es decir, mostramos los valores de las dos variables recibidas. Ejecutamos la funcion que asignamos al puntero y volvemos a mostrar los nuevos valores. La siguiente modificacion sera en el main. En este caso no solo eliminamos las lineas que agregamos en la funcion anterior sino que modificamos el switch de la siguiente manera:

switch(opcion)
{
        case 1: MostrarValores(Cuadrado,vUno,vDos); break;
        case 2: MostrarValores(Intercambiar,vUno,vDos); break;
        case 3: MostrarValores(CambiarValores,vUno,vDos); break;
        default:
                fSalir = true;
                continue;
                break;
}
Anuncios

Ahora en lugar de asignar la funcion al puntero llamamos a la funcion MostrarValores. En cada caso pasamos el nombre de la funcion correspondiente y luego los variables. El resto del codigo sigue siendo el mismo y sin modificaciones. Salvo las que mencionamos anteriormente, compilemos y veamos como trabaja ahora:

$ ./apuntar
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 1
vUno: 4 | vDos: 5
vUno: 16 | vDos: 25
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 2
vUno: 16 | vDos: 25
vUno: 25 | vDos: 16
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 1
vUno: 25 | vDos: 16
vUno: 625 | vDos: 256
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 3
vUno: 625 | vDos: 256
Nuevo valor para vUno: 10
Nuevo valor para vDos: 50
vUno: 10 | vDos: 50
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 2
vUno: 10 | vDos: 50
vUno: 50 | vDos: 10
(0) salir (1) cuadrado (2) intercambiar (3) Cambiar valores: 0
$
Anuncios

No solamente es una practicidad para trabajar con funciones desde otras funciones sino que tambien nos simplifico de gran manera nuestro codigo. Puede que ahora no sea tan intuitivo para una depuracion pero si que simplificamos y no hacemos uso redundante en nuestro codigo. Como todo en este lenguaje, particularmente en este, se domina con la practica y el uso frecuente. Tiene mas posibilidades pero esto es la base de como utilizarlo y a partir de esto salen todas sus variantes.

Anuncios

En resumen, hoy hemos visto a puntero de funciones, que es, para que sirve, como se utiliza, y una serie de ejemplos para ver sus distintas conductas y como puede ayudarnos a simplificar nuestro codigo. Espero les haya resultado 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
pp258

Donatión

It’s for site maintenance, thanks!

$1.50