【问题标题】:Stop execution without skipping destructors停止执行而不跳过析构函数
【发布时间】:2012-01-08 23:04:04
【问题描述】:

是否可以在不跳过对析构函数的调用的情况下终止软件执行?例如,在下面的代码中,test 的析构函数将永远不会因为 exit(1) 语句而被调用。

#include <iostream>
#include <cstdlib>
using namespace std;

class A{
public:
    A(){cout << "Constructed.\n";}
    ~A(){cout << "Destroyed.\n";}
};

void func()
{
    //Assuming something went wrong:
    exit(1);  
}

int main(int argc, char *argv[])
{
    A test;
    func();
    return 0;
}

我需要的是一种结束程序的方法(从func() 中),该程序在终止之前调用所有必要的析构函数。到目前为止,我一直在通过func() 返回值处理这个问题,如:

bool func()
{
    //Assuming something went wrong:
    return false;
}

int main(int argc, char *argv[])
{
    A test;
    if( !func() )return 1;
    return 0;
}

这种方法的问题在于,一旦您需要将其应用于一系列嵌套函数,管理起来很快就会变得非常烦人(并且代码臃肿)。

有没有一种方法可以使用与第一个示例类似的语法(无论您身在何处都调用exit(1))来实现与第二个示例相同的结果(正确的析构函数调用)?

【问题讨论】:

  • 你考虑过使用异常吗?如果你在函数中抛出异常,就会调用析构函数。
  • 不要使用exit;相反,抛出一个异常并在main 级别捕获它。
  • 这是异常的主要目的(允许执行析构函数)。
  • @Nerdtron:稍微更正:如果你抛出并捕获异常,析构函数将被调用。如果没有被捕获,那么是否调用它们是未指定的。

标签: c++ destructor terminate


【解决方案1】:
struct my_exit
{
    int error;
    int operator()()
    {
        // do any cleanup on globals
        return error;
    }
};

int main()
{
    try
    {
        doSomethingThatCouldCauseExit();
    }
    catch (my_exit & me)
    {
        // Clean up globals now
        exit(me());
    }
}

【讨论】:

  • 正如 cmets 在另一个答案中指出的那样,只需从 main 返回或调用 exit 就可以清理全局变量,所以我不确定我的运算符重载最终会给你带来什么.也许我会想到它的另一种用法。
【解决方案2】:

有几种方法可以干净地做到这一点。

一种解决方案是使用atexit 函数,它只是在程序终止时调用给定的函数指针。

您必须在堆外分配所有对象,维护一些带有指向所有实例化类实例的指针的全局表,然后简单地遍历表 delete 注册函数中的每个实例。

【讨论】:

    【解决方案3】:

    你可以依赖stack unwinding:当你想退出时,抛出一个异常并在main()中捕获它。

    【讨论】:

      【解决方案4】:

      抛出异常,在main 中捕获并返回。

      这依赖于在不重新抛出异常的情况下捕获您的异常。

      【讨论】:

      • 关于其他人发现你的异常的注释很重要。如果你想变得粗暴,你可以抛出异常以外的其他类型,这是其他代码不太可能处理的。有谁知道你能不能扔nullptr
      • 哦,当然,你无法避免可怕的catch(...) 语法。
      • @Michael Price:你可以制作自己的类型并扔掉它。 catch(...) 仍然可能是个问题。希望任何这样做的代码都只是记录并重新抛出相同的异常。
      • @Michael:最好的办法是定义一个类,称之为systemexit,它将所需的退出代码作为构造函数参数。扔那个。如果它没有 std::exception 作为基类,那么任何人都可以捕获它的唯一方法是:(a) 明确捕获 systemexit,在这种情况下,他们知道这意味着什么,所以希望他们这样做是为了一个很好的理由,并且会重新抛出它; (b) catch(...),在这种情况下,我们希望他们这样做是有充分理由的,要么重新抛出,要么在最坏的情况下终止。另一个风险是有人从noexcept(true) 函数调用func:结果是终止。
      • @SteveJessop - 更好的是,你可以在 systemexit 上重载 () 运算符,在你的 catch 块中你可以说 myexit(); (其中 myexit 是你的对象的名称捕捉)。您可以使用它来为任何不会被调用的全局变量调用析构函数,因为它们不会被堆栈展开所触及。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-10
      • 2017-09-01
      • 1970-01-01
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      • 2016-06-08
      相关资源
      最近更新 更多