2010-11-05 12 views
6

El reinterpret_cast como sabemos puede convertir cualquier tipo de puntero a cualquier otro tipo de puntero. La pregunta que quiero hacer acerca del operador de conversión son:Pocas dudas sobre operadores de colada en C++

  1. reinterpret_cast ¿Cómo funciona el trabajo, ¿Cuál es la magia (la implementación interna) que permite reinterpret_cast para trabajar?
  2. ¿Cómo garantizar la seguridad al usar reinterpret_cast? Por lo que yo sé, no garantiza una colada segura. Entonces, ¿qué precaución tomar al usar reinterpret_cast?
  3. ¿Cuál es el uso práctico de este operador? Realmente no lo he encontrado en mi experiencia de programación profesional, en la que no podría moverme sin usar este operador. Cualquier ejemplo práctico aparte de lo usual int * to char * será de gran ayuda y apreciado.

otra pregunta con respecto a los operadores de fundición en general:
operadores de fundición (static_cast, dynamic_cast, const_cast, reinterpret_cast) están llamados todos Operators es decir, es lo mejor de mi entendimiento, Por lo que es enunciado correcto para hacer que casting operators cannot be overloaded unlike most other operators (Soy consciente de que no todos los operadores pueden estar sobrecargados y estoy al tanto de cuáles no pueden ser (excepto el QI estoy pidiendo, por favor, no me llame la atención). Solo tenía esta duda de que, como son operadores, ¿qué dice la norma? sobre estos?

+2

¿Qué es un puntero no estándar? –

+0

Este [enlace] (http://www.cplusplus.com/doc/tutorial/typecasting/) podría explicarlo. –

+0

@Konrad: Perdón por un error tipográfico, edité la pregunta. –

Respuesta

4
  1. no hay magia. reinterpret_cast normalmente solo significa (al menos intentar) tratar lo que encuentre en esta dirección como si fuera del tipo que he especificado. El estándar define muy poco sobre lo que hace que podría ser diferente de eso, pero raramente (si es que alguna vez) realmente lo es.
  2. En algunos casos, puede obtener seguridad de algo así como una unión discriminada. Por ejemplo, si está leyendo paquetes de red y lee lo suficiente para ver que lo que ha recibido es un paquete TCP, puede hacer (bastante) con seguridad un reinterpret_cast de IPHdr a TCPHdr (o cualquiera que sea el nombre que haya utilizado)) Sin embargo, el compilador (de nuevo, normalmente) no hará mucho; cualquier seguridad depende de usted para implementarla y hacerla cumplir.
  3. He usado el código como describo en 2), tratando con diferentes tipos de paquetes de red.

Para su última pregunta: puede sobrecargar casting para una clase:

class XXX { 
public: 
    operator YYY() { return whatever; } 
}; 

Esto se puede utilizar para las conversiones en general, sin embargo - ya sea hecho por un static_cast, conversión de estilo C, o incluso una conversión implícita C++ 0x le permite agregar un calificador explicit por lo que no se usará para conversiones implícitas, pero aún no hay manera de diferenciar entre un lanzamiento estático y un lanzamiento de estilo C.

+2

No entiendo la última frase: '* no hay forma de diferenciar entre un elenco de estática y un elenco de estilo de C *'. El problema es que los moldes de estilo C pueden ser 'static_cast' o' reinterpret_cast' (posiblemente incluyendo 'const_cast' con cualquiera de ellos). En todos los casos donde un 'static_cast' es válido, el elenco de estilo C realizará un' static_cast' mientras que cuando no sea posible hará un 'reinterpret_cast'. Si se modifica la const-ness (o volatilidad), entonces incluirá un 'const_cast' en el proceso. –

+0

@dribeas: Solo quise decir que cuando define un operador de conversión de este tipo, puede invocarse mediante un caso de estilo C o un static_cast en el código de cliente, y no puede (por ejemplo) proporcionar una sobrecarga de cómo static_cast se hará, y uno diferente de cómo se realizará un elenco de estilo C. –

+0

En la respuesta a la Q final, se llama "Conversión de operador" y incluso el calificador "explícito" AFAIK está permitido en C++. Mi Q final en realidad significa que puede sobrecargarse, por ejemplo, el operador dynamic_cast? –

2

