tuto try operator dev const_cast cast c++ casting

try - int to string c++



Reinterpretar_cast contra estilo C (5)

Ambos modelos, reinterpret_cast y c-style, están definidos por implementación y hacen casi lo mismo. Las diferencias son:
1. reinterpret_cast no puede eliminar la constness. Por ejemplo :

const unsigned int d = 5; int *g=reinterpret_cast< int* >( &d );

emitirá un error:

error: reinterpret_cast from type ''const unsigned int*'' to type ''int*'' casts away qualifiers

2. Si usa reinterpret_cast , es fácil encontrar los lugares donde lo hizo. No es posible hacerlo con moldes de estilo c

Escuché que reinterpret_cast es implementación definida, pero no sé lo que realmente significa. ¿Puedes dar un ejemplo de cómo puede salir mal y sale mal? ¿Es mejor utilizar el molde C-Style?


Es la implementación definida en un sentido que el estándar no prescribe (casi) cómo deberían verse los diferentes tipos de valores en un nivel de bit, cómo debería estructurarse el espacio de direcciones, etc. Entonces, realmente es una plataforma específica para conversiones como:

double d; int &i = reinterpret_cast<int&>(d);

Sin embargo, como dice la norma

No debe sorprender a quienes conocen la estructura de direccionamiento de la máquina subyacente.

Entonces, si sabes lo que haces y cómo se ve todo en un nivel bajo, nada puede salir mal.

El elenco de estilo C es similar en cierto sentido que puede realizar reinterpret_cast, pero también "prueba" static_cast primero y puede descartar la calificación de cv (mientras que static_cast y reinterpret_cast no) y realizar conversiones sin tener en cuenta el control de acceso (ver 5.4 / 4 en C ++ 11 estándar). P.ej:

#include <iostream> using namespace std; class A { int x; }; class B { int y; }; class C : A, B { int z; }; int main() { C c; // just type pun the pointer to c, pointer value will remain the same // only it''s type is different. B *b1 = reinterpret_cast<B *>(&c); // perform the conversion with a semantic of static_cast<B*>(&c), disregarding // that B is an unaccessible base of C, resulting pointer will point // to the B sub-object in c. B *b2 = (B*)(&c); cout << "reinterpret_cast:/t" << b1 << "/n"; cout << "C-style cast:/t/t" << b2 << "/n"; cout << "no cast:/t/t" << &c << "/n"; }

y aquí hay una salida de ideone:

reinterpret_cast: 0xbfd84e78 C-style cast: 0xbfd84e7c no cast: 0xbfd84e78

tenga en cuenta que el valor producido por reinterpret_cast es exactamente el mismo que una dirección de ''c'', mientras que el molde de estilo C dio como resultado un puntero de desplazamiento correcto.


Existen razones válidas para usar reinterpret_cast y, por estos motivos, el estándar realmente define lo que sucede.

La primera es utilizar tipos de punteros opacos, ya sea para una API de biblioteca o simplemente para almacenar una variedad de punteros en una sola matriz (obviamente junto con su tipo). Puede convertir un puntero a un entero de tamaño adecuado y luego volver a un puntero y será exactamente el mismo puntero. Por ejemplo:

T b; intptr_t a = reinterpret_cast<intptr_t>( &b ); T * c = reinterpret_cast<T*>(a);

En este código, c se garantiza que apunta al objeto b como esperabas. La conversión a un tipo de puntero diferente es, por supuesto, indefinida (más o menos).

Se permiten conversiones similares para punteros de función y punteros de función de miembro, pero en este último caso puede lanzar hacia / desde otro puntero de función miembro simplemente para tener una variable que es grande enouhg.

El segundo caso es para usar tipos de diseño estándar. Esto es algo que fue de factor admitido antes de C ++ 11 y ahora se ha especificado en el estándar. En este caso, el estándar trata reinterpret_cast como static_cast para void * primero y luego un static_cast para el tipo de desinación. Esto se usa mucho al hacer protocolos binarios donde las estructuras de datos a menudo tienen la misma información de encabezado y le permiten convertir tipos que tienen el mismo diseño, pero difieren en la estructura de clases de C ++.

En ambos casos, debe usar el operador explícito reinterpret_cast lugar del C-Style. Aunque el estilo C normalmente haría lo mismo, corre el riesgo de estar sujeto a operadores de conversión sobrecargados.


El elenco estilo C no es mejor.

Simplemente prueba varios moldes de estilo C ++ en orden, hasta que encuentra uno que funcione. eso significa que cuando actúa como reinterpret_cast , tiene exactamente los mismos problemas que reinterpret_cast . Pero además, tiene estos problemas:

  • puede hacer muchas cosas diferentes, y no siempre está claro al leer el código qué tipo de const_cast se invocará (puede comportarse como un reinterpret_cast , un const_cast o un static_cast , y eso hace cosas muy diferentes),
  • de manera similar, cambiar el código circundante podría cambiar el significado del elenco,
  • es difícil de buscar ( reinterpret_cast es fácil de encontrar, lo cual es bueno, porque los moldes son feos y se debe prestar atención cuando se usan. Pero un molde de estilo C, como en (int)42.0 , es mucho más difícil de encontrar al buscar )

Para responder la otra parte de su pregunta, sí, reinterpret_cast está definido por la implementación. Esto significa que cuando lo usa para convertir de, por ejemplo, un int* a un float* , no tiene garantía de que el puntero resultante apunte a la misma dirección. Esa parte está definida por la implementación. Pero si toma el float* resultante float* y lo vuelve a reinterpret_cast en un int* , obtendrá el puntero original. Esa parte está garantizada.

Pero, nuevamente, recuerde que esto es cierto ya sea que use reinterpret_cast o un molde de estilo C:

int i; int* p0 = &i; float* p1 = (float*)p0; // implementation-defined result float* p2 = reinterpret_cast<float*>(p0); // implementation-defined result int* p3 = (int*)p1; // guaranteed that p3 == p0 int* p4 = (int*)p2; // guaranteed that p4 == p0 int* p5 = reinterpret_cast<int*>(p1); // guaranteed that p5 == p0 int* p6 = reinterpret_cast<int*>(p2); // guaranteed that p6 == p0


C ++ tiene tipos, y la única forma en que normalmente se convierten entre sí es mediante operadores de conversión bien definidos que usted escribe. En general, eso es todo lo que ambos necesitan y deben usar para escribir sus programas.

A veces, sin embargo, desea reinterpretar los bits que representan un tipo en otra cosa. Esto generalmente se usa para operaciones de muy bajo nivel y no es algo que deba usar normalmente. Para esos casos, puede usar reinterpret_cast .

Se define la implementación porque el estándar de C ++ realmente no dice mucho acerca de cómo las cosas realmente deben ser presentadas en la memoria. Eso está controlado por su implementación específica de C ++. Debido a esto, el comportamiento de reinterpret_cast depende de cómo el compilador establece las estructuras en la memoria y cómo implementa reinterpret_cast .

Los moldes de estilo C son bastante similares a reinterpret_cast s, pero tienen mucha menos sintaxis y no se recomiendan. Se piensa que el casting es inherentemente una operación fea y requiere una sintaxis fea para informar al programador que algo dudoso está sucediendo.

Un ejemplo fácil de cómo podría salir mal:

std::string a; double* b; b = reinterpret_cast<double*>(&a); *b = 3.4;

El comportamiento de ese programa no está definido: un compilador puede hacer lo que quiera con eso. Lo más probable es que tenga un bloqueo cuando se llame al destructor de la string , ¡pero quién sabe! Podría corromper la pila y provocar un bloqueo en una función no relacionada.