Una funcin es un conjunto de lneas de cdigo que realizan una tarea especfica y puede retornar un valor. Las funciones pueden tomar parmetros que modifiquen su funcionamiento. Las funciones son utilizadas para descomponer grandes problemas en tareas simples y para implementar operaciones que son comnmente utilizadas durante un programa y de esta manera reducir la cantidad de cdigo. Cuando una funcin es invocada se le pasa el control a la misma, una vez que esta finaliz con su tarea el control es devuelto al punto desde el cual la funcin fue llamada.
Ejemplo de una funcin Para comenzar, vamos a considerar el caso en el cual se desea crear la funcin cuadrado(), misma que deber volver el cuadrado de un nmero real (de punto flotante), es decir, cuadrado() aceptar nmeros de punto flotante y regresar una respuesta como nmero flotante. Nota: aunque para la funcin que veremos el tipo de retorno coincide con el tipo de parmetro pasado, algunas veces las cosas pueden cambiar, es decir, no es obligatorio que una funcin reciba un parmetro de un tipo y que tenga que regresar una respuesta de dicho tipo.
// regresar el cuadrado de un nmero double cuadrado(double n) { return n*n; }
[editar] Parmetros
Normalmente, las funciones operan sobre ciertos valores pasados a las mismas ya sea como constantes literales o como variables, aunque se pueden definir funciones que no reciban parmetros. Existen dos formas en C++ de pasar parmetros a una funcin; por referencia o por valor. El hecho es que si en una declaracin de funcin se declaran parmetros por referencia, a los mismos no se les podr pasar valores literales ya que las referencias apuntan a objetos (variables o funciones) residentes en la memoria; por otro lado, si un parmetro es declarado para ser pasado por valor, el mismo puede pasarse como una constante literal o como una variable. Los parmetros pasados por referencia pueden ser alterados por la funcin que los reciba, mientras que los parametros pasados por valor o copa no pueden ser alterados por la funcin que los recibe, es decir, la funcin puede manipular a su antojo al parmetro, pero ningn cambio hecho sobre este se reflejar en el parmetro original. Parametros por valor La funcin cuadrado() (ver arriba) es un clsico ejemplo que muestra el paso de parmetros por valor, en ese sentido la funcin cuadrado() recibe una copia del parmetro n. En la misma funcin se puede observar que se realiza un calculo ( n*n ), sin embargo el parmetro original no sufrir cambio alguno, esto seguir siendo cierto an cuando dentro de la funcin hubiera una instruccin parecida a n = n * n; o n*=n;. Parametros por referencia Para mostrar un ejemplo del paso de parmetros por referencia, vamos a retomar el caso de la funcin cuadrado, salvo que en esta ocasin cambiaremos ligeramente la sintaxis para definir la misma. Veamos:
// regresar el cuadrado de un nmero double cuadrado2(double &n) { n *= n; return n; }
Al poner a prueba las funciones cuadrado() y cuadrado2() se podr verificar que la primera de estas no cambia el valor del parmetro original, mientras que la segunda s lo hace.
Notas: se debe de aclarar que el uso de la palabra void dentro de los parentesis es opcional al momento de declarar una funcin. Asi, la funcin pausa() podra haberse declarado como void pausa(), y la misma puede invocarse como: pausa();.
// declaracin de prototipo char *binstr(unsigned int); // punto de prueba int main() { int n = 128; count << "decimal = " << n << ", endl; cin.get(); }
// definicin de funcin binstr() // nota: esta funcion requiere de la librera estndar string char *binstr(unsigned int n) { static char buffer[65]; int i = 0; strcpy(buffer, "0"); if (n > 0) { while (n > 0) { buffer[i] = ( n & 1 ) + '0'; i++; n >>= 1; } buffer[i] = '\0'; strrev(buffer); } // fin (n > 0) return buffer;
Para ver un ejemplo ms, vamos a considerar el caso de la funcin binstr() del programa funciones01. Ahora, vamos modificar dicha funcin, salvo que esta ocasin nos interesa que la misma sirva para convertir nmeros decimales en cadenas numricas y cuya base de conversin sea pasada como parmetro. Es decir, la funcin de la que estamos hablando podr convertir nmeros decimales a: binario, octal, decimal, hexadecimal, etc.; y la nica condicin ser que la base indicada est entre el 2 y el 36, inclusive. Nota: Ya que la funcin servir para convertir nmeros a cualquier representacin la nombraremos como numstr() en lugar de binstr(). Si la funcin es invocada sin el parmetro base regresar una cadena de digitos decimales.
// programa : funciones02.cpp // autor : Oscar E. Palacios #include <iostream.h> #include <stdlib.h> using namespace std; // declaracin de prototipo char *numstr(unsigned int, const int base = 10); // punto de prueba int main() { int n = 128; count "decimal = " << n << ", endl; count "decimal = " << n << ", endl; cin.get(); }
binario = " << numstr(n, 2) << octal.. = " << numstr(n, 8) <<
// definicin de funcin numstr() // nota: esta funcion requiere de la librera stdlib.h char *numstr(unsigned int n, const int base) { static char buffer[65]; itoa(n, buffer, base);
return buffer; }
ambas reciben un puntero o referencia a un objeto de tipo entero, por lo tanto cualquiera de las funciones del ejemplo puede cambiar el valor de la variable entera apuntada por X, la diferencia radica en la forma en que cada una de las mismas lleva cabo la tarea. Si en la funcin puntero() en lugar de usar *X = 100; se usara X = 100; se le asignara 100 al puntero X, ms no al objeto apuntado por X, y esto podra ser la causa de que el programa se terminara de manera abrupta.
Ahora, pensemos que deseamos escribir una funcin para imprimir variables del tipo empleado. As, la funcin puede escribirse de las tres maneras siguientes:
// Parametro empleado pasado por valor void ImprimeEmpleadoV( empleado e) { printf("Nombre: %s\n", e.nombre); printf("Edad : %i\n", e.edad); printf("Sexo : %c\n", e.sexo); return; } // Parametro empleado pasado por referencia void ImprimeEmpleadoR( empleado &e ) {
printf("Nombre: %s\n", e.nombre); printf("Edad : %i\n", e.edad); printf("Sexo : %c\n", e.sexo); return;
// Parametro empleado pasado como puntero void ImprimeEmpleadoP( empleado *e ) { printf("Nombre: %s\n", e->nombre); printf("Edad : %i\n", e->edad); printf("Sexo : %c\n", e->sexo); return; }
una forma para poder salir y as evitar que stas se esten llamado de manera infinita, ya que sto ocasionara un error conocido como desbordamiento de pila (Stack Overflow). En el programa titulado funciones04.cpp que se ver enseguida, se crean dos funciones; Factorial y Factorial2, con el objetivo de que el estudiante pueda observar la diferencia entre la construccin de una funcin recursiva y otra igual pero no recursiva.
La formula general para calcular el factorial de un nmero n puede expresarse como: n (n-1) (n-2)...(1), misma que ser utilizada por la funcin recursiva factorial() del siguiente programa.
// programa funciones04.cpp // Autor: Oscar E. Palacios #include <stdlib.h> #include <iostream.h> // factorial es una funcion recursiva. // Nota: el maximo valor para el parmetro n es 16 long factorial(long n) { if (n <=1) return(1); else return( n * factorial(n - 1) ); } // factorial2 es una funcion no recursiva. // Nota: el maximo valor para el parmetro n es 16 long factorial2(long n) { long p, r; r = 1; if (n >1) { for (p=1; p<=n; p++) r = r * p; } return r; }
// punto de prueba int main() { cout << "Demostracin de una funcin recursiva" << endl << endl; // llamada a la funcion recursiva cout << "Factorial de 6 = " << factorial(6) << endl; // llamada a la funcion no recursiva cout << "Factorial de 6 = " << factorial2(6) << ednl; system("pause"); return 0;
donde: 1. 2. 3. 4. tipo es el tipo regresado por la funcin nombrefuncion es el nombre de la funcin int num es el nmero de parmetros que la funcin procesar ... esta notacin se emplea para indicar que el nmero de parmetros es variable
Nota: observe que la primera forma de declaracin es realmente variable el nmero de parmetros a procesar y en estos casos se debe establecer el mecanismo para determinar cuando se ha procesado el ltimo de los argumentos, en el segundo tipo de declaracin el nmero total de parmetros a procesar es igual al valor del parmetro num. En el siguiente programa, por ejemplo, se define una funcin ( printstr ) que despliega una lista variable de cadenas de caracteres.
// programa funciones05.cpp // Autor: Oscar E. Palacios #include <iostream.h> #include <stdarg.h> // despliega una lista de cadenas, la ultima debe ser NULL
void printstr(...) { va_list ap; char *arg; va_start(ap, 0); while ( (arg = va_arg(ap, char*) ) != NULL) { cout << arg; } va_end(ap); } int main() { printstr("Hola, ", "Esta es\n", "una prueba\n", NULL); cin.get(); return 0; }
En el programa que se listar en seguida, se define la funcin suma(), misma que operar sobre listas de nmeros enteros, la funcin devolver la suma de dichos nmeros.
// programa funciones06.cpp // Autor: Oscar E. Palacios #include <iostream.h> #include <stdarg.h> // Esta funcin opera sobre una lista variable de nmeros enteros int suma( int num, ... ) { int total = 0; va_list argptr; va_start( argptr, num ); while( num > 0 ) { total += va_arg( argptr, int ); num--; } va_end( argptr ); return( total ); } int main() { cout << suma(4, 100, 200, 300, 400) << endl; cin.get(); return 0; }
de
funcin
Uno de los paradgmas de la programacin es lo que se conoce como: Programacin Genrica. En ese sentido, las plantillas de funciones nos acercan al mencionado
paradgma puesto que podemos usar el mecanismo conocido como plantillas (en ingles, Templates ). Las plantillas de funciones estan pensadas con la idea de crear 'moldes' en donde el cdigo para llevar a cabo determinadas tareas es bsicamente el mismo y donde la diferencia sera el tipo de datos sobre los que opera la funcin. Por ejemplo, pensemos en la creacin de una funcin que reciba un par de objetos y que intecambie el valor de los mismos, es decir, si a la funcin se le pasan los parmetros ( X, Y ) el valor de X se le asignara a Y, y el valor de Y se le pasar a X. Para comenzar, podemos pensar en crear una funcin llamada Swap() para intercambiar un par de nmeros enteros y el cdigo de la misma se vera ms o menos como:
void Swap(int &a, int &b) { int temp = a; a = b; b = temp; }
Ahora bien, si usted pone a prueba la funcin Swap() ver que la misma funciona perfectamente, salvo que tiene la limitante de operar solamente con nmeros de tipo int. Luego, si quisieramos intercambiar objetos de tipo double, float, char, etc. sera que debemos repetir el mismo cdigo para cada uno de los tipos deseados ?. La respuesta a dicha interrogante es No, ya que la mayora de compiladores actuales para C++ permiten el use de plantillas. Con el fin de poner un ejemplo, vamos a escribir un programa y en el mismo declararemos la plantilla de funcin SwapGenerico(), misma que servir para intercambiar el valor de dos objetos de cualquier tipo.
// programa funciones06.cpp // Autor: Oscar E. Palacios #include <iostream.h> template <class T> void SwapGenerico(T &a, T &b) { T temp = a; a = b; b = temp; }; int main() { int i1 = 100; int i2 = 5; cout << "antes.. "<< "i1 = " << i1 << " i2 = " << i2 << endl; SwapGenerico(i1, i2); // swap de nmeros enteros cout << "despues "<< "i1 = " << i1 << " i2 = " << i2 << endl; cout << endl; char c1 = 'a'; char c2 = 'b'; cout << "antes.. " << "c1 = " << c1 << " c2 = " << c2 << endl; SwapGenerico(c1, c2); // swap de caracteres cout << "despues " << "c1 = " << c1 << " c2 = " << c2 << endl; cin.get();
return 0; }
Otro problema
En el ejemplo anterior vimos que la plantilla SwapGenerico solucion el problema de intercambiar el contenido de una pareja de objetos primitivos ( char, int, float, double, etc. ). Sin embargo, si usted pone a prueba la misma plantilla para hacer un Swap de cadenas de caracteres, ver que el compilador le informar de un error, ya que a las cadenas de caracteres no es posible aplicarles el operador de asignacon (=). Pero, ya que C++ lo permite, podemos escribir una funcin SwapGenerico sobrecargada y as el compilador decidir si usar la plantilla o usar la funcin de sobrecarga. Y de tal manera abremos resuelto el problema. As, puede agregar la siguiente funcin al programa visto anteriormente.
void SwapGenerico(char *a, char *b ) { char *tmp = new char[strlen(a)]; strcpy(tmp, a); strcpy(a, b); strcpy(b, tmp); delete tmp; }