reinterpret_cast generalmente le permite hacer algunos ve Son cosas malas En el caso de lanzar un puntero, permitirá la conversión de un tipo a otro que no tiene absolutamente ninguna razón para suponer que esto debería funcionar. Es como decir "créeme, realmente quiero hacer esto". Lo que hace exactamente esto es impredecible de un sistema a otro. En su sistema, podría copiar los patrones de bits, mientras que en otro podría transformarlos de alguna manera (potencialmente útil).

p. Ej.

class Foo { 
    int a; 
}; 

class Bar { 
    int a; 
}; 

int main() { 

    Foo a; 

    // No inheritance relationship and not void* so must be reinterpret_cast 
    // if you really want to do it 
    Bar *b = reinterpret_cast<Bar*>(&a); 

    char buffer[sizeof(Bar)]; 

    Bar *c = reinterpret_cast<Bar*>(buffer); // alignment? 

} 

Muy felizmente te dejaré hacer eso, sin importar el escenario. A veces, si estás haciendo manipulaciones de bajo nivel, esto podría ser lo que realmente quieres hacer. (Imagine char * de una conversión de buffer a algo tipo definido por el usuario)

Las posibles fallas son enormes, incluso en el caso más simple como un buffer, donde la alineación puede ser un problema.

Con dlsym() en Linux es útil poder convertir void* en un puntero a función, que de otra manera es un comportamiento indefinido en C++. (¡Algunos sistemas pueden usar espacios de direcciones separados o diferentes punteros de tamaño!). Esto solo se puede hacer con reinterpret_cast en C++.

+0

"Indefinido" es técnicamente la palabra incorrecta. Los tipos de conversiones que 'reinterpret_cast' puede hacer se definen explícitamente en el Estándar, y las asignaciones están definidas por la implementación. –

+0

Pensé que el puntero void */function uno no estaba definido, pero reinterpret_cast aceptaría. – Flexo

0

reinterpret_cast le dice al compilador "se calle, que es una variable de tipo T *" y no hay ninguna seguridad menos que sea realmente una variable de tipo T *. En la mayoría de las implementaciones simplemente no se hace nada: el mismo valor en la variable se pasa al destino.

Su clase puede tener operadores de conversión a cualquier tipo T * y esas conversiones se invocarán implícitamente bajo ciertas condiciones o puede invocarlas explícitamente usando static_cast.

+2

Incluso si realmente es una variable de tipo T *, puede comportarse de manera errónea. Porque si tienes estructura X: Y, Z {} y lanzas una X * a una Z * usando reinterpret_cast y luego usas ese puntero probablemente obtendrás malos resultados y obtendrás un comportamiento indefinido. Aunque X * no es una Z * en una visión muy correcta y estándar, podría causar malentendidos porque muchos considerarán eso porque X es-una Z que X * es-una Z *. –

-1

El reinterpret_cast como sabemos puede lanzar cualquier puntero no estándar a otro puntero no estándar.

Casi, pero no exactamente. Por ejemplo, no puede usar reinterpret_cast para enviar un const int* a un int*. Para eso, necesitas const_cast.

¿Cómo funciona reinterpret_cast, ¿Cuál es la magia (la interna aplicación) que permite reinterpret_cast para trabajar?

No hay magia en absoluto. En definitiva, todos los datos son solo bytes. El sistema de tipo C++ es simplemente una capa de abstracción que le dice al compilador cómo "interpretar" cada byte. Un reinterpret_cast es similar a un simple C-cast, en el sentido de que simplemente dice "al diablo con el sistema de tipos: interpreta estos bytes como tipo X en lugar de tipo Y!"

¿Cómo garantizar la seguridad cuando se usa reinterpret_cast? Por lo que yo sé, no garantiza la colada segura, entonces ¿qué precaución tomar mientras usa reinterpret_cast?

Bueno, reinterpret_cast es intrínsecamente peligroso. No deberías usarlo a menos que realmente sepas lo que estás haciendo. Trate de usar static_cast en su lugar. El sistema de tipo C++ lo protegerá de hacer algo demasiado peligroso si usa static_cast.

¿Cuál es el uso práctico de este operador. En realidad no he encontrado en mi experiencia profesional programación, en donde me could'nt moverse sin necesidad de utilizar esta operator.Any ejemplos prácticos, aparte de lo habitual int * a char * habrá muy útiles y apreciadas.

