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.9.9  Modelado de tipos

§1  Sinopsis:

El modelado de tipos ("typecasting") es el proceso de convertir o promover un objeto de un tipo a otro. Esta operación es necesaria y frecuente en programación; incluso es realizada infinidad de veces por el compilador de forma automática y transparente para el programador, y ocurre cuando este la solicita de forma implícita. Por ejemplo, cuando una función en cuyo prototipo se ha declarado que espera un float como argumento y le pasamos un entero, o cuando necesitamos sumar un int con un double. En todos estos casos, el compilador realiza automáticamente una conversión de tipo para que el argumento pasado concuerde con el esperado ( 4.4.6). En el caso de operaciones aritméticas, la conversión intenta conseguir la menor pérdida de precisión posible en las operaciones ( 2.2.5).

En otros casos, que tratamos en este capítulo, la necesidad de modelado es indicada en el código de forma explícita, utilizando los recursos que proporciona el lenguaje con este propósito.

§2  Precauciones:

No olvidar que, cuando forzamos una conversión de tipo explícita o implícita, estamos quebrantando deliberadamente uno de los sistemas de seguridad de C++, basado precisamente en la comprobación de tipos. El modelado puede ser causa de problemas, en especial (aunque no exclusivamente) cuando se trata de punteros. Por tanto, se recomienda usarlo con moderación y solo para resolver necesidades puntuales.

No obstante lo anterior, es evidente que determinadas conversiones, como las que realiza el compilador automáticamente con tipos numéricos, no presentan a veces ningún problema; todo lo más pérdidas de precisión. Sobre todo cuando la conversión se realiza en el sentido de número con más precisión a los de menos. Por ejemplo, el compilador puede realizar automáticamente una conversión de tipo para realizar las asignaciones:

short s = 33;
long n = s;
float f = s;

sin que exista pérdida de precisión, puesto que long y float pueden albergar a todos los short ( 2.2.4). Este tipo de promoción se denomina conversión ensanchante. Sin embargo, las conversiones en el sentido del tipo de más precisión al de menos (conversión estrechante), puede resultar en una pérdida de información o incluso en errores peligrosos. Considere la salida del ejemplo siguiente:

#include <iostream.h>

int main() {
  unsigned long ul = 5000;
  long l = ul; int y = ul; short s = ul;
  cout << "UL = " << ul << endl;
  cout << "L = " << l << endl;
  cout << "Y = " << y << endl;
  cout << "S = " << s << endl;
  ul += 4E9;
  l = ul; y = ul; s = ul;
  cout << "UL = " << ul << endl;
  cout << "L = " << l << endl;
  cout << "Y = " << y << endl;
  cout << "S = " << s << endl;
}

Salida:

UL = 5000
L = 5000
Y = 5000
S = 5000
UL = 4000005000
L = -294962296
Y = -294962296
S = 15240

Tenga en cuenta que la compilación del programa anterior se produce sin problemas, y sin que exista la más mínima advertencia de que pueden darse los estrafalarios resultados de la segunda serie de salidas. Puede figurarse lo que le ocurrirá a su programa si se dan circunstancias parecidas a las anteriores en un proceso de cierta responsabilidad. Por ejemplo, en el cálculo de un puente; en un monitor de electromedicina; el mezclador en una fábrica de alimentos, o simplemente durante la confección de nóminas en el programa de administración de una empresa [2].

Otro ejemplo de conversión estrechante que conduce a un resultado aparentemente contradictorio en 2.2.4a.

Tampoco existe ninguna advertencia respecto de la salida obtenida en el ejemplo de promoción de un entero a variable de enumeración ( 4.9.9b), que fuerza al compilador a proporcionar un resultado evidentemente erróneo.

En general son potencialmente peligrosas las conversiones de tipo cuando ambos no utilizan el mismo tipo de alineación interna ( 4.5.9a). También cuando se trata de punteros ( 4.9.9d)

§3  Dos tipos

Como se ha indicado, en C++ existen dos tipos de modelado: implícito y explícito. El primero es realizado automáticamente por el compilador cuando se mezclan tipos. Por ejemplo, las mentadas conversiones numéricas, que permiten operaciones aritméticas entre tipos distintos. El segundo se realiza cuando el programador utiliza explícitamente el operador de modelado.

