C++ 调用函数的析构函数,该函数可以在C++;

C++ 调用函数的析构函数,该函数可以在C++;,c++,exception,destructor,throw,try-catch,C++,Exception,Destructor,Throw,Try Catch,我知道我从析构函数抛出异常 如果我的析构函数调用了一个可以抛出异常的函数,那么如果我在析构函数中捕捉到它,并且不再抛出它,可以吗?或者它会导致中止,而我根本不应该从析构函数调用这样的函数?是的,这是合法的。异常不能从析构函数中逃逸,但析构函数内部或它调用的函数中发生的任何事情都取决于您 (技术上,异常也可以从析构函数调用中逃脱。如果在堆栈展开时发生了另一个异常,则 STD::终止< /代码>。因此它是由标准定义的,但这是一个非常糟糕的想法。” < P>您可以从C++ FAQ Lite中找到信息。

我知道我从析构函数抛出异常


如果我的析构函数调用了一个可以抛出异常的函数,那么如果我在析构函数中捕捉到它,并且不再抛出它,可以吗?或者它会导致中止,而我根本不应该从析构函数调用这样的函数?

是的,这是合法的。异常不能从析构函数中逃逸,但析构函数内部或它调用的函数中发生的任何事情都取决于您


(技术上,异常也可以从析构函数调用中逃脱。如果在堆栈展开时发生了另一个异常,则<代码> STD::终止< /代码>。因此它是由标准定义的,但这是一个非常糟糕的想法。”

< P>您可以从C++ FAQ Lite中找到信息。基本的答案是,“不要这样做。”你计划在哪里捕捉这个异常?无论您在捕获该异常时打算做什么,只需使用函数调用或其他方式(例如,记录该异常或设置一个警告用户的标志或其他方式)即可。从析构函数引发异常有可能导致整个程序终止。

简单回答,决不允许来自dtor的异常

答案很复杂。只有当另一个异常处于活动状态时,该异常逃逸dtor,您才会真正被锁定。这种情况的正常情况是,您已经从另一个异常中展开堆栈,并且所讨论的对象被销毁。在这种情况下,如果异常逃逸dtor,然后调用
std::terminate
,请注意,您可以通过调用
std::set\u terminate
将自己的处理程序放入
std::terminate
std::terminate
的默认实现是调用abort

更复杂的是,大多数想要保证其异常安全性的函数,主要是基本保证或强保证,依赖于自身的底层类型,而不是抛出它们的dtor*

真正的问题是,当这个错误发生时,您的程序将处于什么状态?你怎样才能恢复?这种恢复应该在哪里进行?你需要看看你的具体案例,解决这些问题。有时候捕捉异常并忽略它是很好的。其他时候,你需要发出一些危险信号

<>强>所以答案是:C++允许在Dor中抛出异常,但是你不应该允许它逃脱。< /强>

*这里简要介绍了例外保证(这里有一个更长的例子)

  • 概述:简要定义亚伯拉罕例外安全保证(基本, 强壮的,没有什么)
  • 最基本的保证是它失败了 操作可能会改变程序状态, 但没有发生泄漏并受到影响 对象/模块仍然是可破坏的 并且可用,以一致(但不一致)的方式 (必须是可预测的)状态

    强有力的保证包括 事务提交/回滚 语义:失败的操作保证 程序状态保持不变 关于所操作的对象。 这意味着没有副作用,影响 对象,包括有效性或 相关辅助对象的内容 例如指向 被操纵的容器

    nothrow担保意味着 失败的操作不会发生。这个 操作将不会引发异常

    以标准库中的std::fstream类为例

    • close()可能会引发异常
    • 析构函数可以调用close(),但析构函数不会抛出(它将吞并任何异常)
    其概念是,如果析构函数调用任何可以抛出的方法,那么这些方法应该是公共的。因此,如果对象的用户想要检查异常,他们可以使用公共方法来处理异常。如果他们不关心异常,那么就让析构函数处理这个问题

    返回std::fstream示例

    {
        std::fstream   text("Plop");
        // Load Text.
    
        // I don't care if the close fails.
        // So let the destructor handle it and discard exceptions
    }
    
    
    
    {
        // If this fails to write I should at least warn the user.
        // So in this case I will explicitly try and close it.
        try
        {
            std::ofstram    password("/etc/password");
            // Update the password file.
    
            password.close();
        }
        catch(...)
        {
              Message.ShowDialog("You failed to update the Password File");
        }
    }
    

    您可以在这里找到一些示例:

    如果一个异常在另一个正在传播的异常的堆栈展开期间离开析构函数,则调用std::terminate()

    当没有进行堆栈展开时,异常可以在不调用std::terminate()的情况下离开析构函数。但是,对于在堆上分配的对象,这将导致内存泄漏,因为将异常抛出其析构函数的对象不会调用“运算符删除”。令人惊讶的是,在这种情况下仍然会调用基类的析构函数:


    如果异常在析构函数内部被捕获(以便异常不会离开析构函数),那么即使另一个异常的堆栈展开正在进行,也没有问题。这个案例在这里有更深入的描述:

    很抱歉吹毛求疵,但我会使用“法律”以外的术语。在析构函数中抛出异常也是“合法的”,即。E它将编译并运行。但这是一种会造成不愉快影响的糟糕做法。是和否。你是对的。从技术上讲,从析构函数中抛出异常是合法的(
    std::terminate
    )。但是你对“法律”的定义是错误的。只是因为一些东西编译和运行,并不能使它成为合法的C++。我不确定你想要的是什么意思,但我和Dima在这一点上。虽然C++没有使用工作的合法性,但它确实定义了一个格式良好的程序(可能最接近我认为的“合法”),未指定的行为和未定义的行为。允许异常从析构函数中传播出去可能发生在格式良好的程序中,并且行为既不是未定义的,也不是未指定的。这并不能阻止它成为一种几乎普遍不受欢迎的行为。在正常情况下(不调用std::terminate())异常转义析构函数绝对没有问题。只有当另一个异常已经在传播时(调用std::terminate())才会出现问题,这是