Curso de C++ v2.0
Consultas, lista de correo 'C++ Con Clase' 'C++ Con Clase' página de entrada Librerías estándar C Tabla de contenido Contactar con Webmaster
*Introducción
*1 Toma de contacto
*2 Variables I
*3 Funciones I: Declaración y definición
*4 Operadores I
*5 Sentencias
*6 Declaración de variables
*7 Normas para la notación
*8 Cadenas de caracteres
*9 Conversión de tipos
*10 Variables II: Arrays
*11 Variables III: Estructuras
*12 Variables IV: Punteros 1
*13 Operadores II: Más operadores
*14 Operadores III: Precedencia
*15 Funciones II: Parámetros por valor y referencia
*16 Variables V: Uniones
*17 Variables VI: Punteros 2
*18 Operadores IV: De bits y condicional
*19 Definición de tipos
*20 Funciones III
 . Parámetros por defecto
 . Número de argumentos variable
 . Argumentos de main
 . Funciones inline
 . Punteros a funciones
*21 Funciones IV: Sobrecarga
*22 Operadores V: Sobrecarga
*23 El preprocesador
*24 Funciones V: Recursividad
*25 Variables VII: Modificadores
*26 Espacios con nombre
*27 Clases I: Definiciones
*28 Declaración de clases
*29 Constructores
*30 Destructores
*31 El puntero this
*32 Sistema de protección
*33 Modificadores para miembros
*34 Más sobre funciones
*35 Operadores sobrecargados
*36 Herencia
*37 Funciones virtuales
*38 Derivación múltiple
*39 Trabajar con ficheros
*40 Plantillas
*41 Punteros a miembros
*42 Castings
*43 Excepciones
*Ejemplos capítulos 1 a 6
*Ejemplos capítulos 8 y 9
*A Palabras reservadas C/C++
*B Trigrafos y símbolos alternativos
*C Librerías estándar
*D Streams
<< < > >>

20 Funciones III

Aún quedan algunas cosas interesantes por decir sobre las funciones en C++.

Parámetros con valores por defecto  

Algunas veces nos puede interesar que ciertos parámetros que necesita una función no sea necesario proporcionarlos siempre. Esto suele suceder cuando esos parámetros casi siempre se usan con un mismo valor. En C++, cuando declaramos una función podemos decidir que algunos sus parámetros sean opcionales. En ese caso tendremos que asignarles valores por defecto.

Cuando se llama a la función incluyendo valores para los parámetros opcionales funcionará como cualquiera de las funciones que hemos usado hasta ahora, pero si se omiten todos o algunos de estos parámetros la función trabajará con los valores por defecto que hemos definido.

Por ejemplo:

#include <iostream>
using namespace std;
 
void funcion(int a = 1);
 
int main() { 
   funcion(19); 
   funcion(); 
   
   cin.get();
   return 0;
}
 
void funcion(int a) { 
   cout << a << endl; 
}

La primera llamada a "funcion" dará como salida 19, que es el parámetro que le damos explícitamente. La segunda llamada dará como salida 1, que es el valor por defecto.

Sin embargo este método tiene algunas limitaciones:

  • Sólo los últimos argumentos de las funciones pueden tener valores por defecto.
  • De estos, sólo los últimos argumentos pueden ser omitidos en una llamada.
  • Los valores por defecto deben especificarse bien en los prototipos, bien en las declaraciones, pero no en ambos.

Cuando se declaren valores de parámetros por defecto en prototipos, no es necesario indicar el nombre de los parámetros.

Por ejemplo:

void funcion(int = 1); // Legal
void funcion1(int a, int b=0, int c=1); // Legal 
void funcion2(int a=1, int b, int c); // Ilegal
void funcion3(int, int, int=1); // Legal 
...
void funcion3(int a, int b=3 int c) // Legal
{
}

Los argumentos por defecto empiezan a asignarse empezando por el último.

int funcion1(int a, int b=0, int c=1); 
...
funcion1(12, 10); // Legal, el valor para "c" es 1 
funcion1(12); // Legal, los valores para "b" y "c" son 0 y 1 
funcion1(); // Ilegal, el valor para "a" es obligatorio

Funciones con número de argumentos variable  

También es posible crear funciones con un número indeterminado de argumentos, para ello la declararemos los argumentos conocidos del modo tradicional, de este tipo debe existir al menos uno, y los desconocidos se sustituyen por tres puntos (...), del siguiente modo:

