Si utilizo conversiones de estilo C en mi proyecto de C ++, ¿vale la pena refactorizarlas a conversiones de C ++?

Utilizo conversiones de estilo C en mi proyecto LOC C ++ de 15K, el 90% de las veces para conversiones entre clases base y secundaria.

Incluso cuando leí que es malo usarlos y que pueden dar lugar a errores graves, ya que no son seguros para los tipos como los modelos de C ++, todavía sentir perfectamente bien y cómodo con su uso.

No he experimentado un solo error en mi proyecto hasta ahora que haya sido causado, por ejemplo, por un elenco de estilo C mal escrito accidentalmente, en realidad.

Hay dos razones principales por las que no los he estado usando:

  • No sabía lo suficiente sobre ellos todavía
  • No me gustó su sintaxis, son más detalladas y más difíciles de leer para mí.

Mis preguntas:

  • (Por qué) ¿Debería refactorizar mi proyecto para utilizar conversiones de estilo C ++?
  • ¿Por qué debería utilizar conversiones de estilo C ++ para mis proyectos futuros?

Utilizo tan bien como todas las demás ventajas que me ofrece C ++, desde la programación orientada a objetos, incluidas las clases base virtuales y abstractas, los espacios de nombres, el STL, etc., pero no la nueva sintaxis de conversión de tipos. El argumento "¿Por qué no estás usando C entonces?"no funciona para mí.

preguntado el 18 de febrero de 11 a las 18:02

Tenga en cuenta que las conversiones de estilo C ++ son intencionalmente detalladas; se destacan más! Y, con suerte, le disuadirá de usarlos excepto cuando sea estrictamente necesario. -

Creo que estás haciendo la pregunta incorrecta. Uno no debería preguntarse, "¿Qué yeso debo usar?" Uno debería preguntarse, "¿Por qué estoy usando yesos?" Si no usa ningún molde, entonces la elección de cuál usar es discutible. ¿Hay algo en tu diseño que te anime a usar yesos? ¿Puedes modificar tu diseño para evitarlos? -

Es curioso, creo que los moldes de C ++ se ven mejor que los de C. Pero incluso si fuera posible decir objetivamente que los de C ++ se ven peor, eso posible no sería una razón válida para evitarlos. Y de nuevo, en todo caso, diría que son más fácil entender que los moldes de C, porque los moldes de C ++ tienen poderes definidos, en lugar de solo prueba todos estos tipos subyacentes de yeso hasta algo 'obras'. Así que no puedo estar de acuerdo con ninguna de tus razones. Ah bueno. Opiniones. -

7 Respuestas

La principal ventaja de las conversiones de estilo C ++ es, como ha mencionado, la seguridad de tipos. Cada conversión en C ++ maneja un tipo específico de conversión (o una familia de conversiones relacionadas), por lo que el compilador puede ir y verificar que no esté haciendo accidentalmente más conversiones de las que pretendía, o una secuencia de conversiones que fundamentalmente no es segura. .

Una cosa en la que pensar es que si bien es genial que te sientas cómodo usando yesos estilo C, y si bien es genial que no hayas cometido ningún error con ellos, es posible que otras personas que trabajan en la base del código no sean tan fáciles con estos yesos. como tu eres. El uso de operadores de conversión hace que el código sea más autodocumentado. Si tiene un elenco de estilo C en alguna parte, es posible que otra persona que lea el código no pueda inferir de inmediato qué es lo que está haciendo. Si ven algo como

T* ptr = (T*) var;

Es posible que no puedan saber de inmediato si esto es

  1. Una conversión de una clase base a una clase derivada o al revés.
  2. Un yeso para desnudar constness fuera de var
  3. Una conversión de un tipo integral a un puntero.

Si bien probablemente puedan deducir esto del contexto, es mucho más obvio lo que está sucediendo si usas un elenco como

T* ptr = static_cast<T*>(var);

or

T* ptr = const_cast<T*>(var);

Otra razón para preferir los operadores de conversión de estilo C ++ es que hacen que el código sea más resistente al cambio. Por ejemplo, supongamos que tengo esta función:

void DoSomething(Base* ptr) {
    Derived* derived = (Derived *) ptr;
    DoSomethingElse(derived);
}

