【问题标题】:Terminate application AND call the destructors of local objects终止应用程序并调用本地对象的析构函数
【发布时间】:2011-08-03 02:33:36
【问题描述】:

我在主函数的堆栈上有一些对象:

int main(...)
{
   CFoo foo;
   CBar bar;
}

另外,我有一个函数可以跟踪我的应用程序中的错误:

void Err(std::string msg)
{
   SomehowLogErrorMessage(msg);
   exit(1);
}

当我必须报告致命错误时,Err 函数绝对有用。我只是记录错误并终止应用程序 - 在出现此类错误后它无法恢复。但是,以“exit()”终止并不会调用 foo 和 bar 析构函数——这是我实际上预期的一种行为(但这是错误的)。 “中止()”也无济于事。另外,我不能使用异常 在 main() 中捕获它们。有没有其他方法来实现 Err 函数,它会终止应用程序并正确清理堆栈对象?或者我应该以某种方式重新设计我的错误处理?

谢谢!


附言顺便说一句,我不能将 WM_QUIT 消息发送到我的主窗口吗?我不擅长使用 WinAPI,但我的应用程序是纯 Win32 并且我的 Err() 函数可以获取我的主窗口的句柄。它会起作用吗?

【问题讨论】:

  • 明显愚蠢的问题,但你为什么不能使用异常?
  • 如果你不能抛出异常(为什么不呢?)另一个选择是返回一个错误代码,然后将它一直传播到 main 的末尾。
  • 比如说,特定于平台的限制。项目使用 -fno-exceptions 标志编译。
  • 如果很重要,我想补充一点,我不关心整个调用堆栈的本地对象,我只想清理 main() 中的对象。我不确定现在是否可以......
  • "项目是使用-fno-exceptions 标志编译的" = 项目实际上并未使用 C++,这是一种需要异常支持的语言。

标签: c++ windows stack destructor exit


【解决方案1】:

并非没有异常或从 Err 一直到调用堆栈正常返回。您需要展开堆栈。

【讨论】:

    【解决方案2】:

    还有 C 的 setjmplongjmp。它应该带你回到main,所以程序会在离开main 的范围后照常终止,在这种情况下,C++ 的析构机制会破坏main 中的本地对象。

    当然,在 C++ 中使用 longjmp 是不受欢迎的,这是有充分理由的。它会跳过堆栈上的任何其他函数,因此它实际上只适用于main 中的几个堆栈对象。

    在堆上分配对象并在deleteErr 中手动分配它们可能更容易。

    【讨论】:

    • 并且还跳过 Err 和 main 之间的那些(不在要求中?:-)
    • 好吧,这会很糟糕,但至少它会让我们回到 Main() ......如果我是 OP,我真的会重新设计我的错误处理而不是到处乱搞。
    • 我认为说 C 运行时函数将调用析构函数并不安全。甚至手册页都说寄存器和自动变量将具有未定义的值。
    【解决方案3】:

    在 C++ 中传播错误基本上有两种方法:异常(您似乎随意排除了)和返回码。

    由于您不能使用异常,您将需要开始从您的函数中传递可能失败的返回代码并测试它们以查看您是否需要停止并返回到 main。如果您不这样展开堆栈,则无法保证正确调用析构函数。

    还要考虑如果您的程序处于致命状态,析构函数实际上是否能够按预期进行清​​理?如果对象状态有缺陷并且无法正确删除怎么办?您可以调用abort 而不是调用exit,如果您进入不良状态,这至少会留下一个核心来帮助诊断问题。对于异常不可用的致命错误,这是一个合理的选择。

    【讨论】:

    • 好吧,程序没有处于致命状态(没有任何损坏)。但是接下来的工作是没有意义的,这就是为什么我一直在寻找一种方式离开这里和现在。我希望避免一堆不必要的返回值检查和传播代码......
    • 换句话说,“我想要的正是异常提供的东西,但我不能使用异常”
    【解决方案4】:

    我相信Exit 会立即终止申请。要查看您期望的行为 foobar 必须超出范围。这意味着主函数必须以返回值正常结束。与其从 Err 函数中调用 Exit(),不如找到一种方法返回到 main 函数并让它正常返回并返回错误值。

    【讨论】:

      【解决方案5】:

      我会重新设计您的错误处理。最直接的做法是将控制权返回给main,并让它进行正常的清理工作。异常通过堆栈展开提供此功能,但您不能使用异常。好没问题。有时你就是不能使用异常。

      因此,与其让Err 尝试关闭程序,不如让Err 记录错误并返回错误代码。理想情况下,Err 无论如何都不应该做两份工作,而您正试图让它做:1) 记录错误,2) 优雅地终止应用程序。

      如果您的应用程序碰巧是多线程的,那么您可能会很容易搞砸。在错误记录线程中,发出“死亡”事件的信号。让主线程等待这个事件以及它等待的任何其他事件(或通过QueueUserAPC 或类似方法向它注入一个作业)。当事件发出信号时,关闭应用程序。

      【讨论】:

      • 这将迫使我在调用堆栈中一直分析错误代码。这就是我试图避免的,现在我想 C++ 是不可能的。更糟糕的是,可以在对象构造函数中调用 Err() - 我如何从那里传递错误代码?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-24
      • 2023-03-20
      • 2012-04-29
      • 1970-01-01
      • 2020-03-14
      • 2016-01-29
      相关资源
      最近更新 更多