When catching an exception by reference is the only advantage that i get is avoiding a copy of the exception object being made? Basically the difference between
try
{
CString a_csSQL = _T("SELECT * FROM Library");
CDatabase aDB;
aDB.OpenEx(g_csConnectionStringWdDSN,CDatabase::noOdbcDialog));
aDB.ExecuteSQL(a_csSQL);
}
catch(CDBException *& ex)
{
ex->Delete();
}
AND
try
{
CString a_csSQL = _T("SELECT * FROM Library");
CDatabase aDB;
aDB.OpenEx(g_csConnectionStringWdDSN,CDatabase::noOdbcDialog))
aDB.ExecuteSQL(a_csSQL);
}
catch(CDBException * ex)
{
ex->Delete();
}
The difference between the two codes you posted is that the first one catches a pointer to an exception by reference, and the second one catches a pointer to an exception by value. In neither case is an exception copied, since you're dealing with pointers.
In general, exceptions should be thrown by value, and caught by reference. The C++ standard library is designed with this expectation in mind. However, older libraries, (MFC for instance) throw exceptions by pointer as you do here, and are expected to be caught by pointer.
There's no effective difference between catching a pointer by value and by reference, except that if you catch by reference that gives you the (completely useless) option of deleting the exception, allocating a new exception with that same pointer, and rethrowing the same exception-pointer.
If the exception is throw by a pointer you can avoid using reference.
References are really required if the exception is thrown by value.
Related
Is there any way to get handle to exception thrown inside generic catch block.
try
{
throw ;
}
catch(...)
{
// how to get handle to exception thrown
}
Thanks
You can use std::current_exception.
Rearranging from cppreference:
#include <string>
#include <exception>
#include <stdexcept>
int main()
{
eptr;
try {
std::string().at(1); // this generates an std::out_of_range
} catch(...) {
std::exception_ptr eptr = std::current_exception(); // capture
}
}
Inside the catch(...) block, the current exception has been captured by the exception_ptr eptr. The exception object referenced by an std::exception_ptr remains valid as long as there remains at least one std::exception_ptr that is referencing it: std::exception_ptr is a shared-ownership smart pointer.
The problem is that is C++, an exception is allowed to be of any type and not only a subclass of std::exception. That's the reason why the common idiom is to use only exception classes derived from std::exception to have a coherent interface.
You can always as suggested by #PaoloM use std::current_exception(). But it has some limitations that make it hard to use, because to be allowed to represent any type of exception it is a std::exception_ptr that only can (ref. in cpluscplus.com):
be default-constructed (acquiring a null-pointer value).
be copied, including being copied a null-pointer value (or nullptr).
be compared against another exception_ptr object (or nullptr) using either operator== or operator!=, where two null-pointers are always considered equivalent, and two non-null pointers are considered equivalent only if they refer to the same exception object.
be contextually convertible to bool, as false if having null-pointer value, and as true otherwise.
be swapped, and being destructed.
Performing any other operation on the object (such as dereferencing it), if at all supported by the library implementation, causes undefined behavior.
If you want to be able to to serious things with an exception, you should use dedicated exception handlers:
try
{
throw ;
}
catch (MyException& a) {
// Ok, it is a know type and I know how to deal with it
}
catch (std::exception& e) {
// it is a subclass of std::exception, I can at least use its what() method
catch(...)
{
// I can get a std::exception_ptr from current_exception, but cannot know what to do with it
}
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?
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.
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.
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;
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.
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)
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.
Is it proper to use throw new FoobarException(Baz argument); or throw FoobarException(Baz argument);?
When catching I always use catch(FoobarException& e) "just in case" but I never could find a solid answer whether I had to use new or not in C++ (Java definitely) or if it was just a preference of the programmer.
Exceptions in C++ should be thrown by value, and caught by reference.
So this is the proper way:
try
{
throw FoobarException(argument);
}
catch( const FoobarException &ex )
{
cout << ex.what() << endl;
}
Don't throw an exception created with new, since who's responsible for deleting it is not well-defined. In addition, performing allocations during error handling can throw another exception, obscuring the original problem.
You don't have to catch by const reference (non-const will work fine), but I like doing it anyway. You should however always by reference (not by value) to catch the exception polymorphically. If you don't, the exception's type could be sliced.
unless there is some special requirement not to, I always throw by value and catch by const reference. This is because the new itself could throw an exception as well, during error handling, it is best to avoid things which can cause exceptions.
Say I've written some function myFunc that can throw const char* exceptions:
void myFunc()
{
int returnCode = whatever();
if (!returnCode)
{
std::string msg;
msg.append("whatever function failed");
std::cerr << msg << std::endl; // print warning message
throw msg.c_str(); // throw warning message as exception
}
}
And later I'm using it like so:
void myProgram()
{
try
{
myFunc();
}
catch(const char* str)
{
// is 'str' string memory valid here?
}
}
I realize this isn't really a good strategy for exception usage: better to throw and catch exception classes, not strings. But I'm curious about the scoping involved here.
msg.str() returns a temporary std::string. As temporaries are deleted at the end of a statement ;, the contents of the character string returned by c_str() become undefined when the throw ... ; statement terminates by leaving the scope via the exception mechanism.
(The lifetime of the const char* temporary is obviously extended to reach to the catch handler, but that does not help since the underlying buffer is gone).
Throwing std::string (i.e. throw msg.str();) would work, the lifetime of the temporary would be extended as intended.
Indeed the c_str() call is acting on a temporary (string) object and the pointer will be invalid when you catch it.
Not only that, but since the stringstream and stringcould do allocation, you need to make sure that you're not throwing because of heap problems. If you're at that point due to being out of memory you may bomb out even worse trying to create your exception. You generally want to avoid heap allocation during exceptional cases.
Are you unable to use say runtime_error or create your own exception type?
Note that if you had said:
throw "error";
you would be OK because the lifetime of the string literal is the lifetime of the program. But don't do it, anyway!
Something Alexander Gessler did not mention in his answer, is the possibility of an exception being thrown by by std::string itself during the creation of the temporary string object.
std::exception is guaranteed not to throw an exception during construction, std::string has no such guarantee.
An alternative approach (for classes) is to declare a private std::string object in your class. Assemble the error message prior to the throw, and then throw the c_str(). This will throw a const char* exception, the error message being valid until the next exception is thrown from that class (which would presumably modify the error string again.)
A simple example can be found here: http://ideone.com/d9HhX
Although this was answered over ten years ago, I would give some supplement:
https://learn.microsoft.com/en-us/cpp/cpp/exceptions-and-stack-unwinding-in-cpp
In the C++ exception mechanism, control moves from the throw statement to the first catch statement that can handle the thrown type. When the catch statement is reached, all of the automatic variables that are in scope between the throw and catch statements are destroyed in a process that is known as stack unwinding.
That's to say, any variables in the stack, including object variable, or basic type(int, char, etc) will be destroyed.
When throw an object in c++, c++ will actually make a cloned object by invoking the object copy constructor, the cloned object lifetime will valid through throw-catch.
reference:
C++: Throwing an exception invokes the copy constructor?
Now, return to the OP question, so, "throw msg.c_str();" is invalid because the msg object
is deleted (it's a auto variable), it happens to work because the memory is still there but actually freed.
So, just as #AlexanderGessler, #MarkB suggested,
the best c++ practice is: always throw string object, runtime_error, instead of throwing string.c_str()
Consider the following code:
#include <iostream>
#include <stdexcept>
void foo()
{
throw std::runtime_error("How long do I live?");
}
int main()
{
try
{
foo();
}
catch (std::runtime_error& e)
{
std::cout << e.what() << std::endl;
}
}
Why can I catch the exception by reference, isn't std::runtime_error("How long do I live?") an rvalue?
How come the exception object is still alive in the catch block?
Where exactly are thrown exception objects stored? What is their lifetime?
In the C++ standard, paragraph 15.1.4:
The memory for the temporary copy of
the exception being thrown is
allocated in an unspecified way,
except as noted in 3.7.3.1. The
temporary persists as long as there is
a handler being executed for that
exception. In particular, if a
handler exits by executing a throw;
statement, that passes control to
another handler for the same
exception, so the temporary remains.
When the last handler being executed
for the exception exits by any means
other than throw; the temporary object
is destroyed and the implementation
may deallocate the memory for the
temporary object; any such
deallocation is done in an unspecified
way. The destruction occurs
immediately after the destruction of
the object declared in the
exception-declaration in the handler.
Note that, in C++-standard talk, a handler denote a catch block with the correct argument type.
A thrown exception is not a temporary - the compiler-generated exception code keeps a permanent copy of it. So you can bind it to a non-const reference.
[edit]
I just check the standard and it actually refers to a temporary copy. However, the lifetime of the temporary is guaranteed to be at least as long as that of the exception handler.
As Neil said, there is internal compiler magic going on. Also, be aware that the compiler is allowed to create any number of copies of the exception object.
Kudos for trying to understand the details of the language. At the same time, IMHO, it is far more important to understand why you should catch an exception by reference (and throw it by value), than why you can.
People typically use a hierarchy of exception classes, and catching by reference allows you to leverage polymorphism, and catch an exception of the base class, when there is no need to handle individual exception types separately. If you couldn't catch by reference, you would have had to write a catch clause for every possible type of exception that can be thrown in the try clause.