Ahora, suponga que me doy cuenta de que se supone que esta función no debe realizar ningún cambio en su argumento, así que decido marcarlo const. Por ejemplo:

void DoSomething(const Base* ptr) {
    Derived* derived = (Derived *) ptr;
    DoSomethingElse(derived);
}

Pero ahora tenemos un problema: mi elenco de estilo C que solía hacer un abatimiento ahora también se quita constness. Esto puede conducir a un error fácil en el que DoSomethingElse muta el puntero que paso, a pesar de que el DoSomething El método en sí mismo promete no hacer esto. Si en cambio escribí este código como

void DoSomething(Base* ptr) {
    Derived* derived = static_cast<Derived *>(ptr);
    DoSomethingElse(derived);
}

Y luego cambia el código agregando const:

void DoSomething(const Base* ptr) {
    Derived* derived = static_cast<Derived *>(ptr);
    DoSomethingElse(derived);
}

Ahora obtendré un error del compilador que me dice que mi antiguo elenco está roto, lo que puede llevarme a descubrir que hay un error lógico en el código (es decir, que DoSomethingElse muta su argumento, por lo que no puedo hacer ingenuamente ptr un puntero aconst.

En resumen, el uso de los operadores de conversión de C ++ hace que el código sea más legible y más fácil de mantener. Hace que la lógica detrás del código sea más explícita. Y hace que el código sea menos propenso a errores al hacer que el compilador detecte errores mientras los está cometiendo o más adelante cuando regrese y cambie el código antiguo. Recomendaría intentar usar operadores de conversión de estilo C ++ en el futuro por estas razones principales.

En cuanto a si debe volver atrás e intentar reemplazar sus moldes de estilo C actuales con moldes de estilo C ++, eso realmente depende de usted. Como ejercicio, te sugiero que lo hagas solo para practicar el aprendizaje del tipo de yesos que estás usando. Además, puede encontrar un error lógico allí, ¡lo que haría que la búsqueda valiera la pena!

Respondido 18 Feb 11, 21:02

Gran explicación y ejemplos - Marlon

¡Esta respuesta es más detallada que los modelos de estilo C ++! ...Solo jugando; muy informativo :) - Cheezmeister

En una empresa para la que solía trabajar, una vez tuvimos que migrar varios millones de líneas de aplicación de código a una nueva plataforma. Lo que comenzó como "un puerto más para otra plataforma Unix" (el código ya se ejecuta en Windows, OSX y media docena de plataformas similares a Unix) resultó ser un gran problema. IIRC, la razón era que esta plataforma había estrictos requisitos de alineación, y algunos de nuestros elencos se tropezaron excepciones de hardware debido a la desalineación.

Esto habría sido bastante fácil de arreglar, si no fuera por el hecho de que un cierto porcentaje de estos lanzamientos Yesos estilo C. Era imposible efectivamente grep para éstos. Las búsquedas aparecieron literalmente decenas de miles de líneas de código, que todo tenía que ser inspeccionado manualmente por seres humanos, El 99.99% de los cuales eran totalmente irrelevantes.
No ayudó que nosotros los humanos tendían a extrañar algunos de estos modelos difíciles de detectar, que luego tendrían que encontrarse en otra ronda de revisión decenas de miles de líneas de código, el 99.99% de ellas exactamente igual que en la primera ronda.

Esa tarea era casi imposible de terminar en el tiempo dado, y casi rompió la espalda de la empresa, porque se enfrentaban a una severa sanción contractual si no cumplíamos con el plazo.

No hace falta decir que, posteriormente, se decidió reemplazar todas las versiones de estilo C por su contraparte de C ++. Sin embargo, si esta política hubiera existido antes de que tuviéramos que hacer ese puerto ...

Respondido 19 Feb 11, 23:02

Ciertamente me interesaría por qué se rechazó esto. ¿Tiene algo de malo? - sbi

¿No podría simplemente haber ejecutado el código a través de GCC con -Wold-style-cast? (¡Tenga en cuenta que no voté en contra!) - Oliver Charlesworth

@Oli: No sé, yo era uno de los chicos de Windows. :) Creo que necesitaban un compilador específico de plataforma con eso. Pero sí, si esa advertencia ya existiera en ese entonces, podríamos haberlo intentado. ¿Tiene alguna idea de si esto ya formaba parte de GCC hace unos diez años? Si es así, les enviaré un correo a esos tipos, lo que sin duda los conmoverá. Estoy bastante seguro de que la historia de este puerto sigue siendo una parte vital del folclore de esa empresa ... :) - sbi

