Pergunta

Ouvi dizer que você nunca deve jogar uma corda, porque há uma falta de informação e você vai capturar exceções você não esperar para pegar. Quais são boas práticas para lançar exceções? você herdar uma classe de exceção base? Você tem muitas exceções ou poucos? você faz MyExceptionClass & ou const MyExceptionClass &? etc. Também sei exceções nunca deve foram jogados em destruidores

i vou acrescentar que eu entendo projetar por contrato e quando jogar exceção. Estou perguntando como eu deveria lançar exceções.

Foi útil?

Solução

Na minha opinião, uma função deve lançar uma exceção se ele não pode manter a sua "promessa", se tem que quebrar o seu "contrato". assinatura (nome e parâmetros) da função de determinar o seu contrato.

Dadas estas duas funções membro:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

Os nomes destas funções, bem como os seus valores de retorno indicar-me que, no caso de FindApple a função é perfeitamente capaz de retornar NULL quando a maçã correto não foi encontrado, mas no caso de GetApple você está esperando uma maçã para retorno. Se essa segunda função não pode manter a sua promessa, ele deve lançar uma exceção.

As excepções são destinadas para essas condições excepcionais em que uma função não tem outra maneira de relatar essas condições. Se você decidir torná-la uma parte da promessa (leia-se: assinatura da função). Então ele pode informar que a condição sem lançar uma exceção

Note que, no caso de FindApple , cabe ao autor da chamada para decidir como lidar com a condição de "não encontrar a maçã direita", porque ele não é mais uma condição excepcional.

Você pode ser tentado a tentar evitar todas as exceções, mas isso significa que você tem que conta para todas as condições excepcionais possíveis, e você está colocando a carga sobre o chamador em seu lugar. As necessidades de chamada para verificar se há "condições de erro" em seguida.

Em última análise, uma exceção deve ser tratada, mas apenas pelo chamador que sabe como lidar com uma condição particular de forma útil . E eu quero dizer isso na mais ampla interpretação possível: um serviço que dá-se tentará novamente mais tarde, uma interface de usuário que fornece uma mensagem de erro útil, uma aplicação web que apresenta uma tela de "oops", mas que se recupera com bom gosto, ... e assim por diante .

Dave

Outras dicas

Uma coisa básica é a de exceções de reserva para situações excepcionais. Não usá-los para controle de fluxo. Por exemplo, "arquivo não encontrado" não deve ser uma exceção, deve ser um código de erro ou valor de retorno (a menos que o arquivo é algo que deve existem, por exemplo, um arquivo de configuração). Mas, se um arquivo desaparece de repente, enquanto você está processando-o, em seguida, lançar uma exceção é uma boa escolha.

Quando exceções são usados ??com moderação, você não precisa para transformar seu código em um -spaghetti try-catch, a fim de evitar receber exceções incompreensíveis-in-the-contexto das camadas mais profundas.

Use as exceções padrão! Se você tem um erro específico, tentar evitá-lo com valor de retorno. Se você Have para usar exceções, defina seu exceção personalizada que herda de Exception e criar uma mensagem personalizada.

Às vezes pode acontecer que você não é capaz de devolver o código de erro, por exemplo. quando você precisa contexto exato de quando a situação erro ocorreu, por exemplo. quando você precisa para propagar status de erro 3 níveis acima -. você solta contexto

Nesta classe personalizada situação é a melhor solução. Eu uso essa abordagem, definindo as minhas próprias classes em linha (não há .cpp para eles, só .h), por exemplo:.

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

etc.

Eu, então, pode julgar / agir de acordo com a exceção por tipo e por informação contida dentro.

Eu sempre lançar uma exceção com uma mensagem de onde ele ocorreu e que causou isso aconteça:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

Você pode então usar essas seqüências em messageboxes etc.

Eu sempre pegar via

catch (NException& ex) { ... }

Se você correr janelas que você pode passar o valor de erro e ter uma função derivar a mensagem de erro. O melhor exemplo disso é em Windows via C / C ++ por Jeffrey Richter .

Jogando ponteiros provavelmente não é uma coisa boa, uma vez que complica a propriedade do objeto lançado. tipo de classe exceções são provavelmente melhor do que os fundamentos simplesmente porque eles podem conter mais informações sobre o motivo da exceção.

Ao usar uma hierarquia de classe ou classe há um par de pontos que você deve considerar:

  1. Tanto o construtor de cópia e destruição do objecto excepção Nunca deve lançar uma exceção. E se eles fazem você estiver vontade programa terminar imediatamente. (ISO 15,5 / 1)

  2. Se seus objetos de exceção tem base de aulas, em seguida, usar a herança público.
    Um manipulador só será selecionado para uma derivado de classe base, se a base de classe é acessível . (ISO 15,3 / 3)

  3. Finalmente, (para todos os tipos de exceção) garantir que a expressão que está sendo jogada não pode -se resultar em uma exceção sendo lançada.

