Na minha opinião, o maior argumento é a diferença no que acontece quando o programador comete um erro. Esquecer de lidar com um erro é um erro muito comum e fácil de fazer.
Se você retornar códigos de erro, é possível ignorar silenciosamente um erro. Por exemplo, se malloc falhar, ele retornará NULL
e definirá o% globalerrno
. Então o código correto deve fazer
void* myptr = malloc(1024);
if (myptr == NULL) {
perror("malloc");
exit(1);
}
doSomethingWith(myptr);
Mas é muito fácil e conveniente escrever apenas:
void* myptr = malloc(1024);
doSomethingWith(myptr);
que inesperadamente passará NULL
em seu outro procedimento e provavelmente descartará o errno
que foi cuidadosamente definido. Não há nada visivelmente errado com o código para indicar que isso é possível.
Em uma linguagem que usa exceções, você escreveria
MyCoolObject obj = new MyCoolObject();
doSomethingWith(obj);
Neste exemplo (Java), o operador new
retorna um objeto inicializado válido ou lança OutOfMemoryError
. Se um programador deve lidar com isso, eles podem pegá-lo. No caso usual (e convenientemente, também o preguiçoso) em que é um erro fatal, a propagação da exceção encerra o programa de maneira relativamente limpa e explícita.
Essa é uma das razões pelas quais as exceções, quando usadas adequadamente, podem tornar a escrita mais clara e segura do código muito mais fácil. Este padrão se aplica a muitas, muitas coisas que podem dar errado, não apenas alocando memória.