@sbi: ¡No tengo idea de cuándo se presentó! - Oliver Charlesworth

@sbi: En realidad, un poco de investigación ha demostrado que se remonta al menos hasta GCC 2.95.3, ¡que estaba disponible en 2001! - Oliver Charlesworth

(Por qué) ¿Debería refactorizar mi proyecto para utilizar conversiones de estilo C ++?

Si. Por la misma razón, debería utilizarlos en el futuro.

¿Por qué debería utilizar conversiones de estilo C ++ para mis proyectos futuros?

La lista es bastante larga, pero repasaré las más importantes.

Primero, establecen claramente la intención del elenco. Al leer "static_cast", sabes que el autor pretendía realizar un elenco estático, en lugar de una reinterpretación o una constante.

En segundo lugar, hacen una y solo una cosa. No se puede desechar accidentalmente la constidad, por ejemplo.

Son fáciles de buscar. Busque "const_cast" para encontrar todos los lanzamientos hacia / desde const. ¿Cómo harías esto con C-style? No podrías.

No cambian el tipo de transmisión cuando cambia la semántica del código local. Por ejemplo, (Widget*)(blah) cambiará silenciosamente a una reinterpretación si Widget deja de heredar de cualquier tipo al que blah apuntaba. También cambia a una reinterpretación si la definición del widget no está disponible localmente. Si usa conversiones de estilo nuevo, obtendrá el vómito del compilador en lugar de una reinterpretación silenciosa de la declaración.

Puedes hacer un elenco dinámico. Los yesos estilo C no pueden hacer esto.

Respondido 18 Feb 11, 21:02

Bueno, dos partes:

(Por qué) ¿Debería refactorizar mi proyecto para utilizar conversiones de estilo C ++?

Si su código funciona. Entonces no lo arregles. Déjalo así. De todos modos, encontrar los modelos C puede ser bastante difícil. Si está trabajando en el código y se encuentra con un elenco de C, cámbielo según sea necesario.

¿Por qué debería utilizar conversiones de estilo C ++ para mis proyectos futuros?

Porque los yesos C son fáciles de equivocar.

Respondido 18 Feb 11, 22:02

... hasta que, como ha demostrado sbi, de repente se no se trabaja. Un buen argumento para una prueba de futuro mesurada, diría yo. - subrayado_d

Si el 90% de tus elencos son de subclase a clase base, entonces eliminarlos sería un comienzo, no deberían ser necesarios. Si tienes muchos lances de base a sub, entonces estás haciendo algo más mal.

El resto, le está diciendo al compilador que algo extraño está sucediendo y está rompiendo las reglas, ya sea las reglas del sistema de tipos con un reinterpret_cast o un const_cast, está arriesgando la representación con un static_cast a un tipo integral más pequeño , o está rompiendo LSP y tiene que saber sobre un tipo específico de subclase cuando se le da una clase base.

Hacer explícitos esos casos es algo bueno.

Respondido 18 Feb 11, 21:02

No siempre estás rompiendo las reglas. Por ejemplo, puede estar promocionando tipos de números enteros para hacer cálculos matemáticos correctamente (p. Ej. uint16_t a; uint16_t b; uint32_t c = (uint32_t)a * (uint32_t)b;). - Oliver Charlesworth

@Oli tiendo a usar la sintaxis del constructor en lugar de los casts para promociones, uint16_t a; uint16_t b; uint32_t c = uint32_t(a) * uint32_t(b);, ya que falla en (la mayoría) las conversiones de restricción. - Pete Kirkham

"¿Puede dar un ejemplo en el que la sintaxis del constructor haga algo diferente del elenco de estilo C? - ben voigt

(Why) Should I refactor my project to use C++-style casts?
Why should I use C++-style casts for my future projects?

Hay algunas buenas respuestas (incluida mi propia OMI) a la segunda pregunta sobre esta pregunta.

Para la primera pregunta, si estuviera en su posición, sustituiría los moldes de estilo C ++ por los moldes de estilo C siempre que ya esté editando un archivo. Probablemente no valga la pena reservar un día o dos para editar todo su código y volver a compilarlo, pero probablemente valga la pena mejorar gradualmente su base de código cuando ya esté trabajando en él.