Tiene muchos usos, pero por lo general estos usos son algo "avanzados". Por ejemplo, si está creando una agrupación de memoria de bloques vinculados y almacenando punteros para liberar bloques en los bloques, necesitará reinterpret_cast un bloque de T* a T** para interpretar el bloque como un puntero al bloque siguiente , en lugar de un bloque en sí mismo.

+1

Tu segundo párrafo es incorrecto. 'reinterpret_cast' puede o no cambiar la representación (Estándar 5.2.10/3), y la asignación está puramente definida por la implementación. Considere la conversión de C de 'int' a' double' en comparación con la conversión de 'int *' a 'int'. –

+2

No. Un reinterpret_cast no es como un C-cast. –

+0

La principal diferencia es que reinterpret_cast no descarta la constness, lo que noté. Aparte de eso, decir que reinterpret_cast cast es similar a un C-cast simple, en el sentido de que simplemente puedes "reinterpretar" un patrón de bits como un tipo diferente al convertir un tipo de puntero a otro tipo de puntero, parece perfectamente válido.Las diferencias son lo suficientemente sutiles como para usar la palabra "similar" aquí. Aunque el estándar de C++ permite que las implementaciones alteren el patrón de bits en un reinterpret_cast, en última instancia, la conversión entre dos tipos no relacionados es igualmente peligrosa. –

-1

reinterpret_cast es bastante equivalente a un elenco de estilo C. No garantiza nada; está ahí para permitirte hacer lo que necesites, con la esperanza de que sepas lo que estás haciendo.

Si está buscando garantizar la seguridad, use dynamic_cast, ya que eso es lo que hace. Si el elenco no se puede completar de forma segura, dynamic_cast devuelve NULL o nullptr (C++ 0x).

La conversión a través de los "operadores de conversión" como static_cast, dynamic_cast, etc. no puede sobrecargarse. conversiones recta puede, por ejemplo:

class Degrees 
{ 
public: 
    operator double() { ... } 
}; 
+2

Un reinterpret_cast no es en absoluto equivalente a un elenco de estilo C. Un elenco estilo C puede dar como resultado un static_cast y realmente puede hacer un par de cosas muy raras que son imposibles con cualquier lanzamiento de estilo nuevo. –

1
  1. reinterpret_cast sólo funciona en punteros.La forma en que funciona es que deja el valor del puntero solo y cambia el tipo de información asumida al respecto. Dice: "Sé que estos tipos no son equivalentes, pero quiero que pretendas que esto ahora es un puntero a T2". Por supuesto, esto puede causar cualquier cantidad de problemas si usa el puntero T2 y no apunta a un T2.

  2. Existen muy pocas garantías sobre reinterpret_cast, por lo que debe evitarse. En realidad, solo puedes lanzar de T1 a T2 y luego volver a T1 y saber que, dadas algunas suposiciones, el resultado final será el mismo con el que comenzaste.

  3. El único en el que puedo pensar es convertir un char * en un char * sin firmar. Sé que la representación subyacente es la misma en mi implementación, así que sé que el reparto es seguro. Sin embargo, no puedo usar un molde estático porque es un puntero a un búfer. En realidad, encontrará muy poco uso legítimo de reinterpret_cast en el mundo real.

Sí, son operadores. AFAIK no puedes anularlos.

+2

Hola Noah. Mucho tiempo sin verte :-). Nit re (1): reinterpret_cast también funciona muy bien en las referencias. Nit re (2): también hay una garantía sobre reinterpret_cast hacia/desde el primer miembro de una estructura POD. Re (3), no es una queja: vea mi respuesta (un ejemplo es 'QueryInterface' de Microsoft). Cheers, –

+1

Sí, si usas bibliotecas C y/o C++ de MS, probablemente vas a tener que usar reinterpret_cast mucho debido al uso de long como el tipo vago en lugar de vacío *. La mayoría de las otras bibliotecas de C que he usado, sin embargo, usaron void *. No conozco la historia ni los motivos por los que MS eligió el método long, pero puedes static_cast a/from void * (aunque en realidad no es mucho más seguro). Supongo que no me di cuenta de que funcionaba con referencias ... nunca lo hice. Y sí, no conozco todas las reglas a menos que sea necesario. –

