21 Funciones IV: Sobrecarga

Anteriormente hemos visto operadores que tienen varios usos, como por ejemplo *, &, << o >>. Esto es lo que se conoce en C++ como sobrecarga de operadores. Con las funciones existe un mecanismo análogo, de hecho, en C++, los operadores no son sino un tipo especial de funciones, aunque eso sí, algo peculiares.

Así que en C++ podemos definir varias funciones con el mismo nombre, con la única condición de que el número y/o el tipo de los argumentos sean distintos. El compilador decide cual de las versiones de la función usará después de analizar el número y el tipo de los parámetros. Si ninguna de las funciones se adapta a los parámetros indicados, se aplicarán las reglas implícitas de conversión de tipos.

Las ventajas son más evidentes cuando debemos hacer las mismas operaciones con objetos de diferentes tipos o con distinto número de objetos. Hasta ahora habíamos usado macros para esto, pero no siempre es posible usarlas, y además las macros tienen la desventaja de que se expanden siempre, y son difíciles de diseñar para funciones complejas. Sin embargo las funciones serán ejecutadas mediante llamadas, y por lo tanto sólo habrá una copia de cada una.

Nota: Esta propiedad sólo existe en C++, no en C.

Ejemplo:

#include <iostream>
using namespace std;
 
int mayor(int a, int b);
char mayor(char a, char b); 
double mayor(double a, double b);
 
int main() { 
   cout << mayor('a', 'f') << endl; 
   cout << mayor(15, 35) << endl; 
   cout << mayor(10.254, 12.452) << endl; 
   
   return 0; 
}
 
int mayor(int a, int b) { 
   if(a > b) return a; else return b; 
}
 
char mayor(char a, char b) { 
   if(a > b) return a; else return b; 
}
 
double mayor(double a, double b) { 
   if(a > b) return a; else return b; 
}

Otro ejemplo:

#include <iostream>
using namespace std;
 
int mayor(int a, int b); 
int mayor(int a, int b, int c); 
int mayor(int a, int b, int c, int d);
 
int main() {
   cout << mayor(10, 4) << endl;
   cout << mayor(15, 35, 23) << endl; 
   cout << mayor(10, 12, 12, 18) << endl; 
   
   return 0; 
}
 
int mayor(int a, int b) { 
   if(a > b) return a; else return b; 
}
 
int mayor(int a, int b, int c) { 
   return mayor(mayor(a, b), c); 
}
 
int mayor(int a, int b, int c, int d) { 
   return mayor(mayor(a, b), mayor(c, d)); 
}

El primer ejemplo ilustra el uso de sobrecarga de funciones para operar con objetos de distinto tipo. El segundo muestra cómo se puede sobrecargar una función para operar con distinto número de objetos. Por supuesto, el segundo ejemplo se puede resolver también con parámetros por defecto.

Resolución de sobrecarga

Las llamadas a funciones sobrecargadas se resuelven en la fase de compilación. Es el compilador el que decide qué versión de la función debe ser invocada, después de analizar, y en ciertos casos, tratar los argumentos pasados en la llamadas.

A este proceso se le llama resolución de sobrecarga.

Hay que tener presente que el compilador puede aplicar conversiones de tipo, promociones o demociones, para que la llamada se ajuste a alguno de los prototipos de la función sobrecargada. Pero esto es algo que también sucede con las funciones no sobrecargadas, de modo que no debería sorprendernos.

En nuestro último ejemplo, una llamada como:

...
   cout << mayor('A', 'v', 'r') << endl;
...

Funcionaría igualmente bien, invocando a la versión de la función mayor con tres argumentos. El compilador hará la conversión implícita de char a int. Por supuesto, el valor retornado será un int, no un char.

Sin embargo, si usamos un valor double o float para alguno de los parámetros, obtendremos un aviso.

Problema

Propongo un ejercicio: implementar este segundo ejemplo usando parámetros por defecto. Para que sea más fácil, hacerlo sólo para parámetros con valores positivos, y si te sientes valiente, hazlo también para cualquier tipo de valor.