Si tiene tiempo reservado para actividades de refactorización, este sería un buen candidato para eso.

contestado el 23 de mayo de 17 a las 11:05

Esta era la respuesta que me iba a dar. Reemplace los yesos cuando los vea, pero no vaya a buscarlos. - ben voigt

Si fuera usted, seguiría usando yesos estilo C. Tiene buenas razones: no está experimentando las supuestas deficiencias de los moldes de estilo C, los moldes de C ++ son más detallados, los de C ++ son más difíciles de leer y menos familiares para usted.

De manera más general, como programador de C ++, a menudo leerá o le dirán que alguna forma de hacer las cosas es la forma Antigua, Incorrecta y C de hacer las cosas y que debe utilizar la forma Nueva, Correcta y C ++ de hacer las cosas. Algunos ejemplos:

  • deberías usar conversiones de estilo C ++ en lugar de conversiones de estilo C
  • deberías usar referencias en lugar de punteros
  • deberías usar mucho la palabra clave "const"
  • deberías usar genéricos en lugar de punteros vacíos o encasillamientos
  • debe usar listas de inicializadores en lugar de inicializar variables miembro en el cuerpo de los constructores
  • deberías usar constructores en lugar de una función de inicialización posterior a la construcción
  • nunca deberías usar macros

Por lo general, se le dirá que la nueva forma es más segura, que permite que el compilador detecte más errores y que aclara el código.

Le sugiero que vea este consejo con sospecha. En su lugar, considere: ¿el uso de la nueva función de C ++ resuelve un problema que realmente tiene? ¿Crea nuevos problemas (por cierto, el aumento de la verbosidad del código y la disminución de la legibilidad son problemas)? Si la respuesta a la primera pregunta es no, o la respuesta a la primera pregunta es sí, debería considerar seguir con el método antiguo, incorrecto, C.

Un comentario lateral sobre la claridad del código: la verbosidad reduce la claridad si el texto agregado no ayuda al lector a comprender el programa. Si fuera usted, vería con escepticismo la afirmación de que las versiones de estilo C ++ mejoran la claridad del código, ya que usted, un lector de su programa, no percibe un beneficio.

Solía ​​ser el tipo de persona que siempre trataba de utilizar el método Nuevo, Correcto y C ++. Ya no soy tan dogmático. El ensayo en el enlace de abajo fue un factor importante para cambiar de opinión.

http://yosefk.com/c++fqa/

Respondido 18 Feb 11, 21:02

nitpick: los genéricos no son plantillas - Marlon

La mayoría de las cosas de tu lista son realmente muy bien asesoramiento, excepto en unos pocos casos especiales. Debería hacer todo eso de forma predeterminada y solo volver al "estilo antiguo" cuando necesitas a por la razón que sea. De lo contrario, solo está haciendo que su código sea más propenso a errores intencionalmente. - Oliver Charlesworth

Creo que debería agregar a la lista: debería estar programando en C o en un lenguaje de nivel mucho más alto que C ++ (que parece ser el punto principal de ese sitio). - Unclebens

El FQA es aproximadamente un 10% realmente bueno, un 20% algo básico de hecho pero presentado muy mal, y un 70% FUD. De sus siete puntos, diría que el n. ° 3, n. ° 4, n. ° 5 y n. ° 6 son universalmente ciertos (el n. ° 3 implica el n. ° 5 y el n. ° 6), el n. ° 1 y el n. ° 7 son circunstancialmente verdaderos y el n. ° 2 es un montón de tonterías. Reemplace # 2 con "Debe usar iteradores o punteros inteligentes en lugar de punteros, referencias constantes cuando la copia es costosa y referencias no constantes solo cuando se sobrecargan operadores". (Los punteros inteligentes reemplazan a los punteros que poseen su objetivo, los iteradores reemplazan a los que no lo hacen). ben voigt

De acuerdo con @Ben. Hombres de paja clásicos como quejarse de que sobrecargar las sobrecargas aritméticas puede ser confuso si crea dinámicamente el valor de retorno con new (yosefk.com/c++fqa/operator.html#fqa-13.1) significa que la mayoría de las FQA deben tratarse con extrema precaución. - Oliver Charlesworth

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.