c++支持“finally”块吗?

RAII习语是什么?

c++的RAII习语和c#的using语句有什么区别?


当前回答

在c++中,由于RAII, final是不需要的。

RAII将异常安全的责任从对象的用户转移到对象的设计者(和实现者)。我认为这是正确的地方,因为你只需要让异常安全正确一次(在设计/实现中)。通过使用finally,您需要在每次使用对象时都获得正确的异常安全性。

在我看来,代码看起来更整洁了(见下文)。

例子:

一个数据库对象。为了确保使用了DB连接,必须打开并关闭它。通过使用RAII,这可以在构造函数/析构函数中完成。

C+像火车

void someFunc()
{
    DB    db("DBDesciptionString");
    // Use the db object.

} // db goes out of scope and destructor closes the connection.
  // This happens even in the presence of exceptions.

RAII的使用使得正确使用DB对象变得非常容易。DB对象将通过使用析构函数正确地关闭自身,无论我们如何尝试和滥用它。

Java终于来了

void someFunc()
{
    DB      db = new DB("DBDesciptionString");
    try
    {
        // Use the db object.
    }
    finally
    {
        // Can not rely on finaliser.
        // So we must explicitly close the connection.
        try
        {
            db.close();
        }
        catch(Throwable e)
        {
           /* Ignore */
           // Make sure not to throw exception if one is already propagating.
        }
    }
}

当最终使用对象时,对象的正确使用委托给对象的用户。例如,正确地显式地关闭DB连接是对象用户的责任。现在,您可能会认为这可以在终结器中完成,但资源可能有有限的可用性或其他约束,因此您通常希望控制对象的释放,而不是依赖于垃圾收集器的非确定性行为。

这也是一个简单的例子。 当需要释放多个资源时,代码可能会变得复杂。

更详细的分析可以在这里找到:http://accu.org/index.php/journals/236

其他回答

在c++中,由于RAII, final是不需要的。

RAII将异常安全的责任从对象的用户转移到对象的设计者(和实现者)。我认为这是正确的地方,因为你只需要让异常安全正确一次(在设计/实现中)。通过使用finally,您需要在每次使用对象时都获得正确的异常安全性。

在我看来,代码看起来更整洁了(见下文)。

例子:

一个数据库对象。为了确保使用了DB连接,必须打开并关闭它。通过使用RAII,这可以在构造函数/析构函数中完成。

C+像火车

void someFunc()
{
    DB    db("DBDesciptionString");
    // Use the db object.

} // db goes out of scope and destructor closes the connection.
  // This happens even in the presence of exceptions.

RAII的使用使得正确使用DB对象变得非常容易。DB对象将通过使用析构函数正确地关闭自身,无论我们如何尝试和滥用它。

Java终于来了

void someFunc()
{
    DB      db = new DB("DBDesciptionString");
    try
    {
        // Use the db object.
    }
    finally
    {
        // Can not rely on finaliser.
        // So we must explicitly close the connection.
        try
        {
            db.close();
        }
        catch(Throwable e)
        {
           /* Ignore */
           // Make sure not to throw exception if one is already propagating.
        }
    }
}

当最终使用对象时,对象的正确使用委托给对象的用户。例如,正确地显式地关闭DB连接是对象用户的责任。现在,您可能会认为这可以在终结器中完成,但资源可能有有限的可用性或其他约束,因此您通常希望控制对象的释放,而不是依赖于垃圾收集器的非确定性行为。

这也是一个简单的例子。 当需要释放多个资源时,代码可能会变得复杂。

更详细的分析可以在这里找到:http://accu.org/index.php/journals/236

总之,微软Visual c++确实支持try,它在MFC应用中一直被用作捕获严重异常的方法,否则会导致崩溃。例如;

int CMyApp::Run() 
{
    __try
    {
        int i = CWinApp::Run();
        m_Exitok = MAGIC_EXIT_NO;
        return i;
    }
    __finally
    {
        if (m_Exitok != MAGIC_EXIT_NO)
            FaultHandler();
    }
}

在过去,我用它来做一些事情,比如在退出之前保存打开文件的备份。不过,某些JIT调试设置会破坏这种机制。

I also think that RIIA is not a fully useful replacement for exception handling and having a finally. BTW, I also think RIIA is a bad name all around. I call these types of classes 'janitors' and use them a LOT. 95% of the time they are neither initializing nor acquiring resources, they are applying some change on a scoped basis, or taking something already set up and making sure it's destroyed. This being the official pattern name obsessed internet I get abused for even suggesting my name might be better.

我只是认为不合理的做法是,要求某些特殊列表的每一个复杂设置都必须编写一个类来包含它,以避免在清理过程中出现问题时需要捕获多个异常类型时的复杂性。这将导致大量的临时类,否则这些类是不必要的。

是的,对于专为管理特定资源而设计的类,或者专为处理一组类似资源而设计的泛型类,这是没问题的。但是,即使所有涉及的东西都有这样的包装器,清理的协调也可能不仅仅是简单的反序析构函数调用。

我认为c++有一个final。我的意思是,天哪,在过去的几十年里,它被粘上了这么多零碎的东西,似乎奇怪的是,人们突然对一些东西变得保守起来,比如最终,它可能非常有用,可能不像其他一些已经添加的东西那么复杂(尽管这只是我的猜测)。

As pointed out in the other answers, C++ can support finally-like functionality. The implementation of this functionality that is probably closest to being part of the standard language is the one accompanying the C++ Core Guidelines, a set of best practices for using C++ edited by Bjarne Stoustrup and Herb Sutter. An implementation of finally is part of the Guidelines Support Library (GSL). Throughout the Guidelines, use of finally is recommended when dealing with old-style interfaces, and it also has a guideline of its own, titled Use a final_action object to express cleanup if no suitable resource handle is available.

因此,c++不仅最终支持,实际上还建议在许多常见用例中使用它。

GSL实现的示例使用如下所示:

#include <gsl/gsl_util.h>

void example()
{
    int handle = get_some_resource();
    auto handle_clean = gsl::finally([&handle] { clean_that_resource(handle); });

    // Do a lot of stuff, return early and throw exceptions.
    // clean_that_resource will always get called.
}

GSL的实现和使用与Paolo中的非常相似。Bolzoni的回答。一个区别是gsl::finally()创建的对象缺少disable()调用。如果您需要该功能(例如,在资源组装完成后返回资源,并且不会发生任何异常),那么您可能更喜欢Paolo的实现。否则,使用GSL就相当于使用标准化的特性。

除了使基于堆栈的对象的清理变得容易之外,RAII还很有用,因为当对象是另一个类的成员时,同样的“自动”清理也会发生。当所属类被销毁时,由RAII类管理的资源将被清理,因为该类的dtor将被调用。

这意味着当您达到RAII的天堂,并且类中的所有成员都使用RAII(如智能指针)时,您可以为所有者类使用一个非常简单的(甚至是默认的)dtor,因为它不需要手动管理其成员资源生命周期。