【问题标题】:std::exception's what() returns "std::exception"std::exception 的 what() 返回“std::exception”
【发布时间】:2015-04-17 16:23:34
【问题描述】:

Java 人在这里坚持做一些 c++。我正在捕获一个异常并试图诊断它来自哪里(遗憾的是,通过 gdb 运行时没有抛出异常)。但是,当我打印出异常的 what() 时,我只得到字符串“std::exception”。这是特定于标准库中的任何内容还是许多标准异常返回这个?这是我要打印的内容:

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

输出只是:

std::exception

另外,我正在一个相当大的代码库中工作,这可能来自我们这边的一些异常,但我还没有通过常规搜索技术找到它,所以我目前倾向于这个即将到来来自标准库。

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

【问题讨论】:

  • 您是否获得了可以在 gdb 中加载并进行回溯的核心转储?
  • 哪里抛出了异常?它是如何抛出的?抛出什么异常?请创建一个Minimal, Complete, and Verifiable Example 并向我们展示。
  • 这对于 GCC 库异常来说似乎很不寻常——我希望有一个更具体的派生类,如 std::runtime_error、std::length_error 等(these 之一)与 .what() 进行通信。我认为这可能来自您自己的代码库。可能无济于事,但如果我是你,我会在你自己的代码中为 'throw\s+std::exception' 尝试递归 grep,如果没有出现任何问题,你可以尝试 GCC 标头。
  • 那是 std::exception 的默认字符串吗?那是可能的。我正在寻找字符串“std::exception”,但如果那是默认字符串,那么该字符串本身可能不在代码库中。谢谢。
  • 另外,没有核心转储。我无法将它加载到 gdb 中,因为它很少发生(大约每 50k 次我运行我的测试时发生一次),并且在 gdb 中运行时它会完全消失(heisenbug)。不幸的是,我没有 MCVE,因为那样可以解决问题。在我开始对每个 STL 调用进行 try/catch 之前,我一直在寻找对 STL 异常字符串的引用或遇到类似异常的其他人。

标签: c++ exception stl


【解决方案1】:

这可能是因为两件事:

  1. 有人只是在某个地方做throw std::exception(),这不是很有帮助。

  2. 从std::exception 派生的类被复制到std::exception。这是一个名为Object Slicing 的问题。

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

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

您必须确保执行std::exception&amp; e。我知道你没有犯这个错误,但其他人可能在代码中做了进一步的事情(或者有人从 Google 来到这里)。

【讨论】:

  • 谢谢,我错过了 &。
【解决方案2】:

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++ 标准。

不要像 Java 那样依赖 C++ 异常在捕获它们后准确告诉你它们是从哪里抛出的。这超出了 C++ 标准的范围。在 C++ 中,异常其实就是一种传递执行控制流的机制。

话虽如此:许多 C++ 实现将为您提供一些特定于实现的机制来转储当前堆栈回溯,以最大限度地发挥运行时库的能力。查看 C++ 编译器的文档以获取更多信息。

例如,gcc 提供了 backtrace(),以及一些 gcc 内部函数,用于将 backtrace() 返回的原始地址转换为符号,以及用于解构符号的其他函数。使用它,可以制作出 Java 异常处理的粗略类似物;虽然 gcc 的实现并不完美,并且有一些功能漏洞,而且还需要提前规划,以及自定义异常类,其构造函数捕获当前堆栈帧(在实际抛出异常之前);并且,一旦被捕获,就可以检查抛出的异常类实例以获取捕获的回溯信息。

但这并不能真正帮助您当前的情况。我建议您按照我的建议检查您的 C++ 编译器的文档,并调查您的调试器的功能。 C++ 调试器应该允许您在抛出任何异常时以及在它被捕获之前设置断点,以便您可以在异常发生时通过调试器检查堆栈回溯。

【讨论】:

  • 谢谢,我会看看回溯。这可能使我免于对整个代码库进行二进制搜索!
  • 回溯帮助(永远无法正确解析符号,但我能够将它们保存下来,然后将它们输入 gdb 以获取位置),我最终能够偶然发现问题的根源。谢谢。
  • 这个答案听起来好像异常消息通常是在 C++ 中实现定义的,但这仅适用于 std::exception 基类,而不适用于像 std::runtime_error 这样的派生类,其中 what保证返回您在构造函数中传递的字符串。例如,§22.2.7/3 说:“后置条件:strcmp(what(), what_arg.c_str()) == 0”。 IOW,如果我创建了一个std::runtime_error x("foo"),那么what 一定不能返回"std::exception"。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-04
  • 1970-01-01
  • 2012-06-12
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
相关资源
最近更新 更多