Parámetros de valor y parámetros de referencia
Los argumentos pueden pasarse a una función de dos formas: por valor y por referencia.
Todos los casos que hemos visto hasta ahora pasan por valor. Esta opción significa que el valor del argumento preparado por el fragmento de código de llamada se copia en una nueva variable, la variable de entrada correspondiente de la función. En caso contrario, el argumento y la variable de entrada no están relacionados. Todas las manipulaciones posteriores con la variable dentro de la función no afectan en modo alguno al argumento.
Para describir un parámetro de referencia, añada un signo ampersand '&' a la derecha del tipo. Muchos programadores prefieren añadir un «ampersand» al nombre de un parámetro, enfatizando así que el parámetro es una referencia al tipo dado. Por ejemplo, las siguientes entradas son equivalentes:
void func(int ¶meter);
|
Cuando se llama a una función no se crea una variable local correspondiente para un parámetro de referencia. En lugar de ello, el argumento especificado para este parámetro pasa a estar disponible dentro de la función con el nombre (alias) del parámetro de entrada. Así, el valor no se copia, sino que se utiliza en la misma dirección de la memoria. Por lo tanto, las modificaciones de un parámetro dentro de una función se reflejan en el estado de su argumento asociado. De ello se desprende una característica importante.
Sólo se puede especificar una variable (LValue, véase Operador de asignación) como argumento para un parámetro de referencia. De lo contrario, obtendremos el error «parámetro pasado como referencia, variable esperada».
El paso por referencia se utiliza en varios casos:
- Para mejorar la eficacia del programa eliminando la copia del valor;
- Para pasar datos modificados de una función al código de llamada cuando devolver un único valor con return no es suficiente.
El primer punto es especialmente relevante para variables potencialmente grandes, como cadenas o arrays.
Para distinguir entre la primera y la segunda finalidad de un parámetro de referencia, se recomienda a los autores de la función que añadan el modificador const cuando no se espere que cambie el parámetro dentro de la función. Esto le recordará a Ud. y les dejará claro a otros desarrolladores que pasar una variable dentro de una función no provocará efectos secundarios.
Si no se aplica el modificador const a los parámetros de referencia allí donde sea posible, pueden surgir problemas en toda la jerarquía de llamadas a funciones. El hecho es que llamar a tales funciones requerirá argumentos no constantes. De lo contrario, se producirá el error «la variable constante no puede pasarse como referencia». Como resultado, puede resultar gradualmente que todos los parámetros de todas las funciones deban despojarse del modificador const por el bien de la capacidad de compilación del código. De hecho, esto amplía el ámbito de posibles errores con la corrupción involuntaria de variables. La situación debería corregirse a la inversa: poner const allí donde no se requiera la devolución y modificación de valores.
Para comparar las formas de pasar parámetros en el script FuncDeclaration.mq5 se implementan varias funciones: FuncByValue paso por valor, FuncByReference paso por referencia, FuncByConstReference paso por referencia constante.
void FuncByValue(int v)
|
En la función OnStart invocamos todas estas funciones y observamos su efecto en la variable i utilizada como argumento. Tenga en cuenta que pasar un parámetro por referencia no cambia la sintaxis de llamada a la función.
void OnStart()
|
El literal sólo puede pasarse a la función FuncByValue, ya que otras funciones requieren una referencia, es decir, una variable, como argumento.
La función FuncByReference no puede invocarse con la variable j, ya que esta última se declara como una constante, y esta función declara la capacidad (o intención) de cambiar su parámetro ya que no está equipada con el modificador const. Esto genera el error «la variable constante no puede pasarse como referencia».
El script también describe la función Transpose: transpone una matriz de 2x2 pasada como array bidimensional por referencia.
void Transpose(double &m[][2])
|
Su llamada desde OnStart demuestra el cambio esperado en el contenido del array local a.
double a[2][2] = {{-1, 2}, {3, 0}};
|
En MQL5, los parámetros del array se pasan siempre como una estructura interna de un array dinámico (véase la sección Características de los arrays). En consecuencia, la descripción de dicho parámetro debe tener necesariamente un tamaño abierto en la primera dimensión, es decir, está vacía dentro del primer par de corchetes.
Esto no impide, si es necesario, pasar a la función el argumento real, que es un array con un tamaño fijo (como en nuestro ejemplo). Sin embargo, funciones como ArrayResize no podrán redimensionar o reorganizar de otro modo un array fijo enmascarado de este tipo.
Los tamaños del array en todas las dimensiones excepto la primera deben coincidir tanto para el parámetro como para el argumento. De lo contrario, obtendremos un error de «conversión de parámetros no permitida». En concreto, la función TransposeVector se define en el siguiente ejemplo:
void TransposeVector(double &v[])
|
Un intento de invocarla en un array bidimensional a se comenta en OnStart porque genera el error anterior: las dimensiones del array no coinciden.
Además de pasar parámetros por valor o por referencia, existe otra opción: pasar un puntero. A diferencia de C++, MQL5 sólo admite punteros para tipos de objeto (classes). Examinaremos esta característica en la tercera parte.