V1067. Throwing from exception constructor may lead to unexpected behavior.
Анализатор обнаружил конструктор исключения, из которого может быть брошено другое исключение. Использование такого класса может привести к неожиданному поведению программы при обработке исключений.
Рассмотрим синтетический пример:
#include <stdexcept>
class divide_by_zero_error : public std::invalid_argument
{
public:
divide_by_zero_error() : std::invalid_argument("divide_by_zero")
{
....
if (....)
{
throw std::runtime_error("oops!"); // <=
}
}
};
void example(int a, int b)
{
try
{
if (b == 0)
throw divide_by_zero_error ();
....
}
catch (const divide_by_zero_error &e)
{
....
}
// my_exception thrown and unhandled
}
В коде функции 'example' программист пытается обработать возникшее исключение 'divide_by_zero_error', однако вместо этого будет сформировано исключение 'std::runtime_error' и не перехвачено последующим 'catch'-блоком. Это приведет к тому, что исключение покинет функцию 'example', что может привести к следующим ситуациям:
- исключение будет обработано другим обработчиком исключений выше по стеку вызовов, что также может не являться желаемым поведением;
- соответствующего обработчика исключений может не оказаться выше по стеку вызовов, и тогда программа будет аварийно завершена функцией 'std::terminate' как только исключение покинет функцию 'main'.
При разработке и использовании собственных классов исключений нужно проявлять особую бдительность, поскольку исключения в их конструкторах могут возникнуть в неожиданных местах, например при вызове других функций. В следующем примере при создании логирующего исключения может возникнуть второе исключение из функции 'Log':
#include <ios>
static void Log(const std::string& message)
{
....
// std::ios_base::failure may be thrown by stream operations
throw std::ios_base::failure("log file failure");
}
class my_logging_exception : public std::exception
{
public:
explicit my_logging_exception(const std::string& message)
{
Log(message); // <=
}
};
Данная диагностика классифицируется как: