C++ 在这种情况下,从析构函数抛出是否合适

C++ 在这种情况下,从析构函数抛出是否合适,c++,exception,destructor,C++,Exception,Destructor,我有一个类,它将用户提供的工作分配给多个线程。类似于(简化的): 用户按如下方式使用该类: void foo() { MT mt; mt.work1(/*userdata*/); mt.work2(/*userdata*/); mt.work1(/*userdata*/); status = mt.is_ok(); if (status) { mt.work3(/*userdata*/); //... }

我有一个类,它将用户提供的工作分配给多个线程。类似于(简化的):

用户按如下方式使用该类:

void foo()
{
    MT mt;
    mt.work1(/*userdata*/);
    mt.work2(/*userdata*/);
    mt.work1(/*userdata*/);
    status = mt.is_ok();
    if (status) {
        mt.work3(/*userdata*/);
        //...
    }
    //...
}
类永远不是某个对象的一部分,总是存储在堆栈上

问题 我希望以某种方式发出在其他线程中执行工作时引发的任何异常。不幸的是,我知道一个线程是否只有在加入它之后才成功完成。因此,我必须在以下两种选择中作出选择:

  • MT
    的析构函数中加入线程,并抛出在执行工作时出现的异常(如果有)。如果引发了多个异常,请选择来自最早任务的异常。如果析构函数被调用为执行堆栈展开(我们可以使用检查,吞下任何异常以防止
    terminate()

  • 指示用户始终在析构函数之前调用
    is_ok

我认为第一个选项更简洁,因为用户不需要调用任何东西。但是,通常非常不鼓励从析构函数抛出。提供的参数有:

  • 从析构函数抛出是危险的,因为这可能在堆栈展开期间发生,并导致
    terminate()
  • 即使解决了上述问题,根据堆栈是否被解开,投掷或不投掷也是行为的实质性改变,应予以劝阻
  • 异常表示未满足后置条件。析构函数的后置条件是资源清理,这在任何情况下都必须是可能的
不知何故,我倾向于认为上述论点不适用于这里:

  • 我正确地处理了堆栈展开问题
  • 根据使用模式,从
    foo
    退出的任何异常都意味着工作已失败。抛出或不抛出析构函数并不是行为的实质性改变
  • 析构函数的后置条件不仅是资源清理,而且是工作已成功完成。因此,异常应该是适当的
问题
  • 你认为在这种情况下,从析构函数抛出是合适的吗

不,不要从析构函数中抛出。调用
可以
,捕获并忽略。与流的
close()
相同

如果用户想确保工作已经完成,可以调用
is_ok
,失败时会抛出异常

在实践中,这很少不方便。如果用户编写一个具有多个返回点的函数,则会出现这种情况,但是(不幸的是)这是一个他们必须处理的问题,因为C++没有提供给你一个工具来做。如果你认为这是反社会的,那么,看看如果你在C++ 11中销毁<代码>::线程< /代码>,而不先加入它或拆开它,你会怎么办。 因此,在所有非错误退出路径上,用户调用
是正常的。当已经存在异常时,用户不必费心调用它,因为他们无论如何都无法处理另一个异常。同样,这与流的
close()
相同:如果您希望看到写入缓冲流时出现的错误,则只需显式关闭或刷新即可

即使解决了上述问题,投掷还是不投掷取决于 堆栈是否被解开是行为和性能的重大变化 应该劝阻

至少在C++03中,也不可能正确执行。我不知道C++11在这方面是否有任何改变,但是
std::uncaught\u exception
并没有告诉您需要知道什么

从您到cppreference.com的链接:

std::uncaught_exception()==true调用std::terminate时引发的任何异常

我很确定这是假的<如果异常从作为堆栈展开一部分调用的析构函数中转义,则调用code>terminate
,但不会仅因为析构函数在展开过程中抛出并捕获异常而调用它


如果调用者认为他们可以通过析构函数中抛出的异常做一些有用的事情,那么他们可以编写一个助手类,在其析构函数中调用
is_ok
,并将其放在对象上。如果您认为类可以对同一异常执行一些有用的操作,那么您可以执行一些操作,而不仅仅是在析构函数中忽略它,但您仍然不应该允许它离开析构函数。

使用回调函数?
void foo()
{
    MT mt;
    mt.work1(/*userdata*/);
    mt.work2(/*userdata*/);
    mt.work1(/*userdata*/);
    status = mt.is_ok();
    if (status) {
        mt.work3(/*userdata*/);
        //...
    }
    //...
}