Los casos de uso relevantes para punteros nulos son
- Redirección a algo como un nodo de árbol más profundo, que puede no existir o no ha sido vinculado todavía. Eso es algo que siempre debes mantener encapsulado en una clase dedicada, por lo que la legibilidad o la concisión no son un gran problema aquí.
Moldes dinámicos. Lanzar un puntero de clase base a uno particular de clase derivada (algo que debería intentar evitar de nuevo, pero que a veces puede ser necesario) siempre tiene éxito, pero da como resultado un puntero nulo si la clase derivada no coincide. Una forma de verificar esto es
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
if(derived_ptr != nullptr) { ... }
(o, preferiblemente auto derived_ptr = ...
) Ahora, esto es malo, porque deja el puntero derivado (posiblemente inválido, es decir, nulo) fuera del if
alcance del bloque de protección de seguridad . Esto no es necesario, ya que C ++ le permite introducir variables booleanas convertibles dentro de una if
condición :
if(auto derived_ptr = dynamic_cast<Derived*>(base_ptr)) { ... }
que no solo es más corto y seguro para el alcance, también es mucho más claro en su intención: cuando verifica nulo en una condición if separada, el lector se pregunta "ok, entonces derived_ptr
no debe ser nulo aquí ... bueno, ¿por qué será nulo? Mientras que la versión de una sola línea dice muy claramente "si se puede transmitir de forma segura base_ptr
aDerived*
, y luego usarlo para ...".
Lo mismo funciona igual de bien para cualquier otra operación de posible falla que devuelva un puntero, aunque IMO generalmente debería evitar esto: es mejor usar algo boost::optional
como el "contenedor" para obtener resultados de posibles operaciones fallidas, en lugar de punteros.
Entonces, si el caso de uso principal para punteros nulos siempre se debe escribir en una variación del estilo implícito, diría que es bueno, por razones de coherencia, usar siempre este estilo, es decir, abogaría por if(ptr)
más if(ptr!=nullptr)
.
Me temo que tengo que terminar con un anuncio: la if(auto bla = ...)
sintaxis es en realidad una aproximación un poco engorrosa a la solución real a tales problemas: la coincidencia de patrones . ¿Por qué forzarías primero alguna acción (como lanzar un puntero) y luego considerarías que podría haber una falla ... Quiero decir, es ridículo, ¿no? Es como si tuvieras algo de comida y quieres hacer sopa. Se lo entrega a su asistente con la tarea de extraer el jugo, si resulta ser un vegetal blando. No lo miras primero. Cuando tienes una papa, todavía se la das a tu asistente, pero te la devuelven en la cara con una nota de falla. ¡Ah, programación imprescindible!
Mucho mejor: considere de inmediato todos los casos que pueda encontrar. Entonces actúa en consecuencia. Haskell
makeSoupOf :: Foodstuff -> Liquid
makeSoupOf p@(Potato{..}) = mash (boil p) <> water
makeSoupOf vegetable
| isSoft vegetable = squeeze vegetable <> salt
makeSoupOf stuff = boil (throwIn (water<>salt) stuff)
Haskell también tiene herramientas especiales para cuando realmente existe una posibilidad seria de falla (así como para un montón de otras cosas): mónadas. Pero este no es el lugar para explicarlos.
⟨/Propaganda⟩
0
onullptr
. (NULL
Es un C'ism, y requiere que incluye un archivo de cabecera.)