c++ - que - punteros void lenguaje c
Cuando convierte un puntero de vacío en un puntero de tipo específico, ¿qué símbolo de colada es mejor, static_cast o reinterpret_cast? (6)
El static_cast
es más apropiado para convertir un void*
a un puntero de algún otro tipo.
static_cast
es el elenco de elección cuando hay una conversión natural e intuitiva entre dos tipos que no necesariamente garantiza que funcione en tiempo de ejecución. Por ejemplo, puede usar static_cast
para convertir punteros de clase base a punteros de clase derivados, que es una conversión que tiene sentido en algunos casos pero no se puede verificar hasta el tiempo de ejecución. De forma similar, puede usar static_cast
para convertir de un int
a un char
, que está bien definido pero puede causar una pérdida de precisión cuando se ejecuta.
reinterpret_cast
, por otro lado, es un operador de casting diseñado para realizar conversiones que, fundamentalmente, no son seguras o no son portátiles. Por ejemplo, puede usar reinterpret_cast
para convertir de un void *
a un int
, que funcionará correctamente si su sistema tiene sizeof (void*)
≤ sizeof (int)
. También puede usar reinterpret_cast
para convertir un float*
en un int*
o viceversa, que es específico de la plataforma porque no se garantiza que las representaciones particulares de float
s e int
tengan algo en común entre sí.
En resumen, si alguna vez te encuentras haciendo una conversión en la que el elenco tiene una lógica significativa pero que no necesariamente tiene éxito en el tiempo de ejecución, evita reinterpret_cast
. static_cast
es una buena opción si tienes algún conocimiento previo de que el elenco funcionará en tiempo de ejecución, y se comunica con el compilador "Sé que esto podría no funcionar, pero al menos tiene sentido y tengo una razón para creer que lo hará hacer correctamente lo correcto en el tiempo de ejecución ". El compilador puede verificar que el modelo se encuentre entre los tipos relacionados, informando un error en tiempo de compilación si este no es el caso. El uso de reinterpret_cast
para hacer esto con conversiones de puntero omite por completo la verificación de seguridad en tiempo de compilación.
Hay algunas circunstancias en las que es posible que desee utilizar un dynamic_cast
lugar de un static_cast
, pero en su mayoría implican conversiones en una jerarquía de clases y (solo en raras ocasiones) directamente se refieren a void*
.
En cuanto a cuál es el preferido por la especificación, ninguno es mencionado demasiado como "el correcto para usar" (o al menos, no recuerdo que uno de ellos sea mencionado de esta manera). Sin embargo, creo que la especificación quiere que usted usa static_cast
sobre reinterpret_cast
. Por ejemplo, cuando se usa un molde estilo C, como en
A* ptr = (A*) myVoidPointer;
El orden de los operadores de conversión que se intenta siempre intenta usar un static_cast
antes de un reinterpret_cast
, que es el comportamiento que desea, ya que no se garantiza que el reinterpret_cast
sea portable.
Posible duplicado:
¿Debo usar static_cast o reinterpret_cast al emitir un vacío * a lo que sea?
En este programa tengo un void *
como parámetro y quiero convertirlo a un tipo específico. Pero no sé qué "símbolo de lanzamiento" usar. Tanto static_cast
como reinterpret_cast
funcionan. ¿Cuál es mejor? ¿Cuál recomienda el estándar C ++?
typedef struct
{
int a;
}A, *PA;
int foo(void* a) // the real type of a is A*
{
A* pA = static_cast<A*>(a); // or A* pA = reinterpret_cast<A*>(a);?
return pA->a;
}
Aquí está
A* pA = static_cast<A*>(a);
o
A* pA = reinterpret_cast<A*>(a);
más apropiado?
Es probable que haya obtenido ese void*
con una conversión implícita, por lo que debe usar static_cast
porque está más cerca de la conversión implícita.
Ninguno. ¿Por qué estás pasando un puntero al vacío en primer lugar? En C es bastante común debido a la falta de alternativas, pero en C ++ casi nunca hay una razón para hacer algo por el estilo.
Editar: @Dan O plantea la posibilidad de utilizar un molde para minimizar el código generado. Para cualquiera que no entienda del todo de qué está hablando, considere algo como std::vector
: cuando defina (por ejemplo) un std::vector<A *>
y un std::vector<B *>
, usted Normalmente terminará con un código completamente separado para cada uno, aunque ambos solo almacenan punteros, por lo que podrían compartir el mismo código. Es posible sortearlo definiendo un contenedor que solo contiene void *
y devolverlo al tipo correcto cuando recupera un artículo del contenedor.
En primer lugar, esto suele ser una optimización prematura en el mejor de los casos (y a menudo puede ser una pesimismo). No obstante, a veces tiene sentido. Sin embargo, cuando lo haga, querrá restringir el lanzamiento a un lugar, pero creando un front-end con plantilla para el contenedor pseudo-genérico de void *
s:
template <class T>
pointer_container {
std::vector <void *> data;
public:
T *operator[](size_t index) { return static_cast<T *>(data[index]); }
};
Ahora bien, es cierto que ahora tenemos un elenco. Y sí, para dar una respuesta directa a la pregunta original, un static_cast
es el correcto para usar en este tipo de situaciones.
En los días de MS-DOS (por ejemplo) código como este era bastante común. Hoy, sin embargo, este estilo de código es completamente inútil.
Ahora, el OP ha dicho que tiene una interfaz fija, y es su trabajo implementarla. No sé dónde trabaja o con quién trabaja, y no he visto el resto del código con el que funciona, así que es cierto que no puedo decir con certeza que lo que están haciendo está mal diseñado y debería ser cambiado. OTOH, basado en mi experiencia pasada, supongo que las posibilidades de que así sea son de al menos un 99%. Como tal, estoy de acuerdo con mi respuesta original. Las respuestas publicadas que acaban de decir "static_cast", sin nada sobre evitar el reparto son las que realmente no ayudan. Si bien es casi imposible que el reparto realmente no pueda evitarse, el simple hecho de su justificación es la reacción incorrecta y la respuesta incorrecta.
Iré más lejos: si una pregunta sobre el lanzamiento que no proporciona evidencia sobre por qué ese lanzamiento es inevitable, el único consejo que alguien debería considerar "bueno" es el consejo de eliminar por completo el lanzamiento. Puede (en raras ocasiones) resultar que no se puede evitar el reparto, pero definitivamente es la excepción, no la regla.
Si está lanzando una jerarquía de clase, use dynamic_cast
- verifica para asegurarse de que el objeto real sea compatible con el tipo al que está lanzando.
Usa static_cast
para esto. Solo en el más raro de los casos raros cuando no hay otra forma use reinterpret_cast
.
reinterpret_cast
convertirá forzosamente el void*
al tipo de datos de destino. No garantiza ninguna seguridad y su programa puede bloquearse ya que el objeto subyacente podría ser cualquier cosa.
Por ejemplo, podría encasillar un myclass*
en void*
y luego usar reinterpret_cast
para convertirlo a su yourclass*
que puede tener un diseño completamente diferente.
Así que es mejor y se recomienda usar static_cast