2

En primer lugar, no está claro qué quiere decir con "puntero no estándar". Creo que tu premisa es defectuosa. Afortunadamente, no parece afectar las preguntas.

"¿Cómo funciona [esto]?" Bueno, la intención, como se puede adivinar por el nombre, es simplemente cambiar la interpretación de un patrón de bits, tal vez extendiendo o cortocircuitando según corresponda. Este es un tipo de cambio de tipo en el que el patrón de bits no se modifica, pero la interpretación y, por lo tanto, el valor conceptual se modifican. Y está en contraste con un tipo de cambio de tipo donde se guarda el valor conceptual (por ejemplo, int convertido a double) mientras que el patrón de bits se cambia según sea necesario para mantener el valor conceptual. Pero la mayoría de los casos de reinterpret_cast tienen un efecto de implementación definido, por lo que para esos casos su compilador puede hacer lo que quiera, no necesariamente manteniendo el patrón de bits, siempre que esté documentado.

"Cómo garantizar la seguridad" Eso se trata de saber lo que hace su compilador, y sobre evitandoreinterpret_cast. :-)

"¿Cuál es el uso práctico". En su mayoría se trata de recuperar información de tipo que se ha perdido en el código orientado a C, donde los punteros void* se usan para simular el polimorfismo.

Saludos & HTH.,

+1

Las conversiones de 'void *' no requieren 'reinterpret_cast', sino' static_cast' (que es tan malo como 'reinterpret_cast' en este caso particular, pero * different * de todos modos) –

+0

Excepto que puedes static_cast a/desde el vacío *, ¿no? El problema con la interfaz de MS es que usan enteros en lugar de void *. ¿No es el static_cast a/from void * también 'ligeramente' más seguro también, ya que está garantizado para tomar el comienzo del objeto en lugar de simplemente cambiar el tipo de puntero? Supongo que puede haber casos para ambos métodos, ya que lanzar desde un vacío * a una subclase en un derivado de IM sería malo con static_cast. La mayoría de las veces parece recordar haber usado static_cast. –

+0

@David: hasta donde llega. Estaba pensando principalmente en casos como el de ['QueryInterface'] de Microsoft (http://msdn.microsoft.com/en-us/library/ms682521%28VS.85%29.aspx). La conversión directa de 'void **' a 'T **' requiere 'reinterpret_cast'. Cheers, –

0

He usado reinterpret_cast mucho en la programación de Windows. El manejo de mensajes usa los parámetros WPARAM y LPARAM que necesitan conversión a los tipos correctos.

1

Un uso "práctico" de reinterpret_cast.

Tengo una clase donde los miembros no están destinados a ser leídos. Ejemplo a continuación

class ClassWithHiddenVariables 
{ 
private: 
    int a; 
    double m; 
public: 
    void SetVariables(int s, int d) 
    { 
     a = s; 
     m = d; 
    } 
}; 

Esta clase se utiliza en miles de lugares en una aplicación sin problemas.

Ahora, por alguna razón quiero ver a los miembros en una parte específica. Sin embargo, no quiero tocar la clase existente. De modo que rompa las reglas de la siguiente manera.

Cree otra clase con el mismo patrón de bits y visibilidad pública. Aquí la clase original contiene un int y un doble.

class ExposeAnotherClass 
{ 
public: 
    int a_exposed; 
    double m_exposed; 
}; 

Cuando se quiere ver a miembros de las ClassWithHiddenVariables objeto, utilice reinterpret_cast para echar a ExposeAnotherClass. El ejemplo sigue a

ClassWithHiddenVariables obj; 
obj.SetVariables(10, 20.02);  
ExposeAnotherClass *ptrExposedClass; 
ptrExposedClass = reinterpret_cast<ExposeAnotherClass*>(&obj); 
cout<<ptrExposedClass->a_exposed<<"\n"<<ptrExposedClass->m_exposed; 

No creo que esta situación ocurra en la vida real. Pero esto es solo una explicación de reinterpret_cast que considera los objetos como patrones de bits.

+0

Bonita solución :) –

+0

@Als, gracias. Si pensamos más sobre esto, podemos ver que reinterpret_cast debería estar ahí en C++ para "completarse". – Jimmy