1

So I've written a small C++ class as follows:

class bad_hmean : public std::logic_error {
    const char *nature_;
    char *what_;

public:
    bad_hmean(const char *fname);

    ~bad_hmean() { delete[] what_; }

    const char *what() { return what_; }
};

inline bad_hmean::bad_hmean(const char *fname):nature_("BAD HARMONIC MEAN VALUES"), std::logic_error("BAD HARMONIC MEAN VALUES")
{
    int len = strlen(fname) + strlen(nature_)+3;
    what_ = new char [len];
    strcpy(what_, fname);
    strcat(what_, ": ");
    strcat(what_, nature_);
}

And I've tried testing it inside the following main:

void fun1() { throw bad_hmean(__func__); }

int main()
{
    try
    {
        fun1();
    } catch(std::exception& e)
    {
        std::cout << e.what();
    }
}

And when I run it I encounter one problem that I can't wrap my head around. Even though I throw the exception by value and catch it by reference, slicing still happens because the output of my code is: BAD HARMONIC MEAN VALUES. But if I catch the exception as a reference to bad_hmean the output is what I intended it to be: fun1: BAD HARMONIC MEAN VALUES.

Does anyone know why does this happen?

  • what() is virtual function so it should be called correctly because it is bound to object with virtual table and not statically. It is declared to be virtual in std::exception class. – Nikola Vugdelija Oct 02 '21 at 10:00
  • 2
    If the exception class `bad_hmean` get copied (and exceptions need to be copied) then you will have a double destruct issue (as the class breaks [The Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three)). Easy fix - replace the manually managed c-string with std::string. Preferred fix - replace the exception class with either `std::runtime_error` or your own class derived from `std::runtime_error` – Richard Critten Oct 02 '21 at 10:24

1 Answers1

3

bad_hmean doesn't override what() correctly. It should match the signature of the base class what as:

const char *what() const noexcept { return what_; }
//                 ^^^^^ ^^^^^^^^

BTW: It's better to use override specifier (since C++11) to ensure that the function is overriding a virtual function from a base class. E.g.

const char *what() const noexcept override { return what_; }
songyuanyao
  • 169,198
  • 16
  • 310
  • 405