简体   繁体   中英

Exceptions & C-style strings

I've problem with exceptions:

#include <iostream>
#include <cstring>
#include <exception>

class except :public std::exception
{
    char* err;
public:
    except(const char* s) noexcept
    {
        err = new char[strlen(s) + 1];
        strcpy(err, s);
        err[strlen(s)] = 0; //is it necessary??
    }
    virtual const char* what() noexcept
    {return err;}
    virtual ~except() noexcept
    {delete err;}
};

double div(const double& a, const double& b) noexcept
{
    if (b == 0.0)
        throw except("DIVIDED BY 0");
    return a / b;
}

int main()
{
    try
    {
        std::cout << div(5.0, 0.0);
    }
    catch (std::exception &ex)
    {
        std::cout << ex.what();
    }
    return 0;
}

I want to print "DIVIDED BY 0" but I get:

terminate called after throwing an instance of 'except'
  what():  std::exception
Aborted

Process returned 134 (0x86)

Shall I put noexcept in every function/member function that doesn't throw any exceptions?

Is err[strlen(s)] = 0 necessary? (in except::except(const char* s))

I want to print "DIVIDED BY 0" but I get: terminate called after throwing an instance of 'except'

double div(const double& a, const double& b) noexcept

You are claiming that you won't throw an exception, but you are lying. The result is the call to std::terminate when you actually throw.

The problem is a actually twofold. Firstly, as @manni66 suggests, div should not be declared noexcept and this does cause a run-time error, but removing it does not solve the issue.

The second problem is that what has been declared:

virtual const char* what() noexcept;

whereas it's declared in std::exception as:

virtual const char* what() const noexcept;

and this difference in signature means that when it's caught by the handler for std::exception , it calls std::exception::what() and not except::what()

A couple of points worth mentioning:

  1. Ensure that your function overloads exactly match those in the base class.
  2. If you are expecting a certain type of exception to be thrown then try to catch that first with a specific handler and use an appropriate name for the exception for clarity.
  3. As others have mentioned, please try to use the std::string class if you can. It will make things easier and safer. Although in this case, as point two eludes to, a string member is not really necessary as the class is specific enough not to need further qualification.
  4. Don't create an exception class unless it's adding some specific value (though the value , may simply be that you want to specifically catch a certain type of exception and handle in a certain way.) If you wanted a more general exception then std::runtime_error or in this case std::overflow_error would be decent choices and both take a std::string as an argument to the constructor, so no need to create your own exception class.

For example:

#include <exception>
using namespace std;


class divide_by_zero : public std::exception
{
public:
    virtual const char* what() const noexcept { return "DIVIDED BY 0"; }
};

double div(const double& a, const double& b)
{
    if (b == 0.0)
        throw divide_by_zero();
    return a / b;
}

int main()
{
    try
    {
        std::cout << div(5.0, 0.0);
    }
    catch (divide_by_zero &ex)
    {
        std::cout << ex.what();
    }
    catch (std::exception &ex)
    {
        std::cout << ex.what();
    }
    return 0;
}

Output:

DIVIDED BY 0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM