c++ - sirven - Forma correcta de lanzar tipos de puntero
punteros a cadenas (3)
Deberías static_cast
. Utilice static_cast
en los casos en que está deshaciendo una conversión implícita.
En este caso particular, sin embargo, no hay diferencia porque está convirtiendo de void*
. Pero, en general, la reinterpret_cast
entre dos punteros de objeto se define como (§5.2.10 / 7):
Un puntero de objeto se puede convertir explícitamente a un puntero de objeto de un tipo diferente. Cuando un prvalue
v
de tipo "puntero aT1
" se convierte al tipo "puntero a cvT2
", el resultado esstatic_cast<cv T2*>(static_cast<cv void*>(v))
si ambosT1
yT2
son estándar -los tipos de instalación y los requisitos de alineación deT2
no son más estrictos que los deT1
, o si cualquiera de los tipos esvoid
. Convirtiendo un prvalue de tipo "puntero aT1
" al tipo "puntero aT2
" (dondeT1
yT2
son tipos de objetos y donde los requisitos de alineación deT2
no son más estrictos que los deT1
) y regresa a su tipo original, se obtiene el original valor del puntero El resultado de cualquier otra conversión de puntero no se especifica.
Énfasis mío Como T1
para ti ya está void*
, el lanzamiento al void*
en reinterpret_cast
no hace nada. Esto no es cierto en general, que es lo que dice Drew Dormann :
#include <iostream>
template <typename T>
void print_pointer(const volatile T* ptr)
{
// this is needed by oversight in the standard
std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl;
}
struct base_a {};
struct base_b {};
struct derived : base_a, base_b {};
int main()
{
derived d;
base_b* b = &d; // implicit cast
// undo implicit cast with static_cast
derived* x = static_cast<derived*>(b);
// reinterpret the value with reinterpret_cast
derived* y = reinterpret_cast<derived*>(b);
print_pointer(&d);
print_pointer(x);
print_pointer(y);
}
Salida:
00CBFD5B
00CBFD5B
00CBFD5C
(Tenga en cuenta que porque y
realidad no apunta a un derived
, usarlo es un comportamiento indefinido).
Aquí, reinterpret_cast
viene con un valor diferente porque pasa por void*
. Es por eso que debes usar static_cast
cuando puedas, y reinterpret_cast
cuando sea necesario.
Teniendo en cuenta el siguiente código (y el hecho de que VirtualAlloc()
devuelve un void*
):
BYTE* pbNext = reinterpret_cast<BYTE*>(
VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE));
¿Por qué se elige reinterpret_cast
lugar de static_cast
?
Yo solía pensar que reinterpret_cast
está bien para, por ejemplo, lanzar punteros DWORD_PTR
desde tipos enteros (como por ejemplo, DWORD_PTR
), pero para convertir de un void*
a un BYTE*
, ¿no static_cast
bien el static_cast
?
¿Hay alguna diferencia (¿sutil?) En este caso en particular, ¿o son solo dos lanzamientos de puntero válidos?
¿El estándar C ++ tiene una preferencia para este caso, sugiriendo una forma en lugar de la otra?
El uso de static_cast
para lanzar un puntero static_cast
desde void*
está garantizado para preservar la dirección.
reinterpret_cast
por otro lado, garantiza que si se transfiere el puntero de un tipo a otro y vuelve al tipo original, la dirección se conserva.
Aunque con la mayoría de las implementaciones, vería los mismos resultados en el uso de cualquiera de estos, se preferiría static_cast
.
Y con C++11
, recuerdo que usar reinterpret_cast
para void*
tiene un comportamiento bien definido. Antes de eso, este comportamiento estaba prohibido.
It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.
Resolución propuesta (agosto de 2010):
Cambie 5.2.10 [expr.reinterpret.cast] párrafo 7 de la siguiente manera:
Un puntero de objeto se puede convertir explícitamente a un puntero de objeto de un tipo diferente. Cuando un prvalue v de tipo "puntero a T1" se convierte al tipo "puntero a cv T2", el resultado es static_cast (static_cast (v)) si tanto T1 como T2 son tipos de disposición estándar (3.9 [basic.types] ) y los requisitos de alineación de T2 no son más estrictos que los de T1, o si cualquiera de los tipos es nulo.
Convirtiendo un prvalue de tipo "puntero a T1" al tipo "puntero a T2" (donde T1 y T2 son tipos de objetos y donde los requisitos de alineación de T2 no son más estrictos que los de T1) y regresa a su tipo original, se obtiene el original valor del puntero El resultado de cualquier otra conversión de puntero no está especificado.
Más información here .
Gracias a Jesse Good por el enlace.
Para punteros a tipos fundamentales ambos moldes tienen el mismo significado; así que tienes razón de que static_cast
está bien.
Al convertir entre algunos tipos de punteros, es posible que la dirección de memoria específica mantenida en el puntero necesite cambiar .
Ahí es donde los dos moldes difieren. static_cast
hará el ajuste apropiado. reinterpret_cast
no lo hará.
Por esa razón, es una buena regla general para static_cast
entre los tipos de punteros a menos que sepa que se desea reinterpret_cast
.