Funciones en C++

Básicamente una función puede realizar las mismas acciones que un programa:

  • aceptar datos

  • realizar unos cálculos determinados y, finalmente,

  • devolver resultados

Las funciones son invocadas desde otras funciones, con una excepción: la función global main(), que tienen todos los programas en C++. Permite al compilador conocer donde está el punto inicial de un programa. Por razones obvias, main() no puede ser invocada desde otras funciones.

Podemos distinguir 3 características de las funciones en C++:

  • la definición

  • la declaración

  • la llamada

Definición de una función

Es el código que realiza las tareas para las que la función ha sido prevista.

La primera línea de la definición recibe el nombre de encabezamiento y el resto, un bloque encerrado entre llaves, es el cuerpo de la función.

La definición de una función se debe realizar en alguno de los ficheros que forman parte del programa.

La sintaxis habitual de una definición de función es la siguiente

tipo nombre_funcion(tipo_1 arg_1, ..., tipo_n arg_n)
{
    sentencias;
    return expresion;  // optativo
}
  • tipo: indica el tipo de valor (int, float, etc.) devuelto por la función.

  • nombre_funcion: es el identificador usado para la función.

  • la lista de argumentos es una secuencia de declaración de parámetros separados por comas y encerrados entre paréntesis. Son los llamados parámetros formales de la función.

  • return expresion es un salto incondicional que permite evaluar una expresión y devolver su valor a la función llamante.

Veamos un ejemplo de definición de función que calcula la media de dos números reales.

Ejemplo

double calcula_media(double num1, double num2)
{
  double media;
  media = (num1 + num2)/2.;
  return media;
}
../../_images/funciones2.jpg

El tipo de una función

Una función devuelve, como mucho, un único valor a través de la sentencia return. Este valor tendrá el mismo tipo que el de la función.

La palabra reservada return permite realizar un salto incondicional, de la misma forma que break. En este caso, devuelve desde el interior de la función actual el control del programa a la función que la llamó.

Una función puede tener varios puntos en los que se devuelve un valor con return y, lógicamente, finalizará en el primero que se ejecute.

Al igual que con los break, la legibilidad del código debe ser la decisión que determine que haya un único return (preferible) o varios.

Declaración de una función

Al igual que ocurre con otros identificadores, en C++ no podemos llamar a una función en una sentencia sin que esté declarada previamente.

Una declaración explícita de una función, también denominada prototipo de la función, tiene la expresión general:

tipo nombre_funcion(tipo_1, tipo_2, ..., tipo_n);

En el ejemplo anterior el prototipo de la función es:

double calcula_media(double, double);

Es habitual encontrar prototipos acompañados de identificadores, en aras a mejorar la legibilidad del código. En cualquier caso es opcional. Así, el ejemplo anterior podría programarse como:

double calcula_media(double x, double y);

Recordemos que en C++, una declaración permite al compilador:

  • conocer de antemano las necesidades de memoria

    (se enviarán a la función dos double y se recibirá un double)

  • gracias al tipado estático, detectar errores en tiempo de compilación

    por ejemplo, si utilizamos la función con argumentos cuyos tipos no son compatibles con los explicitados en su prototipo.

LLamada a la función

La llamada a una función se hace especificando su nombre y, entre paréntesis, las expresiones cuyos valores se van a enviar como argumentos de la función.

Estos parámetros pueden ser identificadores o cualquier otra expresión válida.

La llamada a una función se escribe de forma general como sigue:

salida = nombre_funcion(arg_1, arg_2, ..., arg_n);

En nuestro ejemplo podría ser:

double resultado = calcula_media(numero1, numero2);

o, por ejemplo:

double resultado = calcula_media(numero1+numero2, 7);

Veamos un ejemplo completo con la función calcula_media()

Ejemplo

// Cálculo de la media de dos números
#include <iostream>
using namespace std;

double calcula_media(double, double);  // Declaración

int main()
{
    double numero1, numero2;
    cout << "Introduzca el primer número: ";
    cin >> numero1;
    cout << "Introduzca el segundo número: ";
    cin >> numero2;

    double resultado = calcula_media(numero1, numero2); // Llamada
    cout << "La media de "<< numero1 << " y " << numero2
         << " es " << resultado << ".\n";
}

// Definición
double calcula_media(double num1, double num2)
{
    double media;
    media = (num1+num2)/2.;
    return media;
}

Edita, compila y ejecuta el código

Error debido a una función no declarada

Es muy importante entender las consecuencias derivadas de:

  • no declarar una función

  • no definir una función

En el ejemplo, comente la línea de la declaración de la función:

// double calcula_media(double, double);  // Declaración

Al compilar el programa, obtendrá un error similar a:

  • error: use of undeclared identifier 'calcula_media'

o

  • error: 'calcula_media' was not declared in this scope

La razón es obvia. Todo identificador debe estar declarado antes de su uso. Cuando el compilador analiza la línea:

double resultado = calcula_media(numero1, numero2);

desconoce qué tipo de identificador es calcula_media.

Error debido a una función no definida

En el ejemplo, comente toda la definición de la función.

//double calcula_media(double num1, double num2)
//{
//  double media;
//  media = (num1 + num2)/2.;
//  return (media); // return media es válido también
//}

Al compilar el programa, obtendrá un error similar a:

  • undefined reference to `calcula_media(double, double)', error: linker command failed with exit code 1

Claramente aparece el aviso de que no está definida la función, undefined. En realidad, el compilador ha sido capaz de generar el código objeto del archivo. Toda la información necesaria está disponible respecto a la función main().

Es el enlazador (linker) el que ha dado la voz de alarma. No encuentra un archivo donde esté la definición de la función.

Declaración y definición simultáneas

Al igual que con las variables de tipos fundamentales, podemos optar por declarar y definir en un mismo paso, como puede verse en el ejemplo siguiente.

// Cálculo de la media de dos números
#include <iostream>
using namespace std;

// Declaración y definición simultáneas
double calcula_media(double num1, double num2)
{
    double media;
    media = (num1+num2)/2.;
    return (media);  // return media es también válido;
}

int main()
{
    double numero1, numero2;
    cout << "Introduzca el primer número: ";
    cin >> numero1;
    cout << "Introduzca el segundo número: ";
    cin >> numero2;

    double resultado = calcula_media(numero1, numero2); // Llamada
    cout << "La media de "<< numero1 << " y " << numero2
        << " es " << resultado << ".\n";
}

Edita, compila y ejecuta el código

Funciones void

En ocasiones una función no necesita devolver ningún valor y, por tanto, no es obligatorio usar return.

Para ello, debemos definir la función con un tipo especial llamado void.

Ejemplo

#include <iostream>
using namespace std;

void imprime_cadena(string cadena)
{
   cout << cadena;
   // return;  // Opcional
}
int main()
{
   string cadena = "Hola";
   imprime_cadena(cadena);
   imprime_cadena(" mundo.\n");
}

Edita, compila y ejecuta el código

Nota

std::string es un tipo de dato no nativo del espacio de nombres std, que permite almacenar y manipular cadenas de caracteres.

Funciones sin argumentos

Una función puede no necesitar una lista de parámetros.

Ejemplo

#include <iostream>
using namespace std;

//Opcional void imprime_mensaje_inicial(void)
void imprime_mensaje_inicial()
{
   cout << "Este programa bla bla bla…\n";
}

int main()
{
   imprime_mensaje_inicial();
}

Edita, compila y ejecuta el código