Treatment of exceptions in C++

Asked

Viewed 1,115 times

3

I have a question regarding the treatment of exceptions in C++, the class Fracao below is purposely incomplete does not even possess setters or getters and several things have been "set aside", has only two attributes and a constructor, and if the denominator receives zero, an exception will be thrown inside the constructor.

The code works perfectly, what I would like to know is if it is possible to do, not only the launch of the exception within the constructor/method of the class, but also the treatment of the same, ie take out the blocks try and catch of int main, and treat it somewhere else avoiding that every time I need to create any object (like Fracao in that case) have to create it within a block try, followed by a catch.

#include <iostream>
#include <exception>

using namespace std;

class Fracao
{
private:
    int numerador;
    int denominador;

public:
    Fracao(int numerador, int denominador);
};


Fracao::Fracao(int numerador, int denominador)
{
    this->numerador = numerador;

    if(denominador != 0)
        this->denominador = denominador;
    else
        throw "Impossivel dividir por zero";
}

int main()
{

    try
    {
        Fracao f1(1, 0);
    }
    catch(const char* msg)
    {
        cerr << "Erro: " << msg << endl;
    }
    catch(...)
    {
        cerr << "Erro desconhecido\n";
    }

    return 0;
}
  • Handle in which place?

  • @bigown Where would I normally be treated in a C++ program? My goal would be not to use Try catch for every object created on main.

  • And then it would be dealt with where? It’s usually done where it needs to be done. If you want to do something different, you have to justify.

  • "does not even own setters or getters": javismo ? In C++, by Meno in my experience, classes have members that need to have, there is not much this concern with "setters" and "getters"

  • @Joséx. It was just a way of saying that the class is simple, and to focus only on the problem, including thank you for your comment and agree with you.

2 answers

4


There are some possible solutions, even no exception. If the construction of the object has a contract that generates an exception when the creation fails you have to capture the exception and decide what to do.

Note that if this is considered a programming error and nothing should be done you can leave without treating the exception or treating in a generic way in just one place. And that place would probably be the function main(). It is the case that seems to be being done. I would not do otherwise. In a more complex application that calls other functions there is a great chance that I would not do otherwise. It would only give a slightly better error message, log in log or something like that. IE, capture the exception as far as possible from where it occurred. In case it can not go very far that everything takes place in the main().

If you want to do something specific and try to resolve the situation (you should only capture exceptions that can solve the situation) then you have to capture the exception in the location that it can be generated. Then it’s to capture as close as possible that makes sense.

What you can do to make it easier is create a function to manipulate the error and call it instead of treating it everywhere. This is only valid if the treatment is always the same.

Of course there maybe the class should better and does not need all this, but this is just an assumption, it depends a lot on the case. It also has reasons not to do this.

If you want to do the job would be something like this:

#include <iostream>
#include <exception>
using namespace std;

class Fracao {
private:
    int numerador;
    int denominador;
 public:
    Fracao(int numerador, int denominador);
};

Fracao::Fracao(int numerador, int denominador) {
    this->numerador = numerador;
    if (denominador != 0) this->denominador = denominador;
    elsethrow "Impossivel dividir por zero";
}

Fracao HandleExceptionFracao(int numerador, int denominador) {
    try {
        Fracao f(numerador, denominador);
        return f;
    } catch (const char* msg) {
        cerr << "Erro: " << msg << endl;
    } catch(...) {
        cerr << "Erro desconhecido\n";
    }
}

int main() {
    Fracao f1 = HandleExceptionFracao(1, 0);
}

Behold "working" in the ideone. And in the repl it.. Also put on the Github for future reference.

You can even make a generic function that serves to create any object and take care of the exception always the same way. But I still think that according to the example of the question the error is programming and the treatment should be generalized.

It would be interesting to read about exceptions on the site. Most are not for C++, but most concepts are universal. Very few programmers know how to use or understand what an exception is for and how (not) to treat it.

  • I found your answer very interesting, and the link you passed has some interesting discussions, about why/when to use or not exceptions, I’ll check. Thank you very much again

0

You can imitate what other programming languages do, such as Java, where there is an interface Trowable, all the exections released in Java must implement this interface, so the virtual machine takes charge of capturing all the exceptions, so in C++ you would do the following:

Declare your Exception class to serve as the basis for capture at all times;

class RunnableException {
private:

public:
    virtual std::string getMessage() =0;
};

Create a specific class to launch the invalid denominator exception;

class ValorInvalidoException : public RunnableException {
private:
    std::string message;

public:
    ValorInvalidoException(std::string message) {
        this->message = message;
    }

    std::string getMessage() { 
        return this->message;
    };
};

Instead of launching a const char*, you should launch an object;

Fracao::Fracao(int numerador, int denominador)
{
    this->numerador = numerador;

    if(denominador != 0)
        this->denominador = denominador;
    else
        throw ValorInvalidoException("Impossivel dividir por zero");
};

Use a single Try-catch involving all your main function;

int main() {
    try {
        //faca seu codigo do main normalmente
        Fracao f1(1, 0);
        try {

        } catch (ExcecaoEspecifica e) {
            //coloquei esse try catch aqui só para exemplicar o uso de um outro try
            //ele não tem nada haver com a captura do denominador errado
        }
        Fracao f2(2, 0);
    } catch(RunnableException re) {
        //esse catch vai pegar o denominador errado
        std::cout << t.getMessage() << std::endl;
    }
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.