Por exemplo:

class Ex {
public:
  Ex(int i) 
  : m_i (i)
  {
    if (i > 10) {
      throw "Exception value out of range";
    }
  }

  int m_i;
};


void foo (bool b) {
  if (! b) {
     // 'b' is false this is bad - throw an exception
     throw Ex(20);    // Ooops - throw's a string, not an Ex
  }
}

Você deve sempre lançar uma classe de exceção derivada de std :: exceção. Isso permite uma certa consistência à sua interface e permite mais flexibilidade para os clientes desses métodos ou funções. Por exemplo, se você quiser adicionar um prendedor toda manipulador você pode ser capaz de adicionar um

bloco
catch(std::exception& e)
e ser feito com ele. (Embora muitas vezes você não será capaz de fugir com isso, se você não controlar todo o código que pode jogar).

I tendem a jogar apenas excepções previstas pela norma (ou seja, std :: runtime_error), mas se você quiser fornecer granularidade extra para seus manipuladores, você deve se sentir livre para derivar o seu próprio a partir de std :: exceção. Consulte o C ++ FAQ Lite .

Além disso, você deve lançar uma temporária e pegá-lo por referência (para evitar o ctor cópia ser invocado em seu local de captura). Jogando ponteiros também é desaprovada, uma vez que está claro quem deve limpar a memória. C ++ FAQ Lite lida com isso também.

Para um projeto atual, nós pensamos sobre a ação apropriada que poderiam ser tomadas pelo circuito principal do programa. O programa básico aceita mensagens XML e salva as informações em um banco de dados (com uma quantidade razoável de processamento no meio).

  1. erros de dados que indicam algo de errado a entrada. ação apropriada é salvar a mensagem para um diretório de log, mas não processá-lo.
  2. erros de infra-estrutura que indicam algum subcomponente (como a fila de entrada, um banco de dados SQL, uma biblioteca JNI) não está funcionando corretamente. Sono por alguns minutos, em seguida, reconectar.
  3. Os erros de configuração que indicam alguma configuração aspecto é impraticável. Sair do programa.

O primeiro item é uma exceção verificada, desde que considerados os dados de verificação para fazer parte de uma interface do método. Os outros são desmarcada uma vez que o loop principal não pode conhecer as implementações de subcomponentes, por exemplo, uma aplicação pode usar um banco de dados SQL, ou pode simplesmente manter dados na memória -. o chamador não precisa saber

Se você está jogando exceções de um componente de outros desenvolvedores estarão usando a jusante, por favor, faça-lhes um grande favor e sempre derivar suas próprias classes de exceção (se você realmente precisa deles c.f usando as exceções padrão) a partir de std :: exceção. Evite a todo custo abominações proferem como jogar ints, HRESULTS, char *, std :: string ...

Como já foi dito usá-los para situações excepcionais.

Sempre fornecer uma maneira para o usuário para evitar lançar uma exceção, por exemplo. se você tem método, que vai jogar, se algo der errado assim:

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

Fornecer um outro método para o usuário chamada:

public bool CanDoSomething() {
    return File.Exists(..);
}

Desta forma, não o chamador pode evitar exceções, se ele quiser. Não hesite em jogar se algo está errado -. "Falhar rapidamente", mas sempre forneça o caminho livre de exceção

Além disso, mantenha sua classe de exceção hierarquia plana e dar uma olhada nas exceções padrão como InvalidStateException e ArgumentNullExcpetion.

Aqui está um exemplo simples de lançar uma exceção que leva quase nenhum recursos:

class DivisionError {};
class Division
{
public:
    float Divide(float x, float y) throw(DivisionError)
    {
        float result = 0;
        if(y != 0)
            result = x/y;
        else
            throw DivisionError();
        return result;
    }
};

int main()
{
    Division d;
    try
    {
        d.Divide(10,0);
    }
    catch(DivisionError)
    {
        /*...error handling...*/
    }
}

A classe vazia que é jogado não tomar qualquer recurso ou muito poucos ...

Do ++ FAQ C, [17.12] O que devo jogar ? :

Geralmente, é melhor jogar objetos, não built-ins. Se possível, você deve jogar instâncias de classes que derive (em última instância) da classe std::exception.

... e

A prática mais comum é jogar um temporária: (veja o exemplo que se segue)

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