Aunque no sea estrictamente necesario, se recomienda como regla de buena práctica, utilizar declaraciones explícitas incluso en los casos de modelado implícito [1]. Sobre todo cuando se quiera poner énfasis en la conversión realizada. Por ejemplo:

int x = 10;
float fl = 10.0;
int y = x + fl;                    // Ok. modelado implícito
int z = x + static_cast<int>(fl);  // Mejor !!

Aparte de que el código resultante es mucho más explícito, esta práctica facilita la depuración de posibles errores derivados de la mixtura de tipos.

§4  Dos estilos

En el C++ Estándar coexisten dos formas de sintácticas para modelado: la nueva y la clásica (heredada de C). Aunque esta última se desaconseja y está considerada como práctica a extinguir ("Deprecated").

Nota: el compilador GNU gcc dispone de la opción -Wold-style-cast, que muestra un mensaje de aviso si se utiliza el viejo estilo.

§4.1  Estilo C++ de modelado

La nueva sintaxis utiliza cuatro palabras clave específicas (static_castreinterpret_castdynamic_cast y const_cast) y una sintaxis que se presta menos a confusión que la clásica (ver a continuación).  Aparte de la anterior, las razones argumentadas en favor del nuevo estilo es que es menos propenso a obtener resultados indeseados y más fácil de localizar su ocurrencia en el código.

§4.1  Estilo clásico de modelado

Es el estilo heredado de C que se mantiene por compatibildad, tiene dos variedades de notación, aunque el nuevo Estándar C++ lo desaconseja en favor del nuevo estilo.

Sintaxis:

(<Nombre-de-tipo>) <expresión>
<Nombre-de-tipo> (<expresión>)

Comentario

El valor de <expresión> se convierte al tipo definido por <nombre-de-tipo> siguiendo las reglas estándar de conversión. Por ejemplo, la función de la Librería Estándar sqrt() espera un double como argumento, si queremos utilizarla con un int n, se puede utilizar cualquiera de las expresiones siguientes:

sqrt((double) n);    // primera forma de la sintaxis
sqrt(double (n));    // segunda forma de la sintaxis

Observe que n no se altera, ya que la conversión se realiza antes de pasar el argumento a la función. Observe también que si los parámetros se han especificado en el prototipo de la función, esta conversión de argumento no sería necesaria, ya que el compilador realiza automáticamente el modelado correspondiente.

Las expresiones que siguen son válidas en C++:

double peso = 25;
int * ptr;
ptr = (int *)&peso;
cout << (long)&peso;    // para que se muestre en decimal
return (int) peso;
func (double  doble) { return (int) doble+3; }


En ocasiones se puede invertir la colocación del paréntesis (segundo caso de la sintaxis), utilizando entonces una notación parecida a las funciones:

return int (peso);
func (double  doble) { return int (doble+3); }


  El modelado de tipos está estrechamente relacionado con los constructores de clases. La posibilidad de realizar un modelado como:

a = A(b);

de un objeto b de tipo B a otro tipo A distinto, depende de como esté definido el constructor de la clase A. En concreto, debe existir un constructor que acepte un objeto tipo B como único argumento. Es decir, debe existir un método como:

A::A(B b) { /* asignación */  }

Ver comentarios y aclaraciones al respecto en:  Constructores de conversión ( 4.11.2d1)

  Tema relacionado
  • Sobrecarga del operador de modelado ( 4.9.18k)

  Inicio.


[1]  En esta, como en muchas otras cuestiones, el Estándar C++ intenta reconducir viejas prácticas y flaquezas del lenguaje (heredados del C) que a lo largo de los años y de millones de líneas de código se han mostrado perniciosas o manifiestamente mejorables, sobre todo desde la óptica de la seguridad.

[2]  Hace años, cuando la tónica general era la más absoluta incultura informática, los responsables de este tipo de desaguisados siempre podían (podíamos) alegar que la culpa era de un fallo "del ordenador". Tras lo que nos quedábamos tan campantes mientras el auditorio asentía con respeto casi religioso. Actualmente creo que las cosas han cambiado. Es posible que tu jefe incluso tenga un "master" en ciencias de la información y ante una chapuza como la anterior seguro que ya no te servirán declaraciones del mismo tenor.