【问题标题】:Is it "legal" for C++ runtime to call terminate() when the C++ code is used inside some non-C++ program?当在某些非 C++ 程序中使用 C++ 代码时,C++ 运行时调用 terminate() 是否“合法”?
【发布时间】:2010-03-24 12:47:02
【问题描述】:

在某些情况下——尤其是在堆栈展开期间异常从析构函数中转义时——C++ 运行时调用terminate(),它必须做一些合理的事后分析,然后退出程序。当出现“为什么如此苛刻”的问题时,答案通常是“在这种错误情况下没有比这更合理的做法了”。如果整个程序都是 C++ 的话,这听起来很合理。

如果 C++ 代码在库中,而使用该库的程序不在 C++ 中,该怎么办?这种情况经常发生 - 例如,我可能有一个由 .NET 程序使用的本机 C++ COM 组件。一旦在组件代码中调用terminate(),.NET 程序就会突然异常结束。程序作者首先会想到“我不关心C++,为什么这个库让我的程序退出?”

在使用 C++ 开发库时如何处理后一种情况? terminate()意外结束程序合理吗?有没有更好的方法来处理这种情况?

【问题讨论】:

  • 嗯。不要打电话给terminate(),或者做任何定义为打电话给terminate()的事情?同样的问题是否适用于exit()、abort()、引发 SIGKILL 或未定义的行为?如果您的文档说一个函数将返回,但它没有返回,那就是一个错误。程序作者首先认为“为什么这个库让我的程序退出”是正确的,其次是追捕你并要求报复。
  • @Steve:你应该这样回答!
  • @Steve Jessop:我不叫terminate() - C++ 运行时会。
  • 因为您的图书馆所做的事情 - 不要这样做。

标签: c++


【解决方案1】:

为什么 C++ 运行时调用 terminate()?它不是随机执行的,也不是由于编写代码时无法定义和/或避免的情况。这样做是因为您的代码执行了一些定义为导致调用 terminate() 的操作,例如在堆栈展开期间从析构函数中抛出异常。

C++ 标准中有一个列表,列出了所有定义为调用terminate() 的情况。如果您不想调用 terminate(),请不要在代码中执行任何这些操作。这同样适用于unexpected()、abort() 等。

我认为这与您必须避免未定义的行为或通常避免编写错误的代码这一事实没有任何不同。您还必须避免已定义但不受欢迎的行为。

也许你有一个特殊的例子,很难避免调用terminate(),但是在堆栈展开期间从析构函数抛出异常不是它。永远不要从析构函数中抛出异常。这意味着设计你的析构函数,如果它们做了一些可能失败的事情,析构函数会捕获异常并且你的代码继续在定义的状态。

在某些情况下,您的系统会因为您的 C++ 代码所做的事情(尽管不是通过调用 terminate())而无礼地破坏您的进程。例如,如果系统过度使用内存并且 VMM 无法兑现 malloc/new 做出的承诺,那么您的进程可能会被终止。但这是一个系统特性,可能同样适用于调用 C++ 的其他语言。只要您的调用者知道您的库可能会分配内存,我不认为您可以(或需要)做任何事情。在这种情况下,进程死亡不是您的代码的错,而是操作系统对低内存条件的定义响应。

【讨论】:

    【解决方案2】:

    我认为更根本的问题不是终止具体做什么,而是库的设计。该库可能被设计为仅与 C++ 一起使用,在这种情况下,可以在应用程序中适当地捕获和处理异常。

    如果该库旨在与非 C++ 应用程序一起使用,则它需要提供一个接口来确保没有异常离开该库,例如一个执行 catch(. ..)。

    【讨论】:

      【解决方案3】:

      假设您在 C++ 中有一个名为 cppfunc 的函数,并且您正在从另一种语言(例如 C 或 .NET)调用它。我建议你创建一个包装函数,比如说exportedfunc,就像这样:

      int exportsfunc(resultype* outresult, paramtype1 param1, /* ... */) { 尝试 { *outresult = cppfunc(param1,param2,/* ... */); 返回0; // 表示成功 }catch( ... ) { // 可能需要其他处理程序 /* 可能设置其他错误状态信息 */ 返回-1; // 表示失败 } }

      基本上,您需要确保异常不会跨越语言边界...因此您需要使用一个函数来包装您的 C++ 函数,该函数可以捕获所有异常并报告状态代码,或者执行除调用 std::terminate 之外的其他可接受的操作.

      【讨论】:

      • 这对调用terminate() 的情况没有帮助。一旦在堆栈展开期间抛出任何新异常并转义一个析构函数terminate(),调用堆栈上的任何尝试捕获都无法帮助解决这个问题。
      • @sharptooth,没错。但是,如果因为异常进入不支持异常处理的函数而被调用,那么这将解决这种情况。
      • @wheaties,如果你想做的不仅仅是区分成功和失败,你可以使用消息类。但是,使用负值(通常为 -1)表示失败,使用 0 表示成功是 C 中的标准约定。
      • 是的,异常不应传播到非 C++ 代码。任何理智的 COM 开发人员都会处理这个问题。
      【解决方案4】:

      默认终止处理程序将调用abort。如果您不想要这种行为,请定义您自己的终止处理程序并使用set_terminate 进行设置。

      【讨论】:

      • 你将在你自己的处理程序中做什么?标准要求终止程序的执行而不返回调用者 (18.6.3.1)。
      • 是的,这里的关键问题是不允许返回给调用者。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多