std::exception的what()函数返回"std::exception"。

20

我是一名Java程序员,正在尝试写一些C++代码。我遇到一个异常并试图诊断它的出处(遗憾的是,使用gdb运行时未抛出该异常)。然而,当我打印异常的what()方法时,只得到字符串"std::exception"。这是标准库中特定的内容还是许多标准异常都会返回这个字符串呢?这是我的打印代码:

    } catch (const std::exception & ex) {
      std::cout << ex.what() << std::endl;
    }

输出结果就是:

std::exception

此外,我正在使用一个相当大的代码库,有可能这是因为我们自己的异常所导致的,但是我还没有通过常规的搜索技术找到它,因此我目前倾向于认为这来自标准库。

如果相关的话,我正在使用g++ 4.8。


你是否得到了一个可以在gdb中加载并进行回溯的核心转储? - Retired Ninja
异常抛出在哪里?它是如何抛出的?抛出了什么异常?请创建一个最小、完整和可验证的示例并向我们展示。 - Some programmer dude
这似乎是GCC库异常的不寻常之处 - 我希望会有一个更具体的派生类,如std ::runtime_errorstd ::length_error等(在此处之一)用.what()进行通信。我认为这可能来自您自己的代码库。可能没有帮助,但如果我是您,我会在自己的代码中尝试递归grep 'throw\s+std:: exception',如果没有找到任何内容,您可以尝试使用GCC头文件。 - Tony Delroy
那么这是 std::exception 的默认字符串吗?这是有可能的。我正在使用字符串“std::exception”进行搜索,但如果那是默认字符串,那么该字符串本身可能不在代码库中。谢谢。 - Pace
另外,没有核心转储。我无法将其加载到gdb中,因为这是一个相当罕见的情况(每次运行测试时大约发生一次),并且在gdb中运行时完全消失(Heisenbug)。不幸的是,我没有MCVE,因为那样就可以解决问题了。我更多地是在寻找STL异常字符串的参考或者是否有其他人遇到过类似的异常,然后再开始在每个单独的STL调用周围放置try/catch。 - Pace
2个回答

34

可能是由于以下两个原因之一:

  1. 有人在某个地方执行了 throw std::exception(),这并没有提供太多帮助。

  2. std::exception 派生的类被复制到 std::exception。这是一个称为对象切割的问题。

实际上我自己犯了第二个错误。我有这段代码:

try
{
    // Some Boost stuff
}
catch (std::exception e)
{
    cerr << e.what() << endl;
}

你必须确保使用 std::exception& e。我知道你没有犯这个错误,但代码中可能会有其他人犯了这个错误(或者从Google搜索到这里的人会犯这个错误)。


8
谢谢,我漏掉了 & 符号。 - Tuntable

5

C++异常与Java异常完全不同。

C++标准规定what()返回的字符串完全是任意的,并且由实现定义:

virtual const char* what() const noexcept;

Returns: An implementation-defined ntbs.
Remarks: The message may be a null-terminated multibyte string
(17.5.2.1.4.2), suitable for conversion
and display as a wstring (21.3, 22.4.1.4). The return value remains 
valid until the exception object
from which it is obtained is destroyed or a non-const member
function of the exception object is called.

您得到的返回值 "std::exception" 完全符合 C++ 标准。不要依赖 C++ 异常来告诉您它们在何处抛出并被捕获,就像 Java 一样。这超出了 C++ 标准的范围。在 C++ 中,异常实际上只是一种传递执行控制流的机制。
尽管如此:许多 C++ 实现都会为您提供一些特定于实现的机制来转储当前堆栈回溯信息,以尽最大努力。请查阅您的 C++ 编译器文档以获取更多信息。
例如,gcc 提供了 backtrace() 函数,以及一些 gcc 内部函数来将 backtrace() 返回的原始地址转换为符号,并提供其他函数来解码符号。使用这个,可以构建一个粗略的类似于 Java 异常处理的模拟;虽然 gcc 的实现不完美,存在一些功能漏洞,还需要事先规划和自定义异常类,其构造函数捕获当前堆栈帧(在实际抛出异常之前);并且,在捕获后,可以检查抛出的异常类实例以获取捕获的回溯信息。
但这并不能真正帮助您当前的情况。我建议您按照我的建议查看您的 C++ 编译器文档,并调查您的调试器功能。C++ 调试器应该允许您在任何异常被抛出并被捕获之前设置断点,以便在异常发生时通过调试器检查堆栈回溯信息。

谢谢,我会查看一下回溯。这可能会避免我在整个代码库中进行二进制搜索! - Pace
回溯功能帮了我大忙(虽然我一直无法正确解析符号,但我能够将它们保存下来,然后稍后将它们输入到gdb中以获取位置),最终我成功地找到了问题的根源。谢谢。 - Pace
3
这个答案听起来好像在C++中,异常信息通常是由实现定义的,但这仅适用于std::exception基类,而不适用于派生类,比如std::runtime_error。在派生类中,what保证返回您在构造函数中传递的字符串。例如,§22.2.7/3表示:“后置条件:strcmp(what(), what_arg.c_str()) == 0”。也就是说,如果我创建了一个std::runtime_error x("foo"),那么what不能返回"std::exception" - Christian Hackl

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