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.13.6  Conversión automática a tipos básicos

§1  Preámbulo

La técnica que aquí comentamos fue presentada por Coplien [1] bajo la denominación "Member operator function idiom", y podría catalogarse dentro de las técnicas de modelado. En realidad Coplien la presenta bajo el epígrafe "Conversión de tipos" y advierte que solo debe utilizarse para convertir tipos abstractos a tipos escalares (preconstruidos en el lenguaje) que él denomina a veces "tipos indígenas". Se basa en la utilización operadores de conversión, que hemos presentado anteriormente ( 4.9.18k), que utilizan la palabra clave operator,.

Nota: no confundir esta utilización de operator en los operadores de conversión, con su uso en las funciones-operador empleadas para sobrecargar operadores ( 4.9.18).

§2  Función-operador miembro

En ocasiones puede ser necesario que determinados tipos abstractos se comporten como tipos "indígenas". Esto puede hacerse incluyendo una función-operador adecuada en la definición de la clase.  Por ejemplo: supongamos que tenemos una clase Complejo que utilizamos para formular nuestro álgebra de números complejos, pero deseamos que los objetos c de este tipo se comporten como escalares (float) cuando sea necesario un escalar en el contexto de la operación.  En este caso, el objeto c se comportará como un float cuyo valor sea la parte real del complejo (se trata de un ejemplo no limitativo, de forma que el valor podría ser cualquier otro).

Lo que pretendemos es que pueda realizarse las operaciones:

C c1, c2, c3;

...

C cr = c1 + c2 + c3;

Pero también

float f1, f2, f3;

...

float fr = f1 + 1.2 + c1 + f3;

Aunque la parte interesante se concreta en una sola sentencia, expondremos un ejemplo en formato ejecutable incluyendo su salida como demostración:

#include <iostream>

class Complejo {
  float r, i;           // partes real e imaginaria

  public:
  Complejo(){ r=i=0; }  // constructor por defecto
  Complejo(float R, float I) { r = R; i = I;}
  Complejo operator+(const Complejo& c) {  // operador suma
    Complejo ct;
    ct.r = r+c.r; ct.i = i+c.i;
    return ct;
  }
  operator float () {   // L.13
    return r;
  }
  void show() { std::cout << "C.real " << r << ", C.imag " << i << std::endl;}
};

 

int main() {     // ===========
  int x = 12;
  Complejo c1(1.1, 2.2), c2(3, 4), c3(5.3, 6.4);
  c1.show(); c2.show(); c3.show();
  Complejo c4 = c1 + c2 + c3;    // M4: Ok álgebra de complejos
  c4.show();
  float z = 1.3 + 2.0 + x + c1;  // M6: Ok álgebra de floats
  std::cout << "Suma: " << z << std::endl;
  return 0;
}

Salida

C.real 1.1, C.imag 2.2
C.real 3, C.imag 4
C.real 5.3, C.imag 6.4
C.real 9.4, C.imag 12.6
Suma: 16.4

Comentario:

El ejemplo podría ser más sencillo, pero hemos incluido deliberadamente en la definición de la clase toda la parafernalia necesaria para efectuar la operatoria solicitada a los tipos complejos ("azucar sintáctico" que dirían los entendidos).

La sentencia M4 muestra como es posible realizar estas operaciones con tipos complejos.  A su vez M6 muestra como el objeto c1 es transformado automáticamente por el compilador a un tipo float (siguiendo la equivalencia deseada) en un contexto en el que efectivamente debería haber un float en vez de un tipo Complejo.

El secreto está en la función-operador definida en L13,  cuyo cuerpo incluye una sola sentencia:  la devolución del valor real Complejo::r del objeto (en realidad el cuerpo de la función podría contener cualquier otra computación que fuera necesaria).

Observe que desde la óptica del C++ es un recurso realmente sorprendente.  Aparentemente el cuerpo de la clase Complejo aloja una función-operador que parece una invocación al constructor de la clase float. La función se declara no devolviendo nada (ni siquiera void), aunque finalmente su definición incluye la devolución de un valor;  precisamente la conversión tipo-Complejo a tipo-float que nos interesa.

En estas circunstancias, cada vez que el compilador encuentre un tipo Complejo en un contexto donde sea necesario un float, se creará dicho objeto mediante una invocación a la función

Complejo::operator float();

El lector puede comprobar que el recurso funciona con cualquier otro tipo.  Por ejemplo, puede modificarse el diseño para que el tipo Complejo se comporte como un int que sea la suma de las partes real e imaginaria del complejo haciendo:

operator int () {   // L.13bis
    return r+i;
}

Salida correspondiente a M6:

Suma: 18.3


§3  Otro ejemplo podría ser la utilización de la clase String descrita en la Librería de Ejemplos ( 9.3.2), en situaciones donde se requiere un tipo básico.

Supongamos el proceso de lectura de un fichero con el siguiente aspecto:

FILE* fptr  = fopen(filename, mode);

char buff[250];

fread(buff, 250, 1, fptr);


Sucede que la función fread espera un tipo char* como primer argumento, pero deseamos utilizar un objeto String como buffer de entrada, con lo que el código debería tener el siguiente aspecto:

FILE* fptr  = fopen(filename, mode);

String buff(250);

fread(buff, buff.maxLen(), 1, fptr);


Para proporcionar la conversión automática deseada, basta incluir en la definición de la clase la función-operador adecuada. En este caso habría que insertar el siguiente método público en el cuerpo de la clase:

operator char* () { return cptr; }   // conversor automatico

Nota: se trata de un ejemplo didáctico; no de la forma adecuada de rellenar el objeto buff con el contenido de un fichero. La operación se realiza satisfactoriamente, pero debido a nuestro diseño de la clase String, el miembro buff.ln no concuerda con el nuevo contenido. En una aplicación real estas operaciones hay que implementarlas como métodos de la clase.


Suponiendo un objeto String s1 con un contenido determinado, también serían posibles expresiones como las que siguen (esta vez sin efectos colaterales indeseados sobre los objetos):

String substr = buff.substr(1000,1100);
printf ("Contenido substring: %s\n", substr);

e incluso:

printf ("Contenido substring: %s\n", buff.substr(1000,1100));


  Inicio.


[1]  James O. Coplien  "Advanced C++: Programming Styles and Idioms". Addison-Wesley, Reading, MA. 1992.