Em C++, que são os benefícios do uso de exceções e try / catch em vez de apenas retornar um código de erro?

StackOverflow https://stackoverflow.com/questions/196522

Pergunta

Eu já programado em C e C++ por um longo tempo e até agora eu nunca usei e exceções try / catch.Quais são os benefícios de usar o que em vez de ter apenas funções de retornar códigos de erro?

Foi útil?

Solução

Possivelmente um ponto óbvio - um desenvolvedor pode ignorar (ou não estar ciente) do seu status de retorno e ficar felizmente sem saber que algo falhou.

Uma exceção precisa ser reconhecida de alguma forma - ela não pode ser silenciosamente ignorada sem colocar ativamente algo para fazê -lo.

Outras dicas

A vantagem de exceções são duas vezes:

  • Eles não podem ser ignorados. Você deve lidar com eles, em algum nível, ou eles vão encerrar o programa.Com o código de erro, você deve verificar explicitamente para eles, ou eles estão perdidos.

  • Eles podem ser ignorados. Se um erro não pode ser tratada em um nível, ele será automaticamente bolha até o próximo nível, onde pode ser.Códigos de erro deve ser passado explicitamente até atingirem o nível onde ela pode ser tratada.

A vantagem é que você não precisa verificar o código de erro após cada chamada potencialmente falhada. Para que isso funcione, você precisa combiná -lo com as aulas RAII, para que tudo seja limpo automaticamente à medida que a pilha se desenrola.

Com mensagens de erro:

int DoSomeThings()
{
    int error = 0;
    HandleA hA;
    error = CreateAObject(&ha);
    if (error)
       goto cleanUpFailedA;

    HandleB hB;
    error = CreateBObjectWithA(hA, &hB);
    if (error)
       goto cleanUpFailedB;

    HandleC hC;
    error = CreateCObjectWithA(hB, &hC);
    if (error)
       goto cleanUpFailedC;

    ...

    cleanUpFailedC:
       DeleteCObject(hC);
    cleanUpFailedB:
       DeleteBObject(hB);
    cleanUpFailedA:
       DeleteAObject(hA);

    return error;
}

Com exceções e Raii

void DoSomeThings()
{
    RAIIHandleA hA = CreateAObject();
    RAIIHandleB hB = CreateBObjectWithA(hA);
    RAIIHandleC hC = CreateCObjectWithB(hB);
    ...
}

struct RAIIHandleA
{
    HandleA Handle;
    RAIIHandleA(HandleA handle) : Handle(handle) {}
    ~RAIIHandleA() { DeleteAObject(Handle); }
}
...

À primeira vista, a versão Raii/exceção parece mais longa, até você perceber que o código de limpeza precisa ser escrito apenas uma vez (e existem maneiras de simplificar isso). Mas a segunda versão dos dosomethethethethethethethethethings é muito mais clara e sustentável.

Não tente usar exceções em C ++ sem o idioma RAII, pois você vazará recursos e memória. Toda a sua limpeza precisa ser feita em destruidores de objetos alocados em pilha.

Sei que existem outras maneiras de fazer o tratamento do código de erro, mas todas elas acabam parecendo o mesmo. Se você soltar os Gotos, acaba repetindo o código de limpeza.

Um ponto para códigos de erro é que eles tornam óbvio onde as coisas podem falhar e como podem falhar. No código acima, você o escreve com a suposição de que as coisas não vão falhar (mas se o fizerem, você será protegido pelos invólucros da RAII). Mas você acaba prestando menos atenção, onde as coisas podem dar errado.

O manuseio de exceções é útil porque facilita a separação do código de manuseio de erros do código gravado para lidar com a função do programa. Isso facilita a leitura e a escrita do código.

Além das outras coisas mencionadas, você não pode retornar um código de erro de um construtor. Destrutores também, mas você deve evitar jogar uma exceção de um destruidor também.

  • retornar um código de erro quando uma condição de erro for esperado em alguns casos
  • faça uma exceção quando uma condição de erro é não esperado em qualquer caso

No primeiro caso, o chamador da função deve verificar o código de erro para a falha esperada; Neste último caso, a exceção pode ser tratada por qualquer chamador pela pilha (ou pelo manipulador padrão), conforme apropriado

