Pregunta

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

¿Fue útil?

Solución

La razón principal es que los modelos 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.

A static_cast<>() suele ser seguro.Existe una conversión válida en el lenguaje o un constructor apropiado que lo hace posible.El único momento en que es un poco arriesgado es cuando se reduce 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 compruebe el resultado (puntero) o se tenga en cuenta una posible excepción (referencia).

A reinterpret_cast<>() (o un const_cast<>()) por otro lado siempre es peligroso.Le dices al compilador:"confía en mí:Sé que esto no parece un foo (Parece que no es mutable), pero lo es".

El primer problema es que es casi imposible saber cuál ocurrirá en una versión estilo C sin mirar fragmentos de código grandes y dispersos y conocer todas las reglas.

Supongamos estos:

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 existe 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 estilo C son demasiado difíciles de localizar.En expresiones complejas puede resultar muy difícil ver moldes de estilo C.Es prácticamente imposible escribir una herramienta automatizada que necesite localizar conversiones de estilo C (por ejemplo, una herramienta de búsqueda) sin una interfaz de compilador C++ completa.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 sólo los lanzamientos de estilo C son más peligrosos, sino que es mucho más difícil encontrarlos todos para asegurarse de que sean correctos.

Otros consejos

Un consejo pragmático:puedes buscar fácilmente la palabra clave static_cast en tu código fuente si planeas ordenar el proyecto.

En breve:

  1. static_cast<>() Te da una capacidad de verificación de tiempo de compilación, el elenco de estilo C no.
  2. static_cast<>() se puede ver fácilmente en cualquier lugar dentro de un código fuente de C ++;por el contrario, el elenco de C_Style es más difícil de detectar.
  3. Las intenciones se transmiten mucho mejor utilizando conversiones de C++.

Más explicación:

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 elenco de 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 memoria asignada, escribir a este puntero causará un error de tiempo de ejecución o sobrescribirá alguna memoria adyacente.

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

En contraste con el reparto de estilo C, el reparto estático permitirá al compilador verificar que los tipos de datos de puntero y puntee sean compatibles, lo que permite al programador atrapar esta asignación incorrecta de puntero durante la compilación.

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

Leer más sobre:
¿Cuál es la diferencia entre static_cast<> y la conversión estilo C?
y
Elenco regular vs.static_cast vs.transmisión_dinámica

La pregunta es más grande que simplemente usar wither static_cast o la conversión de estilo C porque suceden cosas diferentes cuando se usan conversiones de estilo C.Los operadores de conversión de C++ tienen como objetivo hacer que estas operaciones sean más explícitas.

En la superficie, las conversiones de estilo static_cast y C parecen lo mismo, por ejemplo, cuando se convierte un valor a otro:

int i;
double d = (double)i;                  //C-style cast
double d2 = static_cast<double>( i );  //C++ cast

Ambos convierten el valor entero en doble.Sin embargo, cuando se trabaja con punteros, las cosas se complican más.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 en realidad una instancia de B.Pero, ¿qué pasa si en ese punto del código no sabes a qué apunta realmente?(2) tal vez sea perfectamente legal (solo desea ver un byte del número 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 proporcionando errores en tiempo de compilación o de ejecución cuando sea posible.

Entonces, para una "transmisión de valor" estricta, puede usar static_cast.Si desea una conversión polimórfica de punteros en tiempo de ejecución, utilicedynamic_cast.Si realmente quieres olvidarte de los tipos, puedes usar reintrepret_cast.Y para tirar const por la ventana está const_cast.

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

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

  1. Permite que se encuentren fácilmente en su código usando GREP o herramientas similares.
  2. Lo hace explícito qué tipo de elenco estás haciendo e involucrar la ayuda del compilador para hacer cumplirlo.Si solo desea eliminar la constante, puede usar const_cast, lo que no le permitirá hacer otros tipos de conversiones.
  3. Los yesos son inherentemente feos: usted como programador está anulando cómo el compilador normalmente trataría su código.Le estás diciendo al compilador: "Lo sé mejor que tú". Siendo ese el caso, tiene sentido que realizar un elenco sea algo moderadamente doloroso, y que deberían sobresalir en su código, ya que son una fuente probable de problemas.

Ver C++ efectivo Introducción

Se trata de cuánta seguridad de tipo desea imponer.

Cuando escribes (bar) foo (que equivale a reinterpret_cast<bar> foo si no ha proporcionado un operador de conversión de tipos), le está diciendo al compilador que ignore la seguridad de tipos y que simplemente haga lo que se le indica.

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

static_cast, además de manipular punteros a clases, también se puede utilizar 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);

Las conversiones de estilo C son fáciles de pasar por alto en un bloque de código.Las conversiones de estilo C++ no sólo son una mejor práctica;ofrecen un grado mucho mayor 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, p.desde as enums hasta ints o ints hasta floats o cualquier tipo de datos del que esté seguro.No realiza ninguna comprobación del tiempo de ejecución.

Dynamic_cast, por otro lado, realizará estas comprobaciones y señalará cualquier asignación o conversión ambigua.Sólo funciona con punteros y referencias e incurre en gastos generales.

Hay un par más, pero estos son los principales con los que te encontrarás.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top