Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.11.2a2  Sobrecarga de métodos

§1  Sinopsis

Lo mismo que ocurre con las funciones normales ( 4.4.1), en la definición de funciones-miembro puede ocurrir que varias funciones compartan el mismo nombre, pero difieran en el tipo y/o número de argumentos. Ejemplo:

class hotel {
      char * nombre;
      int capacidad;
   public:
      void put(char*);
      void put(long);
      void put(int);
  }


La función put, definida tres veces con el mismo nombre está sobrecargada; el compilador sabe en cada caso qué definición usar por la naturaleza de los argumentos (puntero-a-char, long o int).

Nota: no confundir estos casos de sobrecarga en métodos con el polimorfismo; este último se refiere a jerarquías de clases en las que pueden existir diversas definiciones de métodos en miembros de distintas generaciones (super-clases y clases derivadas) con los mismos argumentos. Son las denominadas funciones virtuales ( 4.11.8a).

§2  Adecuación automática de argumentos

Una característica interesante, y a nuestro entender muy desafortunada del lenguaje (por la posibilidad de errores difíciles de detectar), es que en la invocación de funciones, sean estas miembros de clase o no, el compilador automáticamente intenta adecuar el tipo del argumento actual con el formal ( 4.4.5). Es decir, adecuar el argumento utilizado con el que espera la función según su definición.

Este comportamiento puede esquematizarse como sigue:

int funcion (int i) { /* ... */ }     // L.1:
....
otraFuncion (){
   int x = funcion(5);          // L.4: Ok. concordancia de argumentos
   int y = funcion(int('a'));   // L.5: Ok. promoción explícita
   int z = funcion('a');        // L.6: Ok. promoción automática!!
   ...
}


Es evidente que en L.4 y L.5 los argumentos formales y los actuales concuerdan, bien porque se trata del mismo tipo, bien porque se ha realizado una promoción explícita ( 4.9.9).

Lo que no resulta ya tan evidente, ni deseable a veces, es que en L.6 el compilador autorice la invocación a funcion pasando un tipo distinto del esperado. Esta promoción del argumento actual realizada de forma automática por el compilador (sin que medie ningún error o aviso -"Warning"- de compilación) puede ser origen de errores, y de que el programa use inadvertidamente una función distinta de la deseada. En este sentido podemos afirmar que el lenguaje C++ es débilmente tipado.

Posiblemente esta característica sea una de las desafortunadas herencias del C clásico [1], y es origen incluso de ambigüedades que no deberían existir. Por ejemplo, no es posible definir las siguientes versiones de un constructor ( 4.11.2d):

class Entero {
   public: int x;
   Entero(float fl = 1.0) { x = int(fl); }
   Entero(char c) { x = int(c); }
}

A pesar de su evidente disparidad, el compilador interpreta que existe ambigüedad en los tipos, ya que char puede ser provomido a float.

§2.1  Para complicar la cosa

Pero la conformación de argumentos no acaba con lo expuesto. Si en la invocación de una función f1 no existe correspondencia entre el tipo de un argumento actual x, y el formal y, el compilador puede buscar automáticamente en el ámbito si existe una función f2 que acepte el argumento discordante x y devuelva el tipo y deseado. En cuyo caso realizará una invocación implícita a dicha función f2(x) y aplicará el resultado y como argumento a la función primitiva. El mecanismo involucrado puede ser sintetizado en el siguiente esquema:

class C {                 // L.1
  public: int x;
  ...
};
int getx (C c1, C c2) { return c1.x + c2.x; }   // L.5
C fun (float f= 1.0) {    // L.6:
  C ct = { f };
  return ct;
}
...
unaFuncion () {
  int x = 10;
  C c1;
  ...
  int z = getx(x, c1);    // L.15
  ...
}

La invocación a getx en L.15 es correcta, a pesar de no existir una definición concordante para esta función cuyos argumentos formales son dos objetos tipo C, y en este caso el primer argumento x es un int. El mecanismo utilizado por el compilador es el siguiente:

Dentro del ámbito de visibilidad existe una función fun (L.6) que acepta un float y devuelve un objeto del tipo necesitado por getx. Aunque el argumento x disponible no es precisamente un float, puede ser promovido fácilmente a dicho tipo. En consecuencia el compilador utiliza en L.15 la siguiente invocación:

int z = getx( fun( float(x) ), c1);


Este tipo de adecuaciones automáticas son realizadas por el compilador tanto con funciones-miembro [2] como con funciones normales. Considere cuidadosamente el ejemplo que sigue, e intente justificar el proceso seguido para obtener cada uno de sus resultados.

#include <iostream>
using namespace std;

int x = 10;
long lg = 10.0;
class Entero {
  public:
  int x;
  int getx (int i) { return x * i; }
  int getx () { return x; }
  int getx (Entero e1, Entero e2) { return e1.x + e2.x; }
  Entero(float f= 1.0) { x = f; }
};
int getx (int i) { return x * i; }
int getx () { return x; }
int getx (Entero e1, Entero e2) { return e1.getx() + e2.getx(); }
Entero fun (float f= 1.0) {
  Entero e1 = { f }; return e1;
}

void main () {    // ==========================
  Entero c1 = Entero(x);
  cout << "E1 = " << c1.getx(0) << endl;
  cout << "E2 = " << c1.getx() << endl;
  cout << "E3 = " << c1.getx('a') << endl;
  cout << "E4 = " << c1.getx(lg) << endl;
  cout << "E5 = " << c1.getx(x, c1) << endl;
  cout << "E6 = " << c1.getx('a', c1) << endl;
  cout << "F1 = " << getx(0) << endl;
  cout << "F2 = " << getx() << endl;
  cout << "F3 = " << getx('a') << endl;
  cout << "F4 = " << getx(lg) << endl;
  cout << "F5 = " << getx(x, c1) << endl;
  cout << "F6 = " << getx('a', c1) << endl;
}

Salida (reformateada en dos columnas):

E1 = 0                 F1 = 0
E2 = 10                F2 = 10
E3 = 970               F3 = 970
E4 = 100               F4 = 100
E5 = 20                F5 = 20
E6 = 107               F6 = 107

§3    Temas relacionados
  • Sobrecarga de funciones ( 4.4.1a)
  • Sobrecarga de funciones genéricas (plantillas) 4.12.1-2

  Inicio.


[1]  El propio Stroustrup reconoce que la sobrecarga se basa en un conjunto relativamente complicado de reglas, y que ocasionalmente el programador puede verse sorprendido por la (no esperada) función que es invocada. Ver al respecto: Congruencia estándar de argumentos ( 4.4.1a).

[2]  Afortunadamente existe una opción para limitar este comportamiento "descontrolado" del compilador ( 4.11.2d1  Constructores explicit).