Pregunta ¿Por qué usar static_cast (x) en lugar de (int) x?


He oído que el static_cast la función debe preferirse al estilo C o al estilo de función simple. ¿Es esto cierto? ¿Por qué?


529
2017-09-19 16:33


origen


Respuestas:


La razón principal es que los cilindros C clásicos no hacen distinción entre lo que llamamos static_cast<>(), reinterpret_cast<>(), const_cast<>()y dynamic_cast<>(). Estas cuatro cosas son completamente diferentes.

UN static_cast<>() por lo general es seguro. Hay una conversión válida en el idioma, o un constructor apropiado que lo hace posible. La única vez que es un poco arriesgado es cuando recurres a una clase heredada; debe asegurarse de que el objeto es realmente el descendiente que usted reclama que es, por medios externos al idioma (como una bandera en el objeto). UN dynamic_cast<>() es seguro siempre que se compruebe el resultado (puntero) o se tenga en cuenta una posible excepción (referencia).

UN reinterpret_cast<>() (o un const_cast<>()) por otro lado siempre es peligroso. Le dices al compilador: "confía en mí: sé que esto no se ve como un foo (Parece que no es mutable), pero es ".

El primer problema es que es casi imposible saber cuál ocurrirá en un elenco de estilo C sin mirar en grande y dispersar fragmentos de código y conocer todas las reglas.

Supongamos que estos:

class CMyClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere

Ahora, estos dos se compilan de la misma manera:

CMyClass *pMyObject;
pMyObject = static_cast<CMyClass*>(pSomething); // Safe; as long as we checked

pMyObject = (CMyClass*)(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 moldes de estilo C son demasiado difíciles de localizar. En expresiones complejas, puede ser muy difícil ver moldes de estilo C. Es virtualmente imposible escribir una herramienta automatizada que necesite ubicar moldes de estilo C (por ejemplo, una herramienta de búsqueda) sin un front-end de 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 modelos de estilo C son más peligrosos, sino que es mucho más difícil encontrarlos para asegurarse de que son correctos.


528
2017-09-19 17:23



Un consejo práctico: puede buscar fácilmente la palabra clave static_cast en su código fuente si planea ordenar el proyecto.


102
2017-09-19 16:59



En breve:

  1. static_cast<>() le da una habilidad de comprobación de tiempo de compilación, estilo C   el reparto no.
  2. static_cast<>() se puede detectar fácilmente   en cualquier lugar dentro de un código fuente de C ++; en cambio, el molde C_Style es más difícil de detectar.
  3. Las intenciones se transmiten mucho mejor usando moldes de C ++.

Más Explicación:

El reparto estático realiza conversiones entre tipos compatibles. Eso   es similar al lanzamiento de estilo C, pero es más restrictivo. Por ejemplo,   el elenco estilo C permitiría que un puntero entero apunte a un char.

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 asignado   memoria, escribir en este puntero provocará un error en tiempo de ejecución o   sobrescribirá algunos memoria adyacente.

*p = 5; // run-time error: stack corruption

A diferencia del elenco estilo C, el elenco estático permitirá   compilador para comprobar que los tipos de datos de puntero y de punta son   compatible, lo que le permite al programador detectar esto incorrectamente   asignación del puntero durante la compilación.

int *q = static_cast<int*>(&c); // compile-time error

Lea más en:
¿Cuál es la diferencia entre la fundición estática <> y el estilo C?
y
Lanzamiento regular vs. static_cast vs. dynamic_cast  


55
2017-10-09 02:12



La pregunta es más grande que simplemente usar wither static_cast o C style casting porque hay cosas diferentes que suceden cuando se usan lanzamientos de estilo C. Los operadores de colada de C ++ están destinados a hacer que estas operaciones sean más explícitas.

En la superficie, los estilos de fundido estático y de estilo C parecen ser los mismos, por ejemplo cuando se transfiere 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) quizás esté bien porque el objeto al que apunta A es en realidad una instancia de B. Pero, ¿qué sucede si no sabes en ese punto del código lo que realmente apunta? (2) tal vez perfectamente legal (solo quiere 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 colación C ++ están destinados a exponer estos problemas en el código al proporcionar errores en tiempo de compilación o en tiempo de ejecución cuando sea posible.

Por lo tanto, para un "lanzamiento de valor" estricto, puede usar static_cast. Si desea utilizar polimórficos en tiempo de ejecución de punteros, use dynamic_cast. Si realmente quieres olvidarte de los tipos, puedes usar reintrepret_cast. Y para lanzar const fuera de la ventana hay const_cast.

Simplemente hacen que el código sea más explícito para que parezca que sabes lo que estás haciendo.


26
2017-09-19 16:46



static_cast significa que no puedes accidentalmente const_cast o reinterpret_cast, Lo que es algo bueno.


21
2017-09-19 17:39



  1. Permite que los moldes se encuentren fácilmente en su código usando grep o similar herramientas.
  2. Lo hace explícito de qué tipo     de yeso que estás haciendo, y atractivo     la ayuda del compilador para hacer cumplir.     Si solo quieres desechar     const-ness, entonces puedes usar     const_cast, que no te permitirá     para hacer otros tipos de conversiones
  3. Los yesos son inherentemente feos: tú como         un programador están ignorando cómo el         compilador ordinariamente trataría su         código. Estás diciendo a la         compilador, "lo sé mejor que tú".         Siendo ese el caso, tiene sentido         que la realización de un elenco debe ser una         cosa moderadamente dolorosa para hacer, y         que deberían sobresalir en su         código, ya que son una fuente probable         de problemas

Ver Eficaz C ++ Introducción


7
2017-09-19 16:47



Se trata de la cantidad de seguridad que desea imponer.

Cuando escribes (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 le dicen.

Cuando escribes static_cast<bar> foo le está pidiendo al compilador que al menos verifique que la conversión de tipo tenga sentido y, para los tipos integrales, 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é. (Ver comentarios.) ¡Pero todavía recibe votos favorables!


7
2017-09-19 17:29



Los estilos C Style son fáciles de perder en un bloque de código. Los moldes de estilo C ++ no son solo una mejor práctica; ofrecen un grado mucho mayor de flexibilidad.

reinterpret_cast permite realizar conversiones de tipo integral de puntero, pero puede no ser seguro si se utiliza incorrectamente.

static_cast ofrece una buena conversión para tipos numéricos, p. desde como enumeraciones a entradas o entradas a flotantes o cualquier tipo de datos de los que tenga confianza. No realiza ningún control 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 gastos generales.

Hay un par de otros, pero estos son los principales que encontrarás.


4
2017-09-19 16:37



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);

3