V509. Exceptions raised inside noexcept functions must be wrapped in a try..catch block.
In case an exception is thrown in a C++ program stack unwinding begins which causes objects to be destroyed by calling their destructors. If a destructor invoked during stack unwinding throws another exception and that exception propagates outside the destructor the C++ runtime immediately terminates the program by calling terminate() function. Therefore destructors should never let exceptions propagate - each exception thrown within a destructor should be handled in that destructor.
The analyzer found a destructor containing the throw operator outside the try..catch block. Here is an example:
LocalStorage::~LocalStorage()
{
...
if (!FooFree(m_index))
throw Err("FooFree", GetLastError());
...
}
This code must be rewritten so that the programmer is informed about the error that has occurred in the destructor without using the exception mechanism. If the error is not crucial, it can be ignored:
LocalStorage::~LocalStorage()
{
try {
...
if (!FooFree(m_index))
throw Err("FooFree", GetLastError());
...
}
catch (...)
{
assert(false);
}
}
Exceptions may be thrown when calling the 'new' operator as well. If memory cannot be allocated, the 'bad_alloc' exception will be thrown. For example:
A::~A()
{
...
int *localPointer = new int[MAX_SIZE];
...
}
An exception may be thrown when using dynamic_cast<Type> while handling references. If types cannot be cast, the 'bad_cast' exception will be thrown. For example:
B::~B()
{
...
UserType &type = dynamic_cast<UserType&>(baseType);
...
}
To fix these errors you should rewrite the code so that 'new' or 'dynamic_cast' are put into the 'try{...}' block.
Also, since C++11, functions can be marked as 'noexcept'. Throwing exceptions from such functions will lead to program termination. The analyzer detects cases where potentially throwing operations are performed in 'noexcept' functions. Here's an example:
int noexceptWithNew() noexcept
{
return *(new int{42});
}
The analyzer will issue a warning in this case, since the 'new' operator can potentially raise an exception. The call to 'new' should be wrapped in a 'try..catch' block.
In addition to that, the analyzer detects calls for functions not marked as 'noexcept' from destructors and 'noexcept' functions. This is a potentially dangerous operation, since such functions can throw exceptions. Consider this example:
int allocate_memory()
{
return *(new int{ 42 });
}
int noexceptFunc() noexcept
{
return allocate_memory();
}
Here, the analyzer will issue a warning at the line with the 'allocate_memory' function call.
Note that even if a function is not explicitly marked as 'noexcept', but the analyzer doesn't detect any throwing operations in it, the warning will not be issued.
Additional materials on this topic:
- Bjarne Stroustrup's C++ Style and Technique FAQ. Can I throw an exception from a constructor? From a destructor? http://www.stroustrup.com/bs_faq2.html
- Throwing destructors. http://www.kolpackov.net/projects/c++/eh/dtor-1.xhtml
This diagnostic is classified as:
You can look at examples of errors detected by the V509 diagnostic. |