停止执行而不跳过析构函数

7

有没有可能在不跳过析构函数调用的情况下终止软件执行?例如,在下面的代码中,由于exit(1)语句的存在,test的析构函数将永远不会被调用。

#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))的语法,可以实现第二个示例(正确的析构函数调用)的相同结果?

4
您考虑过使用异常吗?如果在函数中抛出异常,析构函数将会被调用。 - Nerdtron
1
不要使用 exit,而是抛出异常并在 main 级别捕获它。 - Kerrek SB
1
这是异常的主要目的(允许执行析构函数)。 - Steven Lu
@Nerdtron:稍作修正:如果你抛出 并捕获 一个异常,析构函数将会被调用。如果没有被捕获,那么它们是否被调用是未指定的。 - Mike Seymour
4个回答

9

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

这取决于没有其他东西捕获你的异常而不重新抛出它。


关于其他人捕获您的异常的注释非常重要。如果您想要强制执行,可以抛出一些类型而不是异常,这些类型不太可能被其他代码处理。有人知道是否可以抛出 nullptr 吗? - Michael Price
1
哦,当然,你不能避免可怕的catch(...)语法。 - Michael Price
@Michael Price:你可以创建自己的类型并抛出异常。catch(...)仍然可能存在问题。希望任何这样做的代码只是记录并重新抛出相同的异常。 - Fred Larson
2
@Michael:最好的方法就是定义一个类,将其命名为systemexit,并将所需的退出代码作为构造函数参数传入。然后抛出该异常。如果它没有std::exception作为基类,那么任何人捕获它的唯一方式是:(a) 显式捕获systemexit,在这种情况下,他们知道它的含义,希望他们有充分的理由并会重新抛出它;(b) 捕获catch(...),在这种情况下,希望他们有充分的理由,并且要么重新抛出,要么最坏的情况下终止程序。另一个风险是有人从一个noexcept(true)的函数中调用func:结果是终止程序。 - Steve Jessop
@SteveJessop - 更好的方法是,你可以重载 systemexit 类型的 () 运算符,在 catch 块中调用 myexit();(其中 myexit 是你捕获对象的名称)。你可以利用这个方法来调用任何全局变量的析构函数,因为它们没有被栈展开所触及。 - Michael Price
显示剩余3条评论

5
你可以依靠堆栈展开来实现这一点:当你想要退出时,抛出异常并在main()中捕获它。

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());
    }
}

正如在另一个答案的评论中指出的那样,全局变量会在从main返回或调用exit时被清除,因此我不确定我的运算符重载最终会给你带来什么。也许我会想到另一种用法。 - Michael Price

0

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

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

您必须从堆上分配所有对象,维护一些全局表格以指向所有实例化类实例的指针,然后只需通过表格进行迭代,在注册函数中每个实例执行delete


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