Comentarios de los usuarios (14)

b0ch0n
2010-10-28 04:15:20

algo está muy mal che!

modifico ésto(que no se si está bien):

int mayor(int x, int y=2);

int mayor(int x, int y=2, int w=3);

int mayor(int x, int y=2, int w=3, int s=4);

y me aparecen unos mensajes de error:

 In function `int main()\':
 
10: error: call of overloaded `mayor(int, int)\' is ambiguous
4: note: candidates are: int mayor(int, int)
5: note:                 int mayor(int, int, int)
6: note:                 int mayor(int, int, int, int)

11: error: call of overloaded `mayor(int, int, int)\' is ambiguous
5: note: candidates are: int mayor(int, int, int)
6: note:                 int mayor(int, int, int, int)

 In function `int mayor(int, int, int)\':
 
25: error: call of overloaded `mayor(int&, int&)\' is ambiguous
18: note: candidates are: int mayor(int, int)
24: note:                 int mayor(int, int, int)
6: note:                 int mayor(int, int, int, int)

In function `int mayor(int, int, int, int)\':
 
30: error: call of overloaded `mayor(int&, int&)\' is ambiguous
18: note: candidates are: int mayor(int, int)
24: note:                 int mayor(int, int, int)
29: note:                 int mayor(int, int, int, int)

30: error: call of overloaded `mayor(int&, int&)\' is ambiguous
18: note: candidates are: int mayor(int, int)
24: note:                 int mayor(int, int, int)
29: note:                 int mayor(int, int, int, int)

si alguien ayuda con ésto mejor para saber a que se deben y como se podria implementar

b0ch0n
2010-10-29 18:31:55

Creo que habia interpretado mal el enunciado o me complique sólo...ahora me siento valiente jaja

Nota del administrador: Hemos eliminado el código ya que las soluciones a los problemas no se han incluido de forma intencionada. La idea es que cada uno haga sus propios problemas a su modo.

Steven R. Davidson
2010-12-27 18:18:39

Hola b0ch0n,

Los errores que obtuviste en el primer intento ocurren porque el diseño que implementaste no conduce a una solución única. El compilador no puede decidir cuál versión de las tres funciones de \'mayor\' se debe usar y por tanto existe un problema de ambigüedad. Por ejemplo,

mayor( 5, 10 );

¿A cuál función alude esta invocación? La respuesta es que podríamos invocar cualquiera de las tres, porque cada una es válida.

Ciertamente, esto no era lo que pedía el ejercicio, por lo que el segundo intento es mejor. Sin embargo, el diseño de esta función tampoco es correcto. Queremos diseñar una función que funcione - valga la redundancia - de igual manera que la versión original de las tres funciones sobrecargadas. Esto significa que \'mayor()\' debe aceptar dos, tres, o cuatro parámetros. En tu implementación, tu función acepta hasta 4 parámetros o ninguno; esto no es lo que se pide.

Espero haber aclarado la duda.

Steven

Eric
2011-02-13 04:30:14

Hola queria saber si esta es la solucion que pides o que me haria falta, aqui la funcion funciona con valores por defecto de cualquier tipo creo.

#include <iostream>
using namespace std;

int mayor(int a=2, int b=-9) {
   if(a > b) return a; else return b;
}

int mayor(int a, int b, int c) {
   return mayor(mayor(a, b), c);
}

int mayor(int a, int b, int c, int d) {
   return mayor(mayor(a, b), mayor(c, d));
}
int main() {
   cout << mayor(10, 4) << endl;
   cout << mayor(15, 35, 23) << endl;
   cout << mayor(10, 12, 12, 18) << endl;
   cout << mayor ()<<endl;
   return 0;
}


Steven
2011-02-13 05:02:02

Hola Eric,

El problema pide que reescribas el segundo ejemplo definiendo y usando una sola - versión de la - función de 'mayor()'. Como el uso original de 'mayor()' permite pasar 2, 3, ó 4 parámetros, también queremos que este comportamiento exista en esta nueva versión del programa. Por lo tanto, debes implementar esta única función con parámetros por defecto, para ofrecer el mismo comportamiento original.

