重新抛出异常的自定义错误消息未被what()打印。

12
我正在编写一组扩展std::exception的自定义异常。在某些代码中,当捕获到异常时,我只需将其向上抛出到驱动程序main函数调用catch并打印结果。然而,最终打印的所有内容都是"std::exception"。这似乎不是我之前处理过的作用域问题为什么我的异常消息没有打印出来? 我的异常代码:
// General exception class
struct MyException : public std::exception
{
    std::string _msg;

    MyException(const std::string &exception_name) : _msg(exception_name) {}

    void setMessage(const std::string &message)
    {
        _msg += ": " + message + "\n";
    }

    void setLocation(const char * func, const char * file, const int line)
    {
        _msg += "  In function " + std::string(func) + "(" + file + ":" + std::to_string(line) + ")";
    }

    const char * what() const throw()
    {
        return _msg.c_str();
    }
};

// Specializations of the MyException
struct FileNotFoundException : public MyException
{
    FileNotFoundException() : MyException("FileNotFoundException") {}
};

struct IOException : public MyException
{
    IOException() : MyException("IOException") {}
};
struct DBException : public MyException
{
    DBException() : MyException("DBException") {}
};

所有我的异常抛出都包裹在这个宏中

#define EXCEPTION_THROWER(ET, message)              \
    {                           \
        ET e;                       \
        e.setMessage(message);              \
        e.setLocation(__func__, __FILE__, __LINE__);    \
        throw e;                    \
    }

也被称为

EXCEPTION_THROWER(DBException, "Blah blah database exception")
中间的try/catch块如下所示:

中间的try/catch块如下所示:

try
{
    // Call a function that throws an exception
}
catch(const std::exception &e)
{
    throw e; // Forward any exceptions
}

并且驱动程序代码都在一个try块中,并具有一个catch(const std::exception &e)块。


1
你是如何在 main 中捕获异常的?为什么要重新抛出异常?如果你不捕获它们,那应该是默认行为。 - Daniel H
1
如果异常没有立即被捕获,它会继续传递到堆栈中下一个函数。如果异常一直未被捕获,直到 main 函数,程序将调用 std::terminate 函数,导致程序异常退出。 - Daniel H
1
@marcman 这里有一个关于堆栈展开的描述 cppreference.com - Blastfurnace
1
特别是,这个部分 - Daniel H
1
离题:你可以通过使用更好的构造函数来摆脱宏和设置方法,这些构造函数接受您希望为特定类型的异常提供的任何参数和可用信息。 - user4581301
显示剩余2条评论
1个回答

18

throw e; 将会执行大量的 对象切片,因为它实际上是将任何 e 切片到一个 std::exception(并且 _msg 将会丢失)。

使用 throw; 通过引用重新抛出已捕获的异常。


1
虽然它并不像对象切片那样糟糕(可能很快导致许多未定义的行为),但它仍然不好,也不是你想要的结果。 - Daniel H
1
@DanielH 如果这种情况(从派生类型对象初始化基本类型对象)不符合,那么你对对象切割的定义是什么? - Angew is no longer proud of SO
1
@Angew 我想在初始化时发生了对象切片,但随后使用了一个已初始化的独立对象。我认为这更像是使用子对象并保留其所属对象,以便整个对象的状态被破坏。我想我当时考虑的是另一个问题。 - Daniel H
坦白地说,除非异常最终被_value_捕获,否则我看不到切片发生的地方; 基对象在哪里初始化?参数e是一个(const)引用,如果它被用作调用函数g(const std::exception&)的参数,则引用将被传递而不创建任何对象。据我所知,抛出和捕获错误使用与函数调用相同的机制,因此只要所有捕获都是通过引用进行的,就不会在抛出时发生切片。 - Marc van Leeuwen
好的,我看到了我的错误:在阅读throw的描述时,我发现会创建一个临时对象,其类型为操作数的(去除cv限定符后的)_静态_类型。虽然这并没有提到引用类型会发生什么,但我认为这只能意味着它被忽略了,因为没有引用类型的对象。因此,尽管同一部分说“throw的操作数在调用中被视为函数参数”,但是即使抛出和通过引用捕获,这里也会调用复制构造函数。 - Marc van Leeuwen
@MarcvanLeeuwen:是的,大致就是这样。但请注意throw something;throw;之间的重要区别。 - Bathsheba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接