int funcion2(int a, float b, ...); 

Los parámetros se pasan usando la pila, (esto es siempre así con todos los parámetros, pero normalmente no tendremos que prestar atención a este hecho). Además es el programador el responsable de decidir el tipo de cada argumento, lo cual limita bastante el uso de esta forma de pasar parámetros.

Para hacer más fácil la vida de los programadores, se incluyen algunas macros en el fichero de cabecera "stdarg.h", estas macros permiten manejar "fácilmente" las listas de argumentos desconocidos.

Tipos:

En el fichero de cabecera "stdarg.h" de define un tipo:

va_list

Será necesario declarar una variable de este tipo para tener acceso a la lista de parámetros.

Macros:

También se definen tres macros: va_start, va_arg y va_end.

void va_start(va_list ap, ultimo);

Ajusta el valor de "ap" para que apunte al primer parámetro de la lista. <ultimo> es el identificador del último parámetro fijo antes de comenzar la lista.

tipo va_arg(va_list ap, tipo);

Devuelve el siguiente valor de la lista de parámetros, "ap" debe ser la misma variable que se actualizó previamente con "va_start", "tipo" es el tipo del parámetro que se tomará de la lista.

void va_end(va_list va);

Permite a la función retornar normalmente, restaurando el estado de la pila, esto es necesario porque algunas de las macros anteriores pueden modificarla, haciendo que el programa termine anormalmente.

Uso de las macros para leer la lista de parámetros:

<tipo>funcion(<tipo> <id1> [, <tipo> <id2>...], ...) 
{ 
   va_list ar; // Declarar una variable para manejar la lista
 
   va_start(ar, <idn>); // <idn> debe ser el nombre del último 
                              // parámetro antes de ... 
   <tipo> <arg>; // <arg> es una variable para recoger 
                          // un parámetro 
   while((<arg> = va_arg(ar, <tipo>)) != 0) { 
      // <tipo> debe ser el mismo que es de <arg> 
      // Manejar <arg> 
   }
   va_end(ar); // Normalizar la pila
}

Hay que usar un sistema que permita determinar cuál es el último valor de la lista de parámetros.

Una forma es que el último valor de la lista de parámetros en la llamada a la función sea un 0, (o más en general, un valor conocido).

También puede hacerse que uno de los parámetros conocidos sea la cuenta de los parámetros desconocidos.

Además es necesario que el programador conozca el tipo de cada parámetro, para así poder leerlos adecuadamente, lo normal es que todos los parámetros sean del mismo tipo, o que se use un mecanismo como la de "printf", donde analizando el primer parámetro se pueden deducir el tipo de todos los demás. Este último sistema también sirve para saber el número de parámetros.

Ejemplos:

#include <iostream> 
#include <cstdarg>
using namespace std;
 
void funcion(int a, ...);
 
int main() {
   funcion(1, "cadena 1", 0); 
   funcion(1, "cadena 1", "cadena 2", "cadena 3", 0); 
   funcion(1, 0); 
   
   cin.get();
   return 0; 
}
 
void funcion(int a, ...) {
   va_list p; 
   va_start(p, a); 
   char *arg;
 
   while ((arg = va_arg(p, char*))) {
      cout << arg << " "; 
   } 
   va_end(p); 
   cout << endl; 
}

Otro Ejemplo, este usando un sistema análogo al de "printf":

#include <iostream> 
#include <cstring> 
#include <cstdarg>
using namespace std;
 
void funcion(char *formato, ...);
 
int main() {
   funcion("ciic", "Hola", 12, 34, "Adios"); 
   funcion("ccci", "Uno", "Dos", "Tres", 4); 
   funcion("i", 1); 
   
   cin.get();
   return 0; 
}
 
void funcion(char *formato, ...) {
   va_list p; 
   char *szarg; 
   int iarg; 
   int i;
 
   va_start(p, formato); 
   /* analizamos la cadena de formato para saber el número y 
      tipo de cada parámetro */ 
   for(i = 0; i < strlen(formato); i++) { 
      switch(formato[i]) { 
         case 'c': /* Cadena de caracteres */ 
            szarg = va_arg(p, char*); 
            cout << szarg << " "; 
            break; 
         case 'i': /* Entero */ 
            iarg = va_arg(p, int); 
            cout << iarg << " "; 
            break; 
      } 
   } 
   va_end(p); 
   cout << endl; 
} 
<< < > >>
Free Web Hosting