Las funciones permiten estructurar programas en segmentos de código para realizar tareas individuales.

    En C++, una función es un grupo de instrucciones con un nombre que se puede llamar desde un punto del programa. La sintaxis más común para definir una función es la siguiente:

    tipo de nombre (parámetro1, parámetro2,…) {instrucciones}.

    Dónde:
    – es el tipo de valor devuelto por la función.
    – nombre es el identificador mediante el cual se puede llamar a la función.
    – parámetros (si es necesario): cada parámetro consta de un tipo seguido de un identificador, estando cada parámetro separado del siguiente por una coma. Cada parámetro es muy similar a una declaración de variable regular (por ejemplo, int x) y en realidad actúa en la función como una variable regular local de la función. El propósito de los parámetros es permitir que los argumentos se transmitan a la función desde la ubicación desde la que se llama.
    – es el cuerpo de la función. Es un bloque de instrucciones rodeado de llaves {} que especifican lo que realmente hace la función.

    Veamos un ejemplo:

    // ejemplo de función#incluye <iostream>usando namespace std;int addition (int a, int b){ int r; r = a + b; return r;}int hand (){ int z; z = addition (5,3); cost <<<< "The result is" <<< z;}.

    Este programa está dividido en dos funciones: adición y principal. Recuerde que, independientemente del orden en que se definan, un programa C++ siempre se inicia llamando a mano. De hecho, main es la única función llamada automáticamente y el código de cualquier otra función sólo se ejecuta si su función se llama desde main (directa o indirectamente.

    En el ejemplo anterior, la mano declara primero la variable z del tipo int, luego ejecuta la primera llamada de función: llama a la suma. La llamada a una función sigue una estructura muy similar a su declaración. En el ejemplo anterior, la llamada de adición puede compararse con su definición unas pocas líneas antes:

    Los parámetros de la declaración de función tienen una correspondencia clara con los argumentos pasados en la llamada de función. La llamada transmite dos valores, 5 y 3, a la función; estos corresponden a los parámetros a y b, declarados para la adición de la función.

    Cuando se llama la función desde el componente principal, el control cambia a la función de adición: aquí, la ejecución del parámetro principal se detiene y no se reanuda hasta que se completa la función de adición. Cuando se llama a la función, el valor de los dos argumentos (5 y 3) se copia en las variables locales int a y int b de la función.

    Luego, dentro de la suma, se declara otra variable local (int r), y mediante la expresión r = a + b, se atribuye a r el resultado de a más b; que, en este caso, donde a es 5 y b es 3, significa que 8 se atribuye a r.

    La declaración final en la función:

    return r;

    Finaliza añadiendo una función y devuelve el control al punto en el que se llamó a la función. en este caso: para operar como función principal. En este preciso momento, el programa reanuda su curso en el circuito principal y regresa exactamente al mismo tiempo que fue interrumpido por la llamada de la factura. Pero además, ya que además de un tipo de retorno, la llamada se evalúa como si tuviera un valor y este valor es el valor especificado en la instrucción de retorno que completó la suma: en este caso particular, el valor de la variable local r, que en el momento de la declaración tiene un valor de 8.

    Por lo tanto, la llamada de suma es una expresión con el valor devuelto por la función, y en este caso, el valor 8 se asigna a z. Es como si la llamada de función completa (suma (5,3)) se sustituyera por el valor devuelto (es decir, 8.

    Luego, la mano simplemente muestra este valor llamando:

    coste <<<< "El resultado es" <<<<< z;

    Una función puede ser llamada varias veces en un programa, y su argumento no se limita naturalmente a los literales:

    // ejemplo de función#incluye <iostream>usando namespace std;substraction int (int a, int b){ int r; r = a-b; return r;}int hand (){ int x = 5, y = 3, z; z = substraction (7,2); cost <<<< "El primer resultado es" << z <<< \ n'; cost <<<<<<<< "El segundo resultado es" <<<<< < < \ n'; coste <<<< << < < < < < < < < n'; z = 4 + sustracción (x, y); coste <<< < < < < z < < \ n'; < < < < < < n'; < < < < < < < < < < < < < n'; }.

    Resultado de la ejecución;

    El primer resultado es 5El segundo resultado es 5El tercer resultado es 2El cuarto resultado es 6

    Similar a la función de suma en el ejemplo anterior, este ejemplo define una función de resta, que simplemente devuelve la diferencia entre sus dos parámetros. Esta vez, la entidad de seguridad llama a esta función varias veces, lo que muestra más formas posibles de llamar una función.

    Examinemos cada una de estas llamadas, teniendo en cuenta que cada llamada de función es en sí misma una expresión evaluada como un valor devuelto. De nuevo, puede pensar en esto como si la llamada de función en sí misma hubiera sido reemplazada por el valor devuelto:

    z = resta (7,2);coste <<<< "El primer resultado es" <<<<< z;

    Si sustituimos la llamada a la función por el valor devuelto (es decir, 5), lo habríamos hecho:

    z = 5;coste <<<< "El primer resultado es" <<<< z;
    Con el mismo procedimiento, podríamos interpretar: coste <<<< "El segundo resultado es" <<<<< sustracción (7.2);
    como: coste <<<< "El segundo resultado es" <<<< 5;

    ya que 5 es el valor devuelto por sustracción (7,2.

    En el caso de:

    coste <<<<< "El tercer resultado es" <<<<<  sustracción (x, y);

    Los argumentos que se pasan a la resta son variables en lugar de literales. Esto también es válido y funciona bien. La función se llama con los valores x y y en el momento de la llamada: 5 y 3 respectivamente, devolviendo 2 como resultado.

    La cuarta llamada es de nuevo similar:

    z = 4 + resta (x, y);

    La única adición es que la llamada de función es también un operando de una operación de suma. De nuevo, el resultado es el mismo que si la llamada de función fuera reemplazada por su resultado: 6 Tenga en cuenta que, gracias a la propiedad conmutativa de las adiciones, lo anterior también puede escribirse de la siguiente manera:

    z = resta (x, y) + 4;

    Con exactamente el mismo resultado. También tenga en cuenta que el punto y coma no necesariamente va después de que la función es llamada, sino, como siempre, al final de toda la instrucción. Una vez más, la lógica detrás de esto puede ser fácilmente vista reemplazando las llamadas de función con su valor devuelto:

    z = 4 + 2; // idéntico a z = 4 + resta (x, y);z = 2 + 4; // idéntico a z = resta (x, y) + 4;

    Funciones sin tipo. El uso del vacío

    La sintaxis indicada anteriormente para las funciones:

    nombre de tipo (argument1, argument2….) {statement}

    Requiere que la declaración comience con un tipo. Este es el tipo de valor que devuelve la función. ¿Pero qué sucede si la función no necesita devolver un valor? En este caso, el tipo a utilizar es nulo, que es un tipo especial para representar la ausencia de valor. Por ejemplo, una función que simplemente imprime un mensaje puede no necesitar devolver un valor:

    // ejemplo de void#include <iostream>usando la función namespace std;cancelar mensaje de impresión (){ coste <<<<< "I am a function!";}int main (){ mensaje de impresión ();}}.

    Resultado de la ejecución:

    ¡Soy una función!

    void también puede usarse en la lista de parámetros de la función para especificar explícitamente que la función no toma ningún parámetro real cuando se la llama. Por ejemplo, el mensaje de impresión podría haber sido declarado como:

    void printmessage (void){ coste <<<< "I am a function!";};}

    En C++, se puede utilizar una lista de parámetros vacía en lugar de vacía con el mismo significado, pero el uso de void en la lista de argumentos ha sido popularizado por el lenguaje C, en los casos en que es un requisito.

    Los corchetes que siguen al nombre de la función no son de ninguna manera opcionales, ni en su declaración ni cuando se llama. E incluso cuando la función no toma ningún parámetro, al menos un par de paréntesis vacíos deben añadirse siempre al nombre de la función. Vea cómo se llamaba printmessage en un ejemplo anterior:

    mensaje de impresión ();

    Los paréntesis son los que diferencian las funciones de otros tipos de declaraciones.

    Lo siguiente no llamaría la función:

    mensaje de impresión;

    El valor de retorno de la mano

    Puede que haya notado que el tipo de retorno de mano es int, pero la mayoría de los ejemplos en este capítulo y en los anteriores no devuelven ningún valor de mano.

    Bueno, hay un problema: si la ejecución de la mano normalmente termina sin encontrar instrucciones de retorno, el compilador asume que la función termina con una instrucción de retorno implícita:

    retorno 0;

    Tenga en cuenta que esto sólo se aplica a la función principal por razones históricas. Todas las demás funciones con un tipo de retorno deben terminar con una instrucción de retorno apropiada que incluya un valor de retorno, incluso si nunca se utiliza.

    Cuando el principal devuelve cero (implícita o explícitamente), se interpreta que el entorno indica que el programa ha finalizado con éxito. Otros valores pueden ser devueltos a mano y algunos entornos permiten que la persona que llama acceda a este valor de una forma u otra, aunque este comportamiento no es necesario o necesariamente portátil entre plataformas. Los valores de mano que deben ser interpretados de la misma manera en todas las plataformas son los siguientes:

    ValuedescriptionEl programa ha tenido éxitoEXIT_SUCCESEl programa ha tenido éxitoEste valor está definido en.EXIT_FAILUREel programa ha fallado.

    Porque el retorno implícito 0; Instrucción para principal es una delicada excepción. Algunos autores consideran preferible escribir la instrucción explícitamente.

    Argumentos pasados por valor y por referencia

    En las funciones vistas anteriormente, los argumentos siempre se han pasado por valor. Esto significa que, cuando se llama una función, lo que se transmite a la función son los valores de estos argumentos en el momento de la llamada, que se copian en las variables representadas por los parámetros de la función. Por ejemplo, tome:

    int x = 5, y = 3, z;z = suma (x, y);
    

    En este caso, la suma de las funciones pasa de 5 a 3, que son copias de los valores de x e y, respectivamente. Estos valores (5 y 3) se utilizan para inicializar las variables definidas como parámetros en la definición de la función, pero cualquier modificación de estas variables en la función no tiene efecto sobre los valores de las variables x e y fuera de la función, ya que x e y no se transmitieron a la función en la llamada, sino sólo copias de sus valores en ese momento.

    En algunos casos, sin embargo, puede ser útil acceder a una variable externa desde una función. Para hacer esto, los argumentos pueden ser pasados por referencia en lugar de por valor. Por ejemplo, la función de duplicado en este código duplica el valor de sus tres argumentos, lo que resulta en la modificación de las variables utilizadas como argumentos por la llamada:

    // Pasar parámetros por referencia#incluye <iostream>usando namespace std;void duplicate (int & a, int & b & b, int & c){ a * = 2; b * = 2; c * = 2;}int principal (){ int x = 1, y = 3, z = 7; duplicar (x, y, z); coste << "x =" << x <<", y =" <<< y <<", z =" <<<< z; retorno 0;}.

    Resultado de la ejecución:

    x = 2, y = 6, z = 14

    Para acceder a sus argumentos, la función declara sus parámetros como referencias. En C ++, las referencias se indican con un ampersand (&) dependiendo del tipo de parámetro, como en los parámetros tomados dos veces por el ejemplo anterior.


    Cuando se pasa una variable por referencia, lo que se pasa ya no es una copia, sino que la propia variable, la identificada por el parámetro función, se asocia de alguna manera con el argumento transmitido a la función y cualquier modificación de las variables locales correspondientes en la función se refleja en las variables transmitidas como argumentos en la llamada.

    De hecho, a, b y c se convierten en alias de los argumentos pasados en la llamada de función (x, y y y z) y cualquier modificación hecha a a en la función realmente modifica la variable x fuera de la función. Cualquier modificación en b modifica y, y cualquier modificación en c modifica z. Por lo tanto, cuando, en el ejemplo, la función duplicada modifica los valores de las variables a, b y c, se asignan los valores de x, y y y z.

    Si en lugar de definir duplicado como:

    duplicado vacío (int & a, int & b, int & c)

    En caso de que se defina sin amperios y como:

    duplicado nulo (int a, int b, int c)

    Las variables no se transmitirán por referencia, sino por valor, creando en su lugar copias de sus valores. En este caso, el resultado del programa habría sido los valores de x, y y y z sin ser modificados (es decir, 1, 3 y 7.

    Consideraciones de eficiencia y referencias consistentes

    Cuando se llama a una función con parámetros tomados por valor, los valores se copian. Esta es una operación relativamente barata para tipos fundamentales como int, pero si el parámetro es de tipo compuesto amplio, puede resultar en alguna sobrecarga. Por ejemplo, considere la siguiente función:

    string concatener (cadena a, cadena b){ devolver a + b;}}

    Esta función toma dos cadenas como parámetros (por valor) y devuelve el resultado de su concatenación. Al pasar los argumentos por valor, la función obliga a a y b a copiar los argumentos transmitidos a la función cuando se llama. Y si estas cadenas son largas, esto puede implicar la copia de grandes cantidades de datos sólo para la llamada de función.

    Pero esta copia puede evitarse por completo si ambos parámetros son referencias:

    string concatener (cadena & a, cadena & b){ devolver a + b;}}

    Los argumentos por referencia no requieren una copia. La función actúa directamente sobre los alias de las cadenas pasadas como argumentos, y a lo sumo puede significar la transferencia de algunos punteros a la función. En este sentido, la versión de concatenación basada en referencias es más eficaz que la versión basada en valores, ya que no necesita copiar cadenas de texto costosas para copiarlas.

    Por otro lado, las funciones con parámetros de referencia se perciben generalmente como funciones que modifican los argumentos transmitidos, porque es por eso que los parámetros de referencia están realmente destinados.

    La solución para la función es asegurarse de que sus parámetros de referencia no sean modificados por esta función. Esto puede hacerse calificando los parámetros como constantes:

    string concatener (cadena de const & a, cadena de const & b){ devolver a + b;}}

    Al calificarlos como const, la función tiene prohibido modificar los valores de a ni b, pero puede acceder a sus valores como referencias (alias de argumentos), sin tener que hacer copias de las cadenas.

    Por lo tanto, las referencias const proporcionan una funcionalidad similar a la transmisión de argumentos basada en valores, pero con una mayor eficiencia para parámetros de tipos importantes. Esta es la razón por la que son extremadamente populares en C++ para argumentos de tipo compuesto. Tenga en cuenta, sin embargo, que para la mayoría de los tipos fundamentales, no hay una diferencia significativa en la efectividad, y en algunos casos, las referencias constantes pueden incluso ser menos efectivas.

    Las funciones Inline

    Llamar a una función implica normalmente costes adicionales (argumentos de apilamiento, saltos, etc.) y, por lo tanto, para funciones muy cortas, puede ser más eficiente insertar simplemente el código de la función en la que se llama en lugar de realizar el proceso. para llamar oficialmente a una función.

    La precedencia de una declaración de función con el especificador en línea le dice al compilador que el desarrollo en línea es preferible al mecanismo habitual de llamada de función para una función específica.

    Esto no cambia el comportamiento de una función en absoluto, sino que simplemente sirve para sugerir al compilador que el código generado por el cuerpo de la función debe ser insertado en cada ubicación donde se llama la función, en lugar de ser llamado con una llamada de función regular.

    Por ejemplo, la función de concatenación anterior puede declararse de la siguiente manera:

    concatenador de cadenas en línea (cadena const & a, cadena const & b) { devolver a + b;}

    Esto informa al compilador de que, cuando se llama a la concatenación, el programa prefiere que la función se amplíe en línea, en lugar de hacer una llamada normal. inline sólo se especifica en la declaración de función, no cuando se llama.

    También lea Tutorial, Aprenda a programar en

    CNotez que la mayoría de los compiladores ya optimizan el código para generar funciones en línea cuando ven una oportunidad de mejorar la eficiencia, incluso si no están explícitamente marcados con el especificador en línea. Por lo tanto, este especificador simplemente le dice al compilador que se prefiere inline para esta función, aunque el compilador es libre de no inlinearlo y de optimizarlo de otra manera. En C++, la optimización es una tarea delegada al compilador, que es libre de generar cualquier código siempre y cuando el comportamiento resultante sea el especificado por el código.

    Valores por defecto en los parámetros

    En C++, las funciones también pueden tener parámetros opcionales, para los cuales no se requiere ningún argumento en la llamada, de modo que, por ejemplo, una función con tres parámetros puede llamarse con sólo dos. Para ello, la función debe incluir un valor por defecto para su último parámetro, que es utilizado por la función cuando es llamada con menos argumentos. Por ejemplo:

    // valores por defecto en las funciones#incluyen <iostream>usando namespace std;int dividir (int a, int b = 2){ int r; r = a / b; return (r);}int main (){ cout <<<< dividir (12) <<' <' \ n'; cout << dividir (20,4) <<<<' \ n'; return 0;}}.

    Resultado de la ejecución:

    65

    En este ejemplo, hay dos llamadas a la función de partición. En la primera:

    dividir (12)

    La llamada transmite sólo un argumento a la función, incluso si la función tiene dos parámetros. En este caso, la función asume que el segundo parámetro es 2 (observe la definición de la función, que declara su segundo parámetro como int b = 2. Por lo tanto, el resultado es 6.

    En la segunda llamada:

    dividir (20.4)

    La llamada pasa dos argumentos a la función. Por lo tanto, el valor por defecto para b (int b = 2) es ignorado y b toma el valor pasado como argumento, es decir, 4, dando un resultado de 5.

    Declarar funciones

    En C++, los identificadores sólo se pueden utilizar en expresiones después de haber sido declarados. Por ejemplo, algunas variables x no se pueden usar hasta que se declaran con una instrucción, como por ejemplo:

    int x;

    Lo mismo se aplica a las funciones. Las funciones no se pueden llamar hasta que se declaran. Por eso, en todos los ejemplos de funciones anteriores, las funciones siempre se han definido antes que la función principal, que es la función desde la que se han llamado las otras funciones. Si la mano se definiera antes que las otras funciones, se violaría la regla de que las funciones deben ser declaradas antes de ser utilizadas y, por lo tanto, no se compilan.

    El prototipo de una función puede ser declarado sin definir realmente completamente la función, dando el detalle suficiente para permitir que se conozcan los tipos involucrados en una llamada de función. Por supuesto, la función debe ser definida en otra parte, como más adelante en el código. Pero al menos, una vez declarado como tal, ya puede ser llamado.

    La declaración debe incluir todos los tipos implicados (tipo de retorno y tipo de argumento), utilizando la misma sintaxis que se utiliza en la definición de la función, pero sustituyendo el cuerpo de la función (el bloque de instrucciones) por un punto y coma final.

    La lista de parámetros no necesita incluir nombres de parámetros, sólo sus tipos. Los nombres de los parámetros todavía se pueden especificar, pero son opcionales y no necesariamente tienen que coincidir con los de la definición de la función. Por ejemplo, una función llamada protofunción con dos parámetros int puede ser declarada con una de estas instrucciones:

    int protofunction (int primero, int segundo);int protofunction (int, int);

    En cualquier caso, incluir un nombre para cada parámetro siempre mejora la legibilidad de la declaración.

    // declaración de función prototipos#incluye <iostream>usando namespace std;void odd (int x);void even (int x);int main(){ int i; do { cost <<<<< "Por favor, introduzca el número (0 para salir):"; cin >> i; odd (i); }while (i! = 0); return 0;}void odd (int x){ if ((x% 2)! = 0) coste <<<< "Es extraño. \ n"; si no, el mismo (x);} mismo vacío (int x){ si ((x% 2) == 0) coste <<<< "Es par. \ N"; si no impar (x);} } }

    Resultado de la ejecución

    Por favor, introduzca el número (0 para salir): 9Es extraño. Por favor, introduzca el número (0 para salir): 6 Es par. Por favor, introduzca el número (0 para salir): 1030 Es par. Por favor, introduzca el número (0 para salir): 0 Es par.

    Este ejemplo no es un ejemplo de eficiencia. Probablemente pueda escribir una versión de este programa usted mismo con la mitad de las líneas de código. En cualquier caso, este ejemplo ilustra cómo se pueden declarar las funciones antes de que se definan:

    Las siguientes líneas:

    void odd (int a);void even (int a);

    Declarar el prototipo de las funciones. Ya contienen todo lo que necesitas para llamarlos, su nombre, los tipos de sus argumentos y su tipo de retorno (vacío en este caso. Con estas declaraciones de prototipo en su lugar, se pueden llamar antes de que se definan completamente, lo que permite, por ejemplo, colocar la función desde su ubicación (principal) antes de la definición misma de estas funciones.

    Pero declarar las funciones antes de que sean definidas no sólo es útil para reorganizar el orden de las funciones en el código. En algunos casos, como en este caso en particular, se requiere al menos una de las declaraciones, porque los impares y los pares se llaman entre sí; hay una llamada a par en par y una llamada a par en par. Y, por lo tanto, no hay forma de estructurar el código de manera que lo impar se defina antes que lo par, e incluso antes que lo impar.

    Recursividad

    La recursividad es la propiedad que las funciones deben ser llamadas por sí mismas. Es útil para ciertas tareas, como la clasificación de elementos o el cálculo del factorial numérico. Por ejemplo, para obtener el factorial de un número (n!), la fórmula matemática sería:

    n! = n * (n-1) * (n-2) * (n-3) … * 1

    Más concretamente, 5! (factor de 5) sería:

    5! = 5 * 4 * 3 * 2 * 1 = 120

    Y una función recursiva para calcular esto en C++ podría ser:

    // calculator factor#include <iostream>using namespace std;long factorial (long a){ if (a> 1) return (a * factorial (a-1)); else return 1;}int main (){ long number = 9; cost <<<<<<<"! =" <<< factorial (number); return 0;}.

    Resultado de la ejecución:

    9! = 362880

    Observe cómo, en la función factor, incluimos una llamada a sí misma, pero sólo si el argumento pasado era mayor que 1, porque de lo contrario, la función realizaría un bucle recursivo infinito, en el que, una vez que llegara a 0, continuaría multiplicándose por todo. números negativos (probablemente causando un desbordamiento de pila en algún momento durante la ejecución.

    Otros consejos interesantes:

    1. Clases en C++ (2) Las clases, esencialmente, definen los nuevos tipos que se utilizarán en el código C++. Y los tipos C++ no sólo interactúan con el código por medio de construcciones y asignaciones. También interactúan…..
    2. Las clases en C++ Las clases son un concepto extendido de estructuras de datos: al igual que las estructuras de datos, pueden contener miembros de datos, pero también pueden contener funciones como miembros. Un objeto……
    3. Operadores en C++ Una vez introducidas las variables y constantes, podemos empezar a utilizarlas utilizando operadores. La siguiente es una lista completa de operadores. En esta etapa, probablemente no es necesario……
    4. Memoria dinámica en C++ En los programas descritos en los capítulos anteriores, todos los requisitos de memoria se determinaron antes de ejecutar el programa mediante la definición de las variables necesarias. Pero puede suceder que las necesidades de memoria de un….
    5. Polimorfismo en C++ Antes de profundizar en este capítulo, es necesario tener una buena comprensión de los indicadores y la herencia de clase. Si no está realmente seguro de lo que significa una de las siguientes expresiones, debería……