anurag86
anurag86

Reputation: 1687

Scope of Exception object when caught(using catch) by reference

I have 3 questions :

1. It is always said to catch the exception object by reference. In the following example i see that the destructor is called before the catch block executed, that means we are referring to an object in catch which must have gone out of scope by the time we use it. Why use reference then?

class MyException{
public:
    ~MyException()  {
        cout<<"Dtor for MyException called \n";
    }
};
int main()
{
    try{
        MyException obj1;
        throw obj1;
    }
    catch(MyException& obj)
    {
        cout<<"Catched unhandled exception";
    }
}

2. Why is the destructor called twice here? Once before entering the catch block and second time after execution of catch completes.

3. The example in Lifetime of a thrown object caught by reference shows that the destructor is called just once i.e. after the catch block exits ONLY WHEN a copy constructor is defined in the class.

a. What is the role of copy constructor here when we are catching it by refrence?

b. Even after defining a copy constructor it never gets called anyways. So what impact does it make?

c. I tried to define a copy constructor in my example too as below but still i see destructor getting called twice.Why?:

MyException(const MyException& obj)
{
}

Can somebody answer all the 5 questions(3rd question has 3 parts).

Upvotes: 1

Views: 408

Answers (3)

Guvante
Guvante

Reputation: 19203

They recommend you take your catch block by reference to avoid slicing (since inheriting exceptions is standard) and to avoid needlessly copying, since throw copies unconditionally.

  1. You cannot receive obj1 by reference as throw obj1 causes it to be destroyed (since it is no longer in scope). Due to this throw always copies to temporary memory location that you don't control.

  2. obj1 and the temporary I mentioned before. obj is a reference to that temporary, the referenced temporary will be destroyed after the end of the catch block and before the next operation (similar to if it were actually local to the catch block).

  3. A single destructor only happens when the copy is elided and the standard doesn't guarantee when that will happen, as copy elision is always optional. Defining a copy constructor makes no difference as the compiler defines it for you if you don't define it (and it can be created). Deleting the copy constructor would just result in throw being illegal.

    • a. As above, copy constructor to copy into the temporary. Reference to reference said temporary.

    • b. Did you actually delete the copy constructor? MyException(const MyException&) = delete; is how you delete a copy constructor, failing to define it just causes the compiler to make one for you. And to repeat, deleting it causes throw to be illegal.

    • c. Because the copy isn't elided and two objects exist, a temporary and your object.

Upvotes: 1

Nikita Kakuev
Nikita Kakuev

Reputation: 1136

  1. throw ALWAYS makes a copy of an object you're throwing. You can't throw an object which is not copyable. If fact, throw can even fail if it can't allocate enough memory for a copy.

    This should explain why you see a destructor being called inside a try-block. It's a destructor of your local obj1. It goes out of scope as soon as your throw a copy of it.

    As for catching by reference, it is used to avoid unnecessary copying and, more importantly, potential slicing.
  2. Once again, it was a destructor of your local obj1. It went after out scope when you threw a copy of it.
  3. a) If you catch by reference, no copying is performed. Copying (with potential slicing) is performed ONLY if you catch by value.

    b) You're wrong, throw calls a copy constructor to, well, make a copy of your object and throw it. The only case when a copy constructor won't be called is when you throw a temporary object and compiler elides a copy.

    c) Refer to my first and second answer.

Upvotes: 2

Edward Strange
Edward Strange

Reputation: 40849

You're throwing a copy of the exception you created as a local temporary. So there's two: the first is destroyed when you throw, the second (the copy) after the catch block is done.

Try this:

try { throw MyException{}; }
catch (MyException const& obj) { }

Edit: If your copy constructor is really not being called in your code then my guess is that the compiler has recognized that the exception class is empty? Compiler is free to perform any optimizations it chooses so long as the behavior of the code is as-if it hadn't. Exception to this is copy-ellision but if that was going on you'd not get the double destructor call.

Upvotes: 2

Related Questions