1

I encountered a very strange (at least to me) behaviour of a exception class I have thrown. What I do is that I allocate memory via new for a string in the constructor of the exception class and fill it with characters. So far everything is fine. When debugging the code I can see in Visual Studio that the pointer actually has the right content.

Now the weird thing happens. My next breakpoint is in the catch - block to which the exception is passed after being constructed and here I can see in the debugger that the content of the string contained in the exception object is severly corrupted. Even though the address didn't change at all! So it seems like the content of the string gets destructed.

So I put a breakpoint into the exceptions destructor and really, it is being called before the catch - block is entered. This confuses me a lot since I learned to pass exceptions by reference to the catch block. But what good is that if the destructor gets called before I can access the dynamically created data...

I constructed a minimal example that shows the situation I am in:

#include <iostream>
#include <cstring>

class test_exception {
public:
    test_exception();
    ~test_exception() {
        delete[] _msg;
    }

    // Getter Functions
    char* errorMessage() const {
        return _msg; 
    }
private:
    char* _msg;
};

test_exception::test_exception()
{
    _msg = new char[22];
    strcpy(_msg, "This is a test string");
}

int main(int argc, char* argv[])
{
    try {
        throw test_exception();
    } catch (const test_exception& err) {
        std::cout << err.errorMessage() << std::endl;
    }

    std::cin.get();

    return 0;
}

It would be create if someone could tell me if it is weird MS behaviour or if I misunderstood how try - catch - blocks should be used.

Cyianor
  • 45
  • 5

2 Answers2

3

Exceptions are copied (or in C++11, possibly moved) when they're thrown. Quoting C++11, §15.1/3:

A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively. The temporary is an lvalue and is used to initialize the variable named in the matching handler. If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed. Except for these restrictions and the restrictions on type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a call or the operand of a return statement.

Because test_exception violates the rule-of-three (or for C++11, the rule-of-five), test_exception::_msg has already been deleted by the time you enter your catch block.

Community
  • 1
  • 1
ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • Thank you! I didn't know that. Since I was using a reference in the catch block I thought it was using the object that I created when throwing the exception. But of course this explains it. Also thank you for the link! – Cyianor Apr 11 '12 at 16:30
0

Since exceptions are copied, you should add a copy constructor to your test_exception object. The exception thrown is not the same one as the one received by the catch.

Freddy
  • 3,064
  • 3
  • 26
  • 27