Reputation: 3483
We have an custom error class that is used whenever we throw an exception:
class AFX_CLASS_EXPORT CCLAError : public CObject
It has the following copy constructor defined:
CCLAError(const CCLAError& src) { AssignCopy(&src); } // (AssignCopy is a custom function)
It was originally written and compiled / linked with MSVC6 (Visual Studio 2003). I am in the process of doing necessary changes to get it to compile and link against MSVC8+ (VS 2008+)
When the msvc8 linker is invoked, i get the following error:
LNK2001: unresolved external symbol "private: __thiscall CObject::CObject(class CObject const &)" (??0CObject@@AAE@ABV0@@Z)
I understand what the error is telling me: no copy constructor is defined for some child of CObject, so its going all the way up the inheritance tree until it hits CObject, which as no copy constructor defined.
I first saw the error when compiling the library that defines and first throws a CCLAError
, which is why I am proceeding as if that is the cause.
I was able to resolve the error by changing
throw CCLAError( ... )
to
throw new CCLAError( ... )
and
catch(CCLAError& e)
{
throw e;
}
to
catch(CCLAError& e)
{
throw;
}
However, I do not understand why re-throwing a caught exception would invoke the copy constructor. Am I missing somethnig completely obvious? Subsequently, why would removing the variable holding a reference to a caught exception cause the copy constructor to not be invoked?
Upvotes: 4
Views: 6514
Reputation: 1673
It appears that you have misunderstood what re-throw means. When you do -
catch(CCLAError& e)
{
throw e;
}
-- you are NOT rethrowing. Instead, as you have observed, you are indeed creating a copy of the new exception. Instead (again, as you have found for yourself), this is what is technically the correct way to re-throw:
catch(CCLAError& e)
{
throw;
}
Read chapter 14 in Stroustrup's TC++PL (14.3.1 deals with rethrowing).
Also, you do NOT have to do -
throw new CCLAError( ... )
Instead, do -
throw CCLAError( ... )
-- like you were doing before, only receive by CONST reference (you cannot hold a reference to a temporary).
catch(const CCLAError &e)
Upvotes: 0
Reputation: 308111
When you throw an exception, the object you're throwing generally resides on the stack. The stack is getting cleaned up as part of the process of throwing, so the compiler must make a copy that can continue to live beyond that point.
When you throw an object with new
, you're not throwing the actual object but you're throwing a pointer to the object. That means your catch
block must also catch a pointer rather than a reference. Don't forget to delete
the pointer or you'll have a memory leak!
When the catch block uses throw;
instead of throw e;
it can reuse the copy it made earlier and there's no need to make another copy.
Upvotes: 1
Reputation: 354979
The type of the object thrown must be copyable because the throw
expression may make a copy of its argument (the copy may be elided or in C++11 a move may take place instead, but the copy constructor must still be accessible and callable).
Rethrowing the exception using throw;
will not create any copies. Throwing the caught exception object using throw e;
will cause a copy of e
to be made. This is not the same as rethrowing the exception.
Your "updated" code does not work as you expect. catch (CCLAError&)
will not catch an exception of type CCLAError*
, which is the type of the exception thrown by throw new CCLAError(...);
. You would need to catch CCLAError*
. Do not do this, though. Throw exceptions by value and catch by reference. All exception types should be copyable.
Upvotes: 13
Reputation: 31233
Several points:
1st of all don't call throw new foo()
use throw foo
2nd when your write:
catch(foo const &e) {
throw e;
}
You actually create a new exception, and if for example the exception that
was thrown is subclass of foo
they my calling throw e
you would loose this
information and actually use a copy constructor to generate foo
from e - whatever
it was.
Now when you call
catch(foo const &e) {
throw;
}
You don't create a new exception but rather propagate the same exception.
So: never use throw e;
to propagate the exception forward, use throw;
Upvotes: 4
Reputation: 361262
The language specification allows the compilers to make as many copies of the original object as they want. There is no restriction on the number of copies.
That means, your custom exception class must have an accessible copy-constructor, either the compiler generated one, or user-defined one.
Upvotes: -1
Reputation: 81349
However, I do not understand why re-throwing a caught exception would invoke the copy constructor.
It doesn't, but re-throwing the thrown exception is done with throw;
. When you do throw e;
you are requesting to throw a copy of the caught exception.
Upvotes: 5