Esto aborda principalmente la segunda línea: mejores prácticas, tareas, parámetros de función, etc.
Práctica general. Intenta hacer todo lo const
que puedas. O para decirlo de otra manera, haga todo const
para comenzar, y luego elimine exactamente el conjunto mínimo de const
s necesarios para permitir que el programa funcione. Esto será de gran ayuda para lograr la corrección constante y ayudará a garantizar que no se introduzcan errores sutiles cuando las personas intentan y asignan cosas que no deben modificar.
Evite const_cast <> como la plaga. Hay uno o dos casos de uso legítimo para ello, pero son muy pocos y distantes. Si está tratando de cambiar un const
objeto, será mucho mejor encontrar a quien lo declaró const
en el primer paso y discutir el asunto con ellos para llegar a un consenso sobre lo que debería suceder.
Lo que lleva muy bien a las tareas. Puede asignar a algo solo si no es constante. Si desea asignar algo que sea constante, vea más arriba. Recuerde que en las declaraciones int const *foo;
y int * const bar;
diferentes cosas hay const
otras respuestas aquí que han cubierto ese tema admirablemente, por lo que no voy a entrar en él.
Parámetros de la función:
Pase por valor: por ejemplo void func(int param)
, no le importa de una forma u otra en el sitio de llamadas. Se puede argumentar que existen casos de uso para declarar la función como, void func(int const param)
pero que no tiene ningún efecto en la persona que llama, solo en la función en sí misma, ya que cualquier valor que se pase no puede ser cambiado por la función durante la llamada.
Pase por referencia: por ejemplo, void func(int ¶m)
ahora sí hace la diferencia. Como se acaba de declarar, func
está permitido cambiar param
, y cualquier sitio de llamadas debe estar listo para lidiar con las consecuencias. Cambiar la declaración a void func(int const ¶m)
cambios en el contrato y garantiza que func
ahora no puede cambiar param
, lo que significa que lo que se pasa es lo que volverá a salir. Como otros han señalado, esto es muy útil para pasar de manera económica un objeto grande que no desea cambiar. Pasar una referencia es mucho más barato que pasar un objeto grande por valor.
Pasaremos por el puntero: por ejemplo, void func(int *param)
y void func(int const *param)
estos dos son más o menos sinónimo de sus homólogos de referencia, con la salvedad de que la función llamada ahora necesita para comprobar si hay nullptr
menos que alguna otra garantía contractual asegura func
que nunca va a recibir una nullptr
en param
.
Artículo de opinión sobre ese tema. Probar la corrección en un caso como este es terriblemente difícil, es demasiado fácil cometer un error. Así que no se arriesgue, y siempre verifique los parámetros del puntero nullptr
. Se ahorrará dolor y sufrimiento y será difícil encontrar errores a largo plazo. Y en cuanto al costo del cheque, es muy barato, y en los casos en que el análisis estático integrado en el compilador puede administrarlo, el optimizador lo eludirá de todos modos. Active la Generación de código de tiempo de enlace para MSVC, o WOPR (creo) para GCC, y obtendrá todo el programa, es decir, incluso en llamadas de función que cruzan el límite del módulo de código fuente.
Al final del día, todo lo anterior hace un caso muy sólido para preferir siempre referencias a punteros. Son más seguros en todos los aspectos.