Escuché que la static_cast
función debería preferirse al estilo C o al simple estilo de conversión de funciones. ¿Es esto cierto? ¿Por qué?
Escuché que la static_cast
función debería preferirse al estilo C o al simple estilo de conversión de funciones. ¿Es esto cierto? ¿Por qué?
Respuestas:
La razón principal es que los moldes clásicos C no hacen distinción entre lo que llamamos static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
, y dynamic_cast<>()
. Estas cuatro cosas son completamente diferentes.
A static_cast<>()
suele ser seguro. Hay una conversión válida en el lenguaje, o un constructor apropiado que lo hace posible. La única vez que es un poco arriesgado es cuando echas abajo a una clase heredada; debes asegurarte de que el objeto sea realmente el descendiente que afirmas que es, por medios externos al lenguaje (como una bandera en el objeto). A dynamic_cast<>()
es seguro siempre que se verifique el resultado (puntero) o se tenga en cuenta una posible excepción (referencia).
A reinterpret_cast<>()
(o a const_cast<>()
) por otro lado siempre es peligroso. Le dice al compilador: "confía en mí: sé que esto no se parece a un foo
(parece que no es mutable), pero lo es".
El primer problema es que es casi imposible saber cuál ocurrirá en un reparto de estilo C sin mirar piezas de código grandes y dispersas y sin conocer todas las reglas.
Asumamos esto:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Ahora, estos dos se compilan de la misma manera:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
Sin embargo, veamos este código casi idéntico:
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
Como puede ver, no hay una manera fácil de distinguir entre las dos situaciones sin saber mucho sobre todas las clases involucradas.
El segundo problema es que los modelos de estilo C son demasiado difíciles de localizar. En expresiones complejas puede ser muy difícil ver modelos de estilo C. Es prácticamente imposible escribir una herramienta automatizada que necesite localizar modelos de estilo C (por ejemplo, una herramienta de búsqueda) sin un compilador de C ++ completo. Por otro lado, es fácil buscar "static_cast <" o "reinterpret_cast <".
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
Eso significa que, no solo los lanzamientos de estilo C son más peligrosos, sino que es mucho más difícil encontrarlos a todos para asegurarse de que sean correctos.
static_cast
para rechazar una jerarquía de herencia, sino más bien dynamic_cast
. Eso devolverá el puntero nulo o un puntero válido.
static_cast
en esa situación. dynamic_cast
puede ser más seguro, pero no siempre es la mejor opción. A veces se sabe que un puntero apunta a un subtipo dado, por medio opaco al compilador, y a static_cast
es más rápido. En al menos algunos entornos, dynamic_cast
requiere soporte de compilador opcional y costo de tiempo de ejecución (habilitando RTTI), y es posible que no desee habilitarlo solo para un par de comprobaciones que puede hacer usted mismo. El RTTI de C ++ es solo una posible solución al problema.
static_cast
. El equivalente de C reinterpret_cast
es *(destination_type *)&
, es decir, tomar la dirección del objeto, convertir esa dirección en un puntero a un tipo diferente y luego desreferenciarla. Excepto en el caso de los tipos de caracteres o ciertos tipos de estructuras para los cuales C define el comportamiento de esta construcción, generalmente da como resultado un comportamiento indefinido en C.
int
(y int
solo), por qué usar static_cast<int>
vs. (int)
como el único beneficio parece ser con variables de clase y punteros. Solicite que elabore sobre esto.
int
dynamic_cast
no se aplica, pero todas las otras razones se mantienen. Por ejemplo: digamos que v
es un parámetro de función declarado como float
, entonces (int)v
es static_cast<int>(v)
. Pero si cambia el parámetro a float*
, (int)v
en silencio se convierte reinterpret_cast<int>(v)
, mientras que static_cast<int>(v)
es ilegal y correctamente capturado por el compilador.
Un consejo pragmático: puede buscar fácilmente la palabra clave static_cast en su código fuente si planea ordenar el proyecto.
int
parámetro.
En resumen :
static_cast<>()
le da una capacidad de comprobación de tiempo de compilación, el elenco de C-Style no.static_cast<>()
se puede detectar fácilmente en cualquier lugar dentro de un código fuente de C ++; en contraste, el molde C_Style es más difícil de detectar.- Las intenciones se transmiten mucho mejor usando moldes C ++.
Más explicaciones :
La conversión estática realiza conversiones entre tipos compatibles . Es similar al elenco de estilo C, pero es más restrictivo. Por ejemplo, el reparto de estilo C permitiría que un puntero entero apunte a un carácter.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
Dado que esto da como resultado un puntero de 4 bytes que apunta a 1 byte de memoria asignada, escribir en este puntero provocará un error de tiempo de ejecución o sobrescribirá alguna memoria adyacente.
*p = 5; // run-time error: stack corruption
A diferencia de la conversión de estilo C, la conversión estática permitirá que el compilador verifique que los tipos de datos de puntero y puntero sean compatibles, lo que permite al programador detectar esta asignación de puntero incorrecta durante la compilación.
int *q = static_cast<int*>(&c); // compile-time error
Lea más sobre:
¿Cuál es la diferencia entre static_cast <> y C estilo casting
y
Regular cast vs. static_cast vs. dynamic_cast
static_cast<>()
sea más legible. Quiero decir, a veces lo es, pero la mayoría de las veces, especialmente en tipos enteros básicos, es horrible e innecesariamente detallado. Por ejemplo: esta es una función que intercambia los bytes de una palabra de 32 bits. Sería casi imposible leer usando static_cast<uint##>()
yesos, pero es bastante fácil de entender usando (uint##)
yesos. Imagen del código: imgur.com/NoHbGve
always
tampoco dije nada . (pero la mayoría de las veces sí) Seguro que hay casos en los que el reparto de estilo c es mucho más legible. Esa es una de las razones por las que el estilo c casting todavía está en vivo y pateando en c ++ imho. :) Por cierto, ese fue un muy buen ejemplo
(uint32_t)(uint8_t)
) para lograr que los bytes además del más bajo se restablezcan. Para eso hay bit a bit y ( 0xFF &
). El uso de moldes está ofuscando la intención.
La pregunta es más grande que simplemente usar wither static_cast o el estilo C porque hay diferentes cosas que suceden cuando se usan los moldes de estilo C. Los operadores de conversión de C ++ están destinados a hacer estas operaciones más explícitas.
En la superficie, las conversiones de estilo static_cast y C aparecen de la misma manera, por ejemplo, al convertir un valor a otro:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Ambos arrojan el valor entero a un doble. Sin embargo, cuando se trabaja con punteros, las cosas se vuelven más complicadas. algunos ejemplos:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
En este ejemplo (1) tal vez está bien porque el objeto señalado por A es realmente una instancia de B. Pero, ¿qué sucede si en ese punto del código no sabe a qué apunta realmente? (2) puede ser perfectamente legal (solo desea mirar un byte del entero), pero también podría ser un error en cuyo caso un error sería bueno, como (3). Los operadores de conversión de C ++ están destinados a exponer estos problemas en el código al proporcionar errores en tiempo de compilación o tiempo de ejecución cuando sea posible.
Por lo tanto, para una "conversión de valor" estricta, puede usar static_cast. Si desea la conversión polimórfica de punteros en tiempo de ejecución, use dynamic_cast. Si realmente quiere olvidarse de los tipos, puede usar reintrepret_cast. Y para lanzar const por la ventana hay const_cast.
Simplemente hacen que el código sea más explícito para que parezca que sabes lo que estabas haciendo.
static_cast
significa que no puedes accidentalmente const_cast
o reinterpret_cast
, lo cual es algo bueno.
Ver introducción efectiva de C ++
Se trata de cuánta seguridad de tipografía desea imponer.
Cuando escribe (bar) foo
(que es equivalente a reinterpret_cast<bar> foo
si no ha proporcionado un operador de conversión de tipo) le está diciendo al compilador que ignore la seguridad de tipo, y simplemente haga lo que se le dice.
Cuando escribe, static_cast<bar> foo
le está pidiendo al compilador que al menos verifique que la conversión de tipos tenga sentido y, para los tipos integrales, que inserte algún código de conversión.
EDITAR 2014-02-26
Escribí esta respuesta hace más de 5 años, y me equivoqué. (Véanse los comentarios.) ¡Pero todavía recibe votos positivos!
static_cast<bar>(foo)
, entre paréntesis. Lo mismo para reinterpret_cast<bar>(foo)
.
Los moldes de estilo C son fáciles de perder en un bloque de código. Los modelos de estilo C ++ no solo son una mejor práctica; Ofrecen un mayor grado de flexibilidad.
reinterpret_cast permite conversiones de tipo integral a puntero, sin embargo, puede ser inseguro si se usa incorrectamente.
static_cast ofrece una buena conversión para tipos numéricos, por ejemplo, desde enumeraciones a entradas o entradas a flotantes o cualquier tipo de datos de los que tenga confianza. No realiza ninguna comprobación de tiempo de ejecución.
Dynamic_cast, por otro lado, realizará estas comprobaciones marcando cualquier asignación o conversión ambigua. Solo funciona en punteros y referencias e incurre en una sobrecarga.
Hay un par de otros, pero estos son los principales con los que te encontrarás.
static_cast, además de manipular punteros a clases, también se puede usar para realizar conversiones definidas explícitamente en clases, así como para realizar conversiones estándar entre tipos fundamentales:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, sin embargo, cuando (int)d
es mucho más conciso y legible? (Quiero decir, en el caso de los tipos básicos, no de punteros a objetos).
(int)d
cuando int{d}
es mucho más legible? La ()
sintaxis de constructor, o de función, si la tiene , no es tan rápida para convertirse en un laberinto de paréntesis de pesadilla en expresiones complejas. En este caso, sería en int i{d}
lugar de int i = (int)d
. Mucho mejor OMI. Dicho esto, cuando solo necesito un temporal en una expresión, uso static_cast
y nunca he usado moldes de constructor, no lo creo. Solo lo uso (C)casts
cuando escribo apresuradamente depuración cout
s ...