2

I implemented my exception class which inherited from std::exception like the following :

class MyException : public exception
{
public:
    MyException(string exc)
    {
        this->exc = exc;
    }

    const char * what() const throw ()
    {
        return exc.c_str();
    }

private:
    string exc;
};

Now i throw MyException manually and suppose that an exception occurred in my code, But i tested it in two different ways in catch section :

  1. Catch normally catch (const std::exception exc)

    try
    {
        throw MyException("This is an exception");
    }
        catch (const std::exception exc)
    {
        MessageBoxA(NULL, exc.what(), "", MB_OK);
    }
    
  2. Catch with reference catch (const std::exception &exc)

    try
    {
        throw MyException("This is an exception");
    }
        catch (const std::exception &exc)
    {
        MessageBoxA(NULL, exc.what(), "", MB_OK);
    }
    

The first thrown exception string is :

Unknown exception

And the second thrown exception (The one that has & in catch section) is :

This is an exception

Why can't i get my custom exception string while using catch normally without reference(&) and why is it OK by reference?

Jarod42
  • 203,559
  • 14
  • 181
  • 302

3 Answers3

1

You are catching an std::exception by value, your compiler calls the copy ctor of std::exception and your data is lost.

Either;

  1. Catch your exception by reference, which doesn't result in copying. or
  2. Catch MyException directly.

Option 2 still performs a copy, which is redundant, so I'd go with option 1.

Doga Oruc
  • 783
  • 3
  • 16
1

This is a limit that all of polymorphism has, not just the exceptions.

When you catch by reference, you catch a reference to an existing instance of the derived class, and as such, you can call their virtual methods.

When you catch by value (which you should never do, this being one of the reasons), you catch an std::exception object as itself. Because exception is not abstract, this will, of course, work. However, you are now given a new object whose type is std::exception. As such, it never had a exc member, and more importantly, it does not have the what override that you want. Instead, the original definition of what is called, which - because you never initialized the base exception - prints "Unknown exception".

IWonderWhatThisAPIDoes
  • 1,000
  • 1
  • 4
  • 14
1

You are experiencing object slicing.

This happens when a derived instance is received by value as its base class -- In your case, catching std::exception by value. Your derived class' member is "sliced off" in the process of conversion to the base class.

Receiving by pointer or by (const) reference is always preferable when you expect to use a parameter polymorphically. For catching a std::exception you should always catch by const reference (I can't really think of a scenario where you wouldn't).

AndyG
  • 39,700
  • 8
  • 109
  • 143