| Versión 3.0 |
| 800x600 mínimo |
|
En esta lección: Declaración y definición de funciones ----- Variables globales y locales ----- Parámetros por defecto ----- Número variable de argumentos ----- Sobrecarga de funciones ----- Recursividad ----- Descargas ----- Otras secciones: Conceptos básicos ----- Programando en C ----- Programando en C++ ----- Programando Windows 9x. ----- Teoría electrónica ----- Circuitos electrónicos ----- Actividades adicionales ----- Hipervínculos ----- Contácteme: Dudas y comentarios ----- |
Funciones en C++C++ es un lenguaje que soporta totalmente el concepto llamado "Programación Orientada a Objetos" y por lo tanto centra su atención en los objetos, no obstante, las funciones siguen siendo un componente central de todo programa y merecen atención especial por las características nuevas agregadas por C++. Para información adicional puede consultar el artículo referente a las funciones en C. Primero la definición: Una función es una sección de código independiente, con nombre, que ejecuta una tarea específica y opcionalmente devuelve un valor al programa que la llamó. Ahora veamos las partes de la definición:
Cuando un programa llama a una función, la ejecución del código salta a la posición inicial definida por el nombre de la función, el programa ejecuta los enunciados contenidos en ésta y posteriormente el programa que llamó a la función continúa en la línea de código siguiente. Una función bién diseñada ejecuta una tarea específica y fácil de entender. Es recomendable ante una tarea complicada, dividirla en múltiples funciones y entonces llamarlas a su debido momento. Declaración y definición de funcionesPara utilizar una función en un programa se requiere en primer lugar declararla y después definirla. La declaración de la función le indica al compilador el nombre, el tipo de dato devuelto por la función y los parámetros pasados a la función. A la declaración de una función se le llama también el prototipo de la función. Cuando un programa llama a una función, el programa puede enviar hacia la función información en la forma de uno o más argumentos. Los argumentos son datos que la función puede necesitar para realizar su trabajo. Cuando la función termina su trabajo, la ejecución del programa regresa a la línea siguiente a la llamada a la función. Las funciones pueden enviar información al programa en la forma de un valor devuelto. El prototipo de una función provee al compilador con información de una función que será definida posteriormente en el programa. El prototipo incluye, en este orden, el tipo de valor que la función devolverá, el nombre de la función, el cual debe ser significativo del trabajo realizado por la función, y el tipo de variables utilizadas como argumentos que serán pasados a la función. Opcionalmente se puede incluir el nombre de las variables utilizadas como argumentos. El prototipo de una función, como todo enunciado ejecutable en un programa C++ debe terminarse con un símbolo de punto y coma. La definición de una función es en sí la función misma, está compuesta en su primer línea de código por el encabezado que debe ser idéntico al prototipo de la función pero en éste caso no se utiliza el punto y coma, además, al contrario del prototipo, donde es opcional incluir el nombre de las variables utilizadas como argumentos, en el encabezado de la función se debe incluir el nombre de las variables. Enseguida del encabezado está el cuerpo de la función que contiene, encerrados entre llaves, los enunciados ejecutables propios del trabajo a ejecutar por la función. Si la función devuelve un tipo de valor, éste se debe especificar al final del cuerpo de la función. Con toda la información reunida hasta este punto, estamos en condición de escribir un programa que demuestre el prototipo y definición de una función. Personalmente, primero escribo la definición de la función y después copio el encabezado de la misma para formar el prototipo, agregandole un punto y coma al final del enunciado.
En el código funcion1.cpp se demuestra el uso básico de las funciones en un programa C++, se aprecia en primer lugar el prototipo de dos funciones, una llamada resta y la otra propiamente llamada multiplica, utilizando nombres significativos para las funciones evitamos hacer comentarios redundantes y facilitamos la lectura del código. En el prototipo de la función resta está especificada la palabra clave void lo que nos indica que la función resta no devuelve valor alguno al programa que la llame, además especifica dos variables de tipo int llamadas valor1 y valor2 para ser utilizadas como argumentos de la misma. Recuerde que en el prototipo de la función no es necesario incluir los nombres de las variables utilizadas como argumentos, esto se demuestra el el prototipo de la función llamada multiplica que como está indicado, devuelve un valor de tipo int y requiere dos variables de tipo int como argumentos. Ya en el cuerpo de la función principal, main( ) el programa empieza declarando tres variables llamadas numero1, numero2 y resultado, le solicita introducir dos números que son almacenados en las variables numero1 y numero2 y entonces llama en primer lugar a la función llamada resta( ) en la línea 21 pasandole como argumentos las variables llamadas numero1 y numero2. El programa entonces brinca a la parte del código donde se encuentra la función llamada resta( ), en el encabezado de ésta función vemos que se especifican para los argumentos de la función los nombres de dos variables, valor1 y valor2, en el encabezado de la función sí es obligatorio especificar el nombre de las variables utilizadas como argumentos, se trata en efecto, de la declaración de dos variables de tipo int que serán utilizadas en forma local por la función. Dentro del cuerpo de la función resta se declara una variable int llamada sustraccion en donde se almacena el resultado de restar los valores pasados como argumentos a la función, éste resultado se despliega en pantalla como parte de los enunciados ejecutables de la función. La función resta( ) no devuelve un valor al programa, así que una vez terminados los enunciados ejecutables de la misma la ejecución del programa continúa en la línea 22, pero entonces el programa vuelve a llamar a la función resta( ), sin embargo observe que los argumentos están invertidos con respecto a la llamada anterior, ésto demuestra que los valores se pasan a la función en el orden en que son colocados en la llamada, de tal manera que en la segunda llamada a la función resta( ), valor1 toma el valor almacenado en la variable llamada numero2 y valor2 toma el valor almacenado en numero1. Después de ejecutar por segunda vez la función resta( ) el programa continúa en la línea 24 en donde se asigna a la variable llamada resultado el valor devuelto por la función llamada multiplica( ) a la que nuevamente le pasamos como argumentos los valores almacenados en las variables llamadas numero1 y numero2, esto es, el programa llama a la función multiplica( ) y ya en el cuerpo de ésta función se declara una variable local de tipo int llamada local en donde se almacena el resultado de multiplicar los valores pasados a la función como argumentos, después la función devuelve el valor almacenado en la variable llamada local. Finalmente el programa despliega el valor almacenado en resultado y termina devolviendo al sistema el valor de 0. Variables locales y globalesEn C++ está permitido declarar variables en cualquier parte del código fuente. Además de pasar variables como parte del argumento de una función, también es posible declarar variables dentro del cuerpo de una función, a este tipo de variables se les llama locales ya que sólo son útiles dentro del cuerpo de la función. Los parámetros utilizados en una función se consideran como variables de tipo local y se utilizan exactamente de la misma manera, como lo demuestra el fragmento de código siguiente tomado del programa funcion1.cpp.
Los parámetros de la función, ambos de tipo int, llamados valor1 y valor2 son en efecto variables de alcance local al igual que la variable declarada como de tipo int llamada local dentro del cuarpo de la función multiplica( ). El alcance de una variable es simplemente el espacio dentro del código fuente donde la variable puede ser utilizada, generalmente éste espacio está limitado por el par de llaves que sirven para encerrar las líneas de código que conforman una función, generalmente, porque existen excepciones en C++ como es el caso de las variables utilizadas en el encabezado de un bucle for, sin embargo éste asunto puede tener variantes de acuerdo al nuevo estándar ANSI-C++ y por lo tanto algunos compiladores pueden limitar la existencia de éstas variables estrictamente al código contenido dentro del bucle for. A las variables que declaramos fuera del cuerpo de cualquier función se dice que tienen un alcance global y por lo tanto estas funciones están disponibles para cualquier función del programa, incluyendo a main( ). Las variables locales que tienen el mismo nombre que las variables globales no cambian el valor de las globales, sin embargo una variable local con el mismo nombre que una variable global oculta a la variable global. Los conceptos discutidos hasta este momento quedan demostrados en el siguiente programa:
Estudiamos en primer lugar lo concerniente a las variables de alcance local llamadas local1 y local2, al igual que en C, las variables locales no son automáticamente inicializadas con un valor igual a cero, por éste motivo el resultado desplegado en la línea 17 es incierto, sin embargo al introducir un valor y almacenarlo en local1 tenemos la certeza de utilizar correctamente ésta variable. Mas adelante, dentro del bucle for se declara otra variable de alcance local llamada local2 en la línea 25, ésta variable sólo es útil para los enunciados contenidos dentro del bucle, para demostrarlo puede quitar el comentario del enunciado de la línea 44 y entonces el compilador generará un mensaje de error indicando que la variable local2 no ha sido declarada. Después del bucle for llamamos a la función llamada funcion1( ) pasándole como parámetro a la variable llamada local1, al hacer ésto, el compilador en realidad crea una copia de la variable local1 y asigna el valor contenido en ésta a la variable de alcance local llamda parametro, a éste proceso se le conoce como pasar un parámetro por valor. Ya dentro de la función, se suma el valor de parametro al de la variable de alcance global llamada global cambiando de ésta forma el valor contenido en la variable de alcance global llamada global. Como habrá observado, la función principal contiene a su vez una variable llamada global, ésta por supuesto de alcance local. En las líneas 34 a la 44 se demuestra cómo una variable de alcance local oculta a una variable de alcance global que tienen el mismo nombre, además se demuestra cómo utilizar el operador de alcance global (::) para tener acceso a una variable de alcance global oculta por una variable homónima de alcance local. También se demuestra que una variable local no cambia el valor contenido en una variable de alcance global homónima. Parámetros por defectoPara cada parámetro declarado en el prototipo de la función y en su definición, una llamada a la función debe pasar un valor del tipo específico, si la definición de la función difiere ó si Usted comete un error al pasar un valor equivocado a la función, entonces el compilador generará un mensaje de error, la única excepción a la regla es si el prototipo de la función declara un valor por defecto para el parámetro. Un valor por defecto es el valor a utilizar en caso de que ninguno sea especificado, así, una declaración de una función podría ser esta:
En éste ejemplo se declara una función llamada una_funcion que toma un parámetro de tipo int, pero en caso de que no se incluya el parámetro necesario, entonces se utilizará el valor de 120 para ser utilizado por la función. Como no es absolutamente necesario utilizar nombres para los parámetros en el prototipo de una función, también podemos escribir la declaración de una función de la siguiente manera:
Es posible asignarle valores por defecto a todos los parámetros de una función, pero con una condición, si cualquiera de los parámetros no lleva un valor por defecto, entonces los parámetros previos no podrán llevar un valor por defecto, por ejemplo, si el prototipo de una función es parecido a esto:
Usted puede asignar un valor por defecto a parametro2 solo si se le ha asignado un valor por defecto a parametro3. Usted solo podrá asignar un valor por defecto a parametro1 solo si se le ha asignado valores por defecto a parametro2 y a parametro3. El siguiente programa demuestra lo dicho:
Número variable de argumentosUna de las características atractivas de C++ es la manera estricta en que verifica el tipo de parámetros utilizados en las funciones, sin embargo habrá ocasiones en las que necesitemos una función con un número variable de parámetros, un ejemplo es la función C printf( ). El ANSI-C dispone de una serie de macros para determinar el número de argumentos utilizados en una función, éstas macros están definidas en el archivo de cabecera stdarg.h y también están disponibles para C++ pero necesitamos una manera de evitar que el compilador C++ haga un chequeo estricto de los argumentos pasados a una función, esto se consigue utilizando tres puntos como se indica en la línea 11 del siguiente programa:
El prototipo indicado en la línea 11 dice que se requiere un parámetro de tipo int y a partir de éste el compilador no checará el tipo y número de argumentos pasados a la función llamada varios_argumentos( ). Observará que el programa consiste en tres llamadas a la función varios_argumentos( ) precisamente con diferente número de argumentos en cada llamada, Usted puede poner el número que desee de parámetros en la llamada a la función siempre y cuando el primer parámetro sea uno de tipo int, después de ésto es responsabilidad suya utilizar con cuidado el tipo y número de argumentos pasados a la función, se debe evitar escribir código que resulte difícil de interpretar. Sobrecarga de funcionesC++ permite crear más de una función con el mismo nombre las cuales se diferenciarán por el número de parámetros utilizados ó en el tipo de éstos o en ámbos. El valor regresado por las funciones puede ser igual ó diferente sin embargo dos o más funciones con el mismo nombre y parámetros pero con valor de retorno diferente generará un mensaje de error por parte del compilador. Al proceso de llamar a más de una función con el mismo nombre se le llama sobrecarga de funciones. El siguiente programa demuestra la sobrecarga de la función llamada polimorfo:
Este programa demuestra además lo estricto que es C++ con respecto a checar el tipo de parámetros pasados a una función. En la línea 24 asignamos a una variable de tipo int llamada valor2 el contenido de la variable de tipo float llamada valor. Truncar la parte fraccionaria de la variable llamada valor ó introducir un número entero no es suficiente para llamar a la función polimorfo(int numero), es necesario utilizar las variables de tipo adecuado, es decir, los parámetros formales declarados en el prototipo de las funciones. RecursividadUna función se puede llamar a sí misma, a este proceso se le llama recursividad y puede ser directa e indirecta. Es directa cuando una función se llama a sí misma. Ocurre recursividad indirecta cuando una función llama a otra función y ésta última llama a la primera. Ambos tipos de recursividad se dan en dos formas, aquellas que eventualmente terminan y producen un resultado y el caso en que la recursividad nunca termina produciendo un bucle infinito y de aquí una falla en tiempo de ejecución por falta de memoria. Algunos programadores piensan que este último caso es divertido, sobre todo cuando le pasa a otra persona. :-) Es importante notar que cuando una función se llama a sí misma, una nueva copia de ésta función es la que se ejecuta. Las variables locales de la segunda función no interfieren con las variables locales de la primera, así mismo, las variables locales no se pueden afectar mutuamente. Tomemos como ejemplo un programa que calcule el factorial de un numero entero introducido por el usuario. El factorial está definido como el producto del número en cuestion por todos los numeros enteros menores que él, de tal manera que el factorial de 5 = 5*4*3*2*1 = 120. El código puede ser así:
Como el factorial de un número suele dar como resultado un valor muy grande necesitamos utilizar una función de tipo double que tome como parámetro un valor de tipo int, dicha función la hemos declarado con el nombre de factorial. Utilizamos una variable de alcance global llamada j para llevar un registro del número de veces en que la función factorial( ) es llamada. El programa empieza solicitándole un número entero y después llama por primera vez a la función factorial( ). La recursividad necesita una condición para que en el momento oportuno detener la acción, por ésta razón, la función factorial( ) determina en primer lugar si el valor pasado como parámetro de la función es igual a 1, en cuyo caso la función retorna 1. Si el valor del parámetro es mayor que 1, entonces la función factorial( ) se llama a sí misma tomando como parámetro el valor original menos uno, multiplicando éste valor por el valor del parámetro original y almacenando el resultado en la variable llamada resultado, como se aprecia en la línea 38 del programa. Cada vez que la función factorial( ) sea llamada con un parámetro mayor que 1 se incrementa la variable global llamada j, ésta variable debe tener ámbito global porque necesitamos compartir el valor acumulado en la variable entre todas las copias que en determinado momento se hagan de la función factorial( ), como resultado del fenómeno de recursividad. Un ejemplo adicional de recursividad lo encontrará en el programa llamado funcion6.cpp que está incluido en el archivo comprimido Cplus2.zip y que puede descargar al final de éste artículo. DescargasPara descargar el archivo comprimido Cplus2.zip (3.52 Kb) que contiene el código fuente de los programas de éste artículo, haga clic aquí. |