Eu escrevi uma entrada no blog sobre isso (Exceções fazem o código elegante), que foi posteriormente publicado em Sobrecarga. Na verdade, escrevi isso em resposta a algo que Joel disse no podcast Stackoverflow!

De qualquer forma, acredito firmemente que as exceções são preferíveis a códigos de erro na maioria das circunstâncias. Acho realmente doloroso usar funções que retornam códigos de erro: você deve verificar o código de erro após cada chamada, o que pode interromper o fluxo do código de chamada. Isso também significa que você não pode usar operadores sobrecarregados, pois não há como sinalizar o erro.

A dor de verificar códigos de erro significa que as pessoas geralmente negligenciam, tornando -as completamente inúteis: pelo menos você precisa ignorar explicitamente exceções com um catch declaração.

O uso de destruidores no C ++ e nos dispositivos no .NET para garantir que os recursos sejam libertados corretamente na presença de exceções também podem simplificar bastante o código. Para obter o mesmo nível de proteção com códigos de erro, você precisa de muitos if declarações, muito código de limpeza duplicado, ou goto Chamadas para um bloco comum de limpeza no final de uma função. Nenhuma dessas opções é agradável.

Aqui está Uma boa explicação do EAFP ("mais fácil pedir perdão do que permissão".), O que eu acho que se aplica aqui, mesmo que seja uma página do Python na Wikipedia. Usar exceções leva a um estilo mais natural de codificação, IMO - e na opinião de muitos outros também.

Quando eu costumava ensinar C ++, nossa explicação padrão era que eles permitiram que você evitasse cenários de dia ensolarado e dia chuvoso. Em outras palavras, você poderia escrever uma função como se tudo funcionasse bem e pegue a exceção no final.

Sem exceções, você precisaria obter um valor de retorno de cada chamada e garantir que ela ainda seja legítima.

Um benefício relacionado, é claro, é que você não "desperdice" seu valor de retorno em exceções (e, portanto, permite que métodos que devem ser nulos) e também podem retornar erros de construtores e destruidores.

Guia de estilo C ++ do Google tem uma análise ótima e completa dos prós e contras do uso de exceção no código C ++. Também indica algumas das perguntas maiores que você deve fazer; ou seja, pretendo distribuir meu código a outras pessoas (que podem ter dificuldade em integrar com uma base de código com exceção)?

Às vezes, você realmente precisa usar uma exceção para sinalizar um caso excepcional. Por exemplo, se algo der errado em um construtor e você achar que faz sentido notificar o chamador sobre isso, você não tem escolha a não ser fazer uma exceção.

Outro exemplo: às vezes não há valor que sua função possa retornar para indicar um erro; Qualquer valor que a função possa retornar denota sucesso.

int divide(int a, int b)
{
    if( b == 0 )
        // then what?  no integer can be used for an error flag!
    else
        return a / b;
}

O fato de você ter que reconhecer exceções está correto, mas isso também pode ser implementado usando estruturas de erro. Você pode criar uma classe de erro base que verifique seu DTOR se um determinado método (por exemplo, ISOK) foi chamado. Caso contrário, você pode registrar algo e depois sair, ou jogar uma exceção, ou aumentar uma afirmação, etc ...

Apenas chamar o ISOK no objeto de erro sem reagir a ele, seria o equivalente a escrever captura (...) {} Ambas as instruções exibiriam a mesma falta de boa vontade do programador.

O transporte do código de erro até o nível correto é uma preocupação maior. Você basicamente teria que fazer com que quase todos os métodos retornem um código de erro para o único motivo de propagação. Mas, novamente, uma função ou método deve sempre ser anotada com as exceções que pode gerar. Então, basicamente, você tem o mesmo problema, sem uma interface para apoiá -lo.

Como @Martin apontou, lançar exceções força o programador a lidar com o erro. Por exemplo, não verificar os códigos de retorno é uma das maiores fontes de orifícios de segurança nos programas C. Exceções Certifique -se de lidar com o erro (espero) e fornecer algum tipo de caminho de recuperação para o seu programa. E se você optar por ignorar uma exceção em vez de apresentar um buraco de segurança que seu programa trava.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top