En algunos lenguajes de programación , la sobrecarga de funciones o de métodos es la capacidad de crear múltiples funciones con el mismo nombre y con diferentes implementaciones. Las llamadas a una función sobrecargada ejecutarán una implementación específica de esa función apropiada para el contexto de la llamada, lo que permite que una llamada de función realice diferentes tareas según el contexto.
Por ejemplo, doTask() y doTask(object o) son funciones sobrecargadas. Para llamar a la última, se debe pasar un objeto como parámetro , mientras que la primera no requiere un parámetro y se llama con un campo de parámetro vacío. Un error común sería asignar un valor predeterminado al objeto en la segunda función, lo que daría como resultado un error de llamada ambiguo , ya que el compilador no sabría cuál de los dos métodos utilizar.
Otro ejemplo es una función Print(object o) que ejecuta diferentes acciones según se trate de imprimir texto o fotos. Las dos funciones diferentes pueden estar sobrecargadas como Print(text_object T); Print(image_object P) . Si escribimos las funciones de impresión sobrecargadas para todos los objetos que nuestro programa "imprimirá", nunca tendremos que preocuparnos por el tipo de objeto y la llamada a la función correcta, siempre será: Print(something) .
Los lenguajes que admiten la sobrecarga de funciones incluyen, entre otros, los siguientes:
La sobrecarga de funciones suele estar asociada a lenguajes de programación de tipado estático que aplican la comprobación de tipos en las llamadas a funciones . Una función sobrecargada es un conjunto de funciones diferentes que se pueden llamar con el mismo nombre. Para cualquier llamada en particular, el compilador determina qué función sobrecargada utilizar y la resuelve en tiempo de compilación . Esto es cierto para lenguajes de programación como Java. [10]
La sobrecarga de funciones se diferencia de las formas de polimorfismo en las que la elección se realiza en tiempo de ejecución, por ejemplo, a través de funciones virtuales , en lugar de hacerlo de forma estática.
Ejemplo: Sobrecarga de funciones en C++
#include <flujo de datos> int Volumen ( int s ) { // Volumen de un cubo. return s * s * s ; } double Volume ( double r , int h ) { // Volumen de un cilindro. return 3.1415926 * r * r * static_cast < double > ( h ); } long Volume ( long l , int b , int h ) { // Volumen de un cuboide. return l * b * h ; } int main () { std :: cout << Volumen ( 10 ); std :: cout << Volumen ( 2.5 , 8 ); std :: cout << Volumen ( 100l , 75 , 15 ); }
En el ejemplo anterior, el volumen de cada componente se calcula utilizando una de las tres funciones denominadas "volumen", con selección basada en el diferente número y tipo de parámetros reales.
Los constructores , utilizados para crear instancias de un objeto, también pueden estar sobrecargados en algunos lenguajes de programación orientados a objetos . Debido a que en muchos lenguajes el nombre del constructor está predeterminado por el nombre de la clase, parecería que solo puede haber un constructor. Siempre que se necesitan varios constructores, se deben implementar como funciones sobrecargadas. En C++ , los constructores predeterminados no toman parámetros, instanciando los miembros del objeto con sus valores predeterminados apropiados, "que normalmente es cero para campos numéricos y cadena vacía para campos de cadena". [11] Por ejemplo, un constructor predeterminado para un objeto de factura de restaurante escrito en C++ podría establecer la propina en 15%:
Factura () : propina ( 0,15 ), // porcentaje total ( 0,0 ) { }
La desventaja de esto es que se necesitan dos pasos para cambiar el valor del objeto Bill creado. A continuación se muestra la creación y el cambio de valores dentro del programa principal:
Factura del café ; café.propina = 0,10 ; café.total = 4,00 ;
Si se sobrecarga el constructor, se pueden pasar la propina y el total como parámetros en la creación. Esto muestra el constructor sobrecargado con dos parámetros. Este constructor sobrecargado se coloca en la clase, así como el constructor original que usamos antes. El que se use depende de la cantidad de parámetros proporcionados cuando se crea el nuevo objeto Bill (ninguno o dos):
Factura ( doble propina , doble total ) : propina ( propina ), total ( total ) { }
Ahora, una función que crea un nuevo objeto Bill podría pasar dos valores al constructor y establecer los miembros de datos en un solo paso. A continuación, se muestra la creación y la configuración de los valores:
Billete de café ( 0,10 , 4,00 );
Esto puede ser útil para aumentar la eficiencia del programa y reducir la longitud del código.
Otra razón para la sobrecarga del constructor puede ser la de imponer miembros de datos obligatorios. En este caso, el constructor predeterminado se declara privado o protegido (o preferiblemente eliminado desde C++11 ) para que no sea accesible desde el exterior. Para la factura anterior, total podría ser el único parámetro del constructor, ya que una factura no tiene un valor predeterminado razonable para total, mientras que la propina tiene un valor predeterminado de 0,15.
Dos problemas interactúan con la sobrecarga de funciones y la complican: el enmascaramiento de nombres (debido al alcance ) y la conversión de tipos implícita .
Si se declara una función en un ámbito y luego se declara otra función con el mismo nombre en un ámbito interno, hay dos posibles comportamientos de sobrecarga naturales: la declaración interna enmascara la declaración externa (independientemente de la firma), o tanto la declaración interna como la externa se incluyen en la sobrecarga, y la declaración interna enmascara la externa solo si la firma coincide. La primera se toma en C++: "en C++, no hay sobrecarga entre ámbitos". [12] Como resultado, para obtener un conjunto de sobrecarga con funciones declaradas en diferentes ámbitos, es necesario importar explícitamente las funciones del ámbito externo al ámbito interno, con la using
palabra clave.
La conversión de tipos implícita complica la sobrecarga de funciones porque si los tipos de parámetros no coinciden exactamente con la firma de una de las funciones sobrecargadas, pero pueden coincidir después de la conversión de tipos, la resolución depende de la conversión de tipos elegida.
Estos pueden combinarse de maneras confusas: una coincidencia inexacta declarada en un ámbito interno puede enmascarar una coincidencia exacta declarada en un ámbito externo, por ejemplo. [12]
Por ejemplo, para tener una clase derivada con una función sobrecargada que toma a double
o an int
, usando la función que toma an int
de la clase base, en C++, se escribiría:
clase B { público : void F ( int i ); }; clase D : público B { público : usando B :: F ; void F ( double d ); };
No incluir los using
resultados en un int
parámetro pasado a F
la clase derivada se convierte en un doble y coincide con la función en la clase derivada, en lugar de en la clase base; incluir da using
como resultado una sobrecarga en la clase derivada y, por lo tanto, coincide con la función en la clase base.
Si un método está diseñado con una cantidad excesiva de sobrecargas, puede resultar difícil para los desarrolladores discernir qué sobrecarga se está invocando simplemente leyendo el código. Esto es particularmente cierto si algunos de los parámetros sobrecargados son de tipos que son tipos heredados de otros parámetros posibles (por ejemplo, "objeto"). Un IDE puede realizar la resolución de la sobrecarga y mostrar (o navegar hasta) la sobrecarga correcta.
La sobrecarga basada en tipos también puede obstaculizar el mantenimiento del código, ya que las actualizaciones del código pueden cambiar accidentalmente qué método de sobrecarga elige el compilador. [13]