En mi opinión, el mayor argumento es la diferencia en lo que sucede cuando el programador comete un error. Olvidarse de manejar un error es un error muy común y fácil de cometer.
Si devuelve códigos de error, es posible ignorar un error en silencio. Por ejemplo, si malloc falla, devuelve NULL
y establece el errno
global. Así que el código correcto debería hacer
void* myptr = malloc(1024);
if (myptr == NULL) {
perror("malloc");
exit(1);
}
doSomethingWith(myptr);
Pero es muy fácil y conveniente escribir en su lugar:
void* myptr = malloc(1024);
doSomethingWith(myptr);
que inesperadamente pasará NULL
a su otro procedimiento y probablemente descartará el errno
que se estableció cuidadosamente. No hay nada visible mal con el código para indicar que esto es posible.
En un idioma que usa excepciones, en lugar de eso, escribirías
MyCoolObject obj = new MyCoolObject();
doSomethingWith(obj);
En este ejemplo (Java), el operador new
devuelve un objeto inicializado válido o lanza OutOfMemoryError
. Si un programador debe manejar esto, pueden atraparlo. En el caso habitual (y convenientemente, también perezoso) en el que se trata de un error fatal, la propagación de excepciones termina el programa de una manera relativamente limpia y explícita.
Esa es una de las razones por las que las excepciones, cuando se usan correctamente, pueden hacer que la escritura de códigos claros y seguros sea mucho más fácil. Este patrón se aplica a muchas, muchas cosas que pueden salir mal, no solo a la asignación de memoria.