Por cierto, para corregir los ejercicios, pedimos que los mandéis a: ejercicioscpp@conclase.net Así no tentamos a los demás seguidores del curso a copiar los códigos fuente presentados por algunas personas como comentarios en nuestra página.

Espero haber aclarado la duda.

Steven

Maximiliano
2011-03-25 01:36:04

Hola muy bueno el tutorial. Lo estoy leyendo como repaso y para aprender algunas cosas que me quedaron pendiente te felicito.

Estaba leyendo algo sobre templates, sobrecarga de funciones y argumentos variables

Entonces hice esta funcion maximo

template <typename T> T Maximo(T maximo, ...)

{

T arg;

va_list ar;

va_start(ar, maximo);

while ((arg = va_arg(ar, T)) && arg != NULL)

{

if (arg > maximo)

maximo = arg;

}

va_end(ar);

return maximo;

}

bueno por que no soy un experto en el tema puse como condicion de salida del while que el arg no sea null pero en una funcion de maximo estaria mal por que sacaria como posible valor maximo al cero.

mi pregunta es que condicion generica puedo colocar al algoritmo para salir del bucle. gracias

Steven
2011-03-25 06:16:43

Hola Maximiliano,

La solución que aconsejo es agregar un número entero como el primer parámetro a esta función-plantilla, que sirva para indicar cuántos valores de tipo T se van a pasar a esta función. Esto es,

template< typename T >
T Maximo( unsigned short int nCant, ... );

Nota: No creo que necesites tantos parámetros que la cantidad que te proporciona 'unsigned short int', si éste ocupa 2 bytes.

Ahora podemos usar 'nCant' para controlar el bucle. Esto es,

  T arg, max = va_arg(ar, T);

  for( unsigned short int i=2; i<=n; i++ )
    if( (arg=va_arg(ar, T)) > max )
      max = arg;

El único problema de este diseño es que al no haber ningún parámetro en la función que sea del tipo genérico, T, tenemos que invocar esta función escribiendo el parámetro de la plantilla explícitamente. Esto es porque no hay suficiente información para que el compilador pueda determinar el tipo del dato particular. Por ejemplo,

int main()
{
  cout << Maximo<int>(4, 10,-20,30,40) << endl;
  return 0;
}

Espero haber aclarado la duda.

Steven

fco_gar_alb
2011-11-29 20:32:25

Para el problema 21 solo se me ocurre hacer esto... si hay alguna otra forma de resolver el problema, pueden dar alguna pista más; para ver por donde van los tiros.

#include <iostream>
#include <stdlib.h>
#include <limits.h>

using namespace std;
 

int mayor(int a, int b = INT_MIN, int c = INT_MIN, int d = INT_MIN ); 

 
int main() {
   cout << mayor(5) << endl;
   cout << mayor(15, 35, 23, 43) << endl; 

   system("pause");   
   return 0; 
}
 
int mayor(int a, int b, int c, int d) 
{    
    int numMay;
    
    if( c == INT_MIN && d == INT_MIN )
        ( a > b )? numMay = a : numMay = b;
    else
        numMay = mayor( mayor(a, b), mayor(c, d) );
           
    return numMay;
}

Steven R. Davidson
2011-11-29 21:49:10

Hola fco_gar_alb,

En primer lugar, pedimos que para los ejercicios hechos, por favor enviadlos a nuestro apartado de correo-e correspondiente: ejercicioscpp <arroba> conclase <punto> net. Así no tentamos a los demás que no los han hecho aún.

En cuanto al diseño, es acertado, pero tu implementación usa recursividad, la cual no la hemos visto todavía y la verdad es que no es necesaria en este caso. De hecho, es más eficiente no usar recursividad.

Hasta pronto,

Steven

Alejandro
2013-05-08 02:21:06

Gracias por este curso. Lo utilizo de apoyo a mi bibliografia.

Saludos.

ivan
2013-05-09 01:36:34

hola a todos.

