c++ - Venderme en const correctness
const-correctness (16)
Entonces, ¿por qué exactamente se recomienda siempre usar const tan a menudo como sea posible? Me parece que usar const puede ser más un dolor que una ayuda en C ++. Pero, de nuevo, estoy hablando de esto desde la perspectiva de Python: si no quieres que se modifique algo, no lo cambies. Entonces con eso dicho, aquí hay algunas preguntas:
Parece que cada vez que marco algo como const, obtengo un error y tengo que cambiar alguna otra función en algún lugar para que también sea const. Entonces esto me obliga a tener que cambiar otra función en otro lugar. ¿Es esto algo que se vuelve más fácil con la experiencia?
¿Los beneficios de usar const son realmente suficientes para compensar el problema? Si no tiene la intención de cambiar un objeto, ¿por qué no simplemente no escribir código que no lo cambie?
Debo señalar que en este momento, estoy más centrado en los beneficios de usar const para fines de corrección y mantenimiento, aunque también es bueno tener una idea de las implicaciones de rendimiento.
Parece que cada vez que marco algo como const, obtengo un error y tengo que cambiar alguna otra función en algún lugar para que también sea const. Entonces esto me obliga a tener que cambiar otra función en otro lugar. ¿Es esto algo que se vuelve más fácil con la experiencia?
Por experiencia, este es un mito total. Sucede cuando no const-correct se sienta con el código correcto, seguro. Si diseña const-correct desde el principio, esto NUNCA debería ser un problema. Si haces algo const, y luego algo más no cumple, el compilador te está diciendo algo extremadamente importante, y debes tomarte el tiempo para arreglarlo correctamente .
Aquí hay un fragmento de código con un error común que const correctness puede protegerlo contra:
void foo(const int DEFCON)
{
if (DEFCON = 1) //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
{
fire_missiles();
}
}
Cuando utiliza la palabra clave "const", está especificando otra interfaz para sus clases. Hay una interfaz que incluye todos los métodos y una interfaz que incluye solo los métodos const. Obviamente, esto le permite restringir el acceso a algunas cosas que no quiere cambiar.
Sí, se hace más fácil con el tiempo.
Digamos que tiene una variable en Python. Sabes que no debes modificarlo. ¿Qué pasa si lo haces accidentalmente?
C ++ te brinda una forma de protegerte de hacer algo accidentalmente que no se suponía que pudieras hacer en primer lugar. Técnicamente puedes evitarlo de todos modos, pero tienes que trabajar más para dispararte a ti mismo.
Este es el artículo definitivo sobre "const correctness": https://isocpp.org/wiki/faq/const-correctness .
En pocas palabras, usar const es una buena práctica porque ...
- Le protege de cambiar accidentalmente variables que no están destinadas a ser cambiadas,
- Le protege de hacer asignaciones de variables accidentales, y
El compilador puede optimizarlo. Por ejemplo, estás protegido de
if( x = y ) // whoops, meant if( x == y )
Al mismo tiempo, el compilador puede generar un código más eficiente porque sabe exactamente cuál será el estado de la variable / función en todo momento. Si está escribiendo un código C ++ estricto, esto es bueno.
Tiene razón en que puede ser difícil usar const-correctness de forma consistente, pero el código de finalización es más conciso y más seguro de programar. Cuando haces un montón de desarrollo en C ++, los beneficios de esto se manifiestan rápidamente.
Hay un buen artículo here sobre const en c ++. Es una opinión bastante directa, pero espero que ayude a algunos.
La programación de C ++ sin const es como conducir sin el cinturón de seguridad puesto.
Es muy doloroso ponerse el cinturón de seguridad cada vez que subes al automóvil, y 364 de los 365 días llegarás de manera segura.
La única diferencia es que cuando te metes en problemas con tu auto lo sentirás de inmediato, mientras que con la programación sin const tendrás que buscar durante dos semanas lo que causó ese bloqueo solo para descubrir que inadvertidamente echaste a perder un argumento de función que pasaste por referencia sin const para eficiencia.
Me gusta la precisión const ... en teoría. Por cada vez que he tratado de aplicarlo rigurosamente en la práctica, se ha roto eventualmente y el const_cast comienza a hacer que el código sea feo.
Tal vez solo sean los patrones de diseño que uso, pero siempre termina siendo un cepillo demasiado amplio.
Por ejemplo, imagine un motor de base de datos simple ... tiene objetos de esquema, tablas, campos, etc. Un usuario puede tener un puntero ''const Table'' lo que significa que no puede modificar el esquema de la tabla en sí ... pero ¿qué pasa con la manipulación? los datos asociados con la tabla? Si el método Insertar () está marcado const, entonces internamente tiene que descartar la constidad para realmente manipular la base de datos. Si no está marcado const, entonces no protege contra llamar al método AddField.
Quizás la respuesta sea dividir la clase según los requisitos de la constidad, pero eso tiende a complicar el diseño más de lo que me gustaría por el beneficio que trae.
Mi filosofía es que si vas a usar un lenguaje exigente con las comprobaciones de tiempo de compilación que hacer el mejor uso posible, puedes hacerlo. const
es una forma forzada de compilar de comunicar lo que quieres decir ... es mejor que los comentarios o doxygen será alguna vez. Estás pagando el precio, ¿por qué no derivar el valor?
No es para ti cuando estás escribiendo el código inicialmente. Es para otra persona (o usted unos meses más tarde) que está mirando la declaración de método dentro de la clase o interfaz para ver qué hace. No modificar un objeto es una información importante para obtener de eso.
Para la programación integrada, usar const
juiciosamente al declarar estructuras de datos globales puede ahorrar mucha RAM al hacer que los datos constantes se ubiquen en ROM o flash sin copiarlos a la RAM en el momento del arranque.
En la programación diaria, usar const
ayuda a evitar escribir programas que se bloquean o se comportan impredeciblemente porque intentan modificar literales de cadena y otros datos globales constantes.
Cuando se trabaja con otros programadores en proyectos grandes, el uso de const
ayuda a evitar que otros programadores lo estrangulan.
Puede dar pistas al compilador también const ... según el siguiente código
#include <string>
void f(const std::string& s)
{
}
void x( std::string& x)
{
}
void main()
{
f("blah");
x("blah"); // won''t compile...
}
Si utiliza const rigurosamente, se sorprenderá de la cantidad de variables reales que hay en la mayoría de las funciones. A menudo no es más que un contador de bucle. Si tu código está llegando a ese punto, obtienes una sensación cálida en tu interior ... validación por compilación ... el ámbito de la programación funcional está cerca ... casi puedes tocarlo ahora ...
const correctness es una de esas cosas que realmente necesita estar en su lugar desde el principio. Como ha encontrado, es un gran problema agregarlo más adelante, especialmente cuando existe una gran dependencia entre las nuevas funciones que está agregando y las funciones antiguas que ya no existen.
En gran parte del código que escribo, realmente ha valido la pena el esfuerzo porque tendemos a usar mucho la composición:
class A { ... }
class B { A m_a; const A& getA() const { return m_a; } };
Si no tuviéramos corrección de const, tendríamos que recurrir a devolver objetos complejos por valor para asegurarnos de que nadie estaba manipulando el estado interno de la clase B a sus espaldas.
En resumen, la corrección de const es un mecanismo de programación defensivo para salvarse del dolor en el futuro.
const es una promesa que estás haciendo como desarrollador, y alistar la ayuda del compilador en la aplicación.
Mis razones para ser const-correcto:
- Comunica a los clientes de su función que su no cambiará la variable u objeto
- La aceptación de los argumentos por const reference le da la eficiencia de pasar por referencia con la seguridad de pasar por el valor.
- Escribir sus interfaces como const correct les permitirá a los clientes usarlas. Si escribe su interfaz para incluir referencias que no sean const, los clientes que estén utilizando const deberán alejarse de la confusión para poder trabajar con usted. Esto es especialmente molesto si su interfaz acepta caracteres no const * y sus clientes están usando std :: strings, ya que solo puede obtener un const char * de ellos.
- El uso de const hará que el compilador te mantenga honesto para que no cambies por error algo que no debería cambiar.
const
ayuda a aislar el código que "cambia cosas" a sus espaldas. Entonces, en una clase, marcaría todos los métodos que no cambian el estado del objeto como const
. Esto significa que las instancias const
de esa clase ya no podrán llamar a ningún método no const
. De esta forma, no puede invocar accidentalmente la funcionalidad que puede cambiar su objeto.
Además, const
es parte del mecanismo de sobrecarga, por lo que puede tener dos métodos con firmas idénticas, pero uno con const
y otro sin. El que tiene const
se llama para las referencias const
, y el otro se llama para referencias no const
.
Ejemplo:
#include <iostream>
class HelloWorld {
bool hw_called;
public:
HelloWorld() : hw_called(false) {}
void hw() const {
std::cout << "Hello, world! (const)/n";
// hw_called = true; <-- not allowed
}
void hw() {
std::cout << "Hello, world! (non-const)/n";
hw_called = true;
}
};
int
main()
{
HelloWorld hw;
HelloWorld* phw1(&hw);
HelloWorld const* phw2(&hw);
hw.hw(); // calls non-const version
phw1->hw(); // calls non-const version
phw2->hw(); // calls const version
return 0;
}