alguien me podria decir como se puede concatenar en c++

un ejemplo:

codigo javascript.

var variable = "hola co";

var seguir = variable+"mo estas";

document.write(seguir);

es un ejemplo para explicarme mejor eso es algo que

quiero saber como hacer en c++

gracias por la respuestas de antemano.

Steven R. Davidson
2013-05-09 02:35:33

Hola Iván,

Las cadenas de caracteres en C/C++ son básicas y se basan en arrays (arreglos) de tipo 'char'. Por lo tanto, no hay concatenación en el lenguaje, aunque sí para cadenas literales. Por ejemplo,

cout << "hola" " a " "todos";

se interpretará como:

cout << "hola a todos";

Si quieres concatenar cadenas de caracteres, entonces usa la función estándar 'strcat()' o incluso, 'strncat()'. Por ejemplo,

char szMensaje[32] = "Hola";
strcat( szMensaje, " a " );
strcat( szMensaje, "todos" );

cout << szMensaje;

Puedes consultar estas funciones y más, que se declaran en <cstring>, yendo a: http://c.conclase.net/librerias/?ansilib=string#inicio

En C++, podemos usar la clase 'string', definida en <string>. Por ejemplo,

std::string sMensaje("Hola");
sMensaje += " a ";
sMensaje += "todos";

cout << sMensaje;

o incluso,

std::string sMensaje("Hola");
cout << sMensaje + " a " + "todos";

Espero que esto te ayude.

Steven

dresko
2016-08-28 09:05:55

por que me sale error en este programa en int conta; e conta= 2 y en el else

#include <iostream>

#include <cmath>

using namespace std;

struct Series {

double X;

int Numt ; };

void Leer (Series& S) ;//Todos tienen datos de X,Numt

void Imprimir (const Series& S);

void Potencia (const Series& S);

double Factorial(const Series& S);

double Suma (const Series& S);

int main (void){//Principiuo de int main

system ("cls");

Series Cas; //Es S o mas bien Cas (const Series& S); , es el apodo de cas osea S

int Pedrin;

Leer(Cas);

Pedrin=Factorial(Cas);

cout<<"Factorialo de: "<<Cas.Numt<<endl

<<endl<<Pedrin<<endl;

Imprimir (Cas);

system ("pause");

return 0;

}

void Leer (Series& S) {

cout<<"DATOS DE LA SERIE SENO"<<endl

<<"Dame el valor de x : "<<endl;

cin>>S.X;

cout<<"Numero de terminos : "<<endl;

cin>>S.Numt;

cout<<endl;

}

void Imprimir (const Series& S){

cout<<"Valor de la serie "<<endl

<<"SENO ( "<<S.X<<")="<<endl

<<Suma(S)<<endl<<endl;

}

void Potencia (const Series& S){

}

double Factorial(const Series& S) {

double F//De factorial

int conta ; //aqui es donde me aparece el error

conta = 2 ; //aqui tambien aparece un error

int F;

F=1;

if (S.Numt==0||S.Numt==1);{

return F;

}

else { //en este else tambien por que

for(conta=2; conta<=S.Numt;conta++)

{

F=F*conta ; //(cuando es del lado izquierodo conta++ primero se incrementa)

}

}

return F;

}

double Suma (const Series& S){

}

Steven R. Davidson
2016-08-28 19:53:29

Hola Dresko,

Tienes varios errores en 'Factorial()':

- No has escrito el punto y coma al final de la definición de 'F'. Escribes:

double Factorial(const Series& S) {
double F//De factorial
...

Debería ser,

double F;

- Intentas redefinir 'F', de esta manera:

int F;

No puedes redefinir una entidad en el mismo ámbito. Debes elegir una definición u otra.

- En la sentencia 'if', escribes,

if (S.Numt==0||S.Numt==1);{
return F;
}
else ...

Agregas un punto y coma, cuando no debería existir en este caso. Deberías escribir,

if( S.Numt==0 || S.Numt==1 )
{
  return F;
}
else ...

Espero que esto te aclare las dudas.

Steven