从事 Java 工作很多年了,所以一直没有关注 C++。有 最后 子句是否已添加到语言定义中的 C++ 异常处理中?

有没有一种流行的模仿 Java 的 try/finally 的习惯用法?

我还担心 C++ 没有一个终极超类型来处理所有可能抛出的异常 - 就像 Java 的 Throwable 类。

我可以写:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

附录编辑:

我最终接受了最高投票的答案,即使用击路仪进行清理。当然,从我自己的评论中,很明显,我并不完全同意。但是,C ++就是它的本质,因此在我想到的应用程序中,我或多或少会努力遵守共同的社区实践。我将使用模板类来包装尚未具有类驱动器(即C库资源)的资源,从而赋予它们destructor语义。

新的附录编辑:

嗯,而不是 最后 那也许是封闭功能?封闭式与ScopeGuard方法相结合(请参阅下面的答案之一)将是一种通过任意动作并访问清理代码的外部范围上下文来完成清理的方法。清理可以按照 Ruby 编程中的习惯用法来完成,在打开资源时它们会提供清理块。C ++是否考虑过关闭功能?

有帮助吗?

解决方案

通过有效地利用析构函数。当一个异常在try块被抛出,在其内部产生的任何对象会被立即销毁(以及因此它的析构函数调用)。

这是从Java不同,你不知道什么时候一个对象的终结器将被调用。

<强>更新:直从马的嘴:为什么不是C ++提供 “最后” 构建体?

其他提示

我的$ .02。我在如C#和Java托管语言编程了多年,但被迫做出切换到C ++的速度的目的。起初,我简直不敢相信我是如何在头文件,然后cpp文件两次写出来的方法签名,我不喜欢怎么没有finally块,并没有垃圾收集意味着跟踪内存泄漏无处不在 - 天哪我不喜欢它!

然而,正如我说我被迫使用C ++。所以我被迫认真学习它,现在我终于明白了所有的编程风格类似RAII和我得到的语言,这样所有的微妙之处。我花了一段时间,但现在我看到它是多么种语言的不同相比,C#或Java。

这些天,我觉得C ++是有最好的语言!是的,我能理解,有更多一点就是我所说的“糠”有时(写看似不必要的东西),但实际使用严重的语言后,我已经完全改变了我的脑海里了。

我曾经有内存泄漏的所有时间。我以前写我的所有代码到.h文件,因为我讨厌的代码分离,我不明白他们为什么要这么做!我以前总是愚蠢的循环结束了包括依赖关系,和堆更多。我真的挂了C#或Java的,对我来说C ++是一个巨大的下台。这些天,我得到它。我几乎从来没有内存泄漏,我喜欢接口和实现的分离,我没有与循环依赖的问题了。

和我不要错过最后要么块。说实话,我的观点是,你说说写在catch块反复清理这些动作C ++程序员只是声音对我来说,他们只是糟糕的C ++程序员。我的意思是,它看起来并不像任何在这个线程的其他C ++程序员都是有任何的你提到的问题。 RAII确实会让终于多余的,如果有的话,它的工作少。你写一个析构函数,然后你再也不用写另一终于永远!嗯,至少该类型。

对于,我认为是怎么回事是你只是使用到Java现在,就像我一直。

C ++的回答是RAII:外出时的范围的对象的析构函数将被执行。无论是一个回报,一个异常或什么的。如果处理异常别的地方,你可以确保从被调用函数到您的处理程序中的所有对象将具有称为他们的析构函数被正确销毁。他们会收拾你。

读取 http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

Nofinally 还没有被添加到 C++ 中,也不可能被添加。

C++ 使用构造函数/析构函数的方式使得不需要finally。
如果您使用 catch(...) 进行清理,那么您没有正确使用 C++。清理代码应该全部位于析构函数中。

尽管不要求使用它,但 C++ 确实有 std::exception。
强制开发人员从特定类派生以使用异常违背了 C++ 的“保持简单”理念。这也是为什么我们不要求所有类都从 Object 派生的原因。

读: C++ 支持“finally”块吗?(我经常听到的“RAII”是什么?)

使用finally 比析构函数进行清理更容易出错。
这是因为您强制对象的用户而不是类的设计者/实现者进行清理。

好的,我必须添加对您在单独的答案帖子中提出的观点的答案:(如果您将其编辑到原始问题中,会方便得多,这样它就不会出现在底部 以下 的答案。

如果所有清理始终都在灾难中完成,则不需要在捕获块中有任何清理代码 - 但是C ++具有完成清理操作的捕获块。的确,它有一个捕获(...)的障碍,只有可以进行清理操作(嗯,当然无法获得任何例外信息来进行任何记录)。

catch 有一个完全独立的目的,作为 Java 程序员,您应该意识到这一点。finally 子句用于“无条件”清理操作。无论如何退出区块,都必须这样做。Catch 用于有条件的清理。如果抛出此类异常,我们需要执行一些额外的操作。

无论是否有例外,最终块中的清理都将完成 - 当确实存在清理代码时,这是人们一直想发生的事情。

真的吗?如果我们想要它 总是 发生在这种类型上(比如说,我们总是想在完成数据库连接后关闭它),那么为什么我们不定义它 一次?在类型本身?让数据库连接自行关闭,而不是在每次使用它时都进行尝试/最终操作?

这就是析构函数的要点。它们保证每种类型在每次使用时都能够处理自己的清理工作,而调用者不必考虑它。

从第一天开始,C ++开发人员不得不重复在成功退出Try Block时出现在代码流中的捕获块中出现的清理操作。Java和C#程序员只需在最后一个封锁中完成一次即可。

不。C++ 程序员从未受此困扰。C 程序员有。C 程序员意识到 C++ 有类,然后称自己为 C++ 程序员。

我每天都用 C++ 和 C# 编程,我觉得我被 C# 可笑的坚持所困扰,即我必须提供一个 finally 子句(或一个 using 块)每次我使用数据库连接或其他必须清理的东西。

C++ 让我一劳永逸地指定“每当我们完成此类型时,它都应该执行这些操作”。我不会冒忘记释放内存的风险。我不会冒忘记关闭文件句柄、套接字或数据库连接的风险。因为我的内存、句柄、套接字和数据库连接都是自行完成的。

怎么可以 曾经 每次使用类型时都必须编写重复的清理代码更好吗?如果您需要包装该类型,因为它本身没有析构函数,您有两个简单的选择:

  • 寻找一个提供此析构函数的合适的 C++ 库(提示:促进)
  • 使用 boost::shared_ptr 来包装它,并在运行时为其提供自定义函子,指定要完成的清理。

当您编写诸如Java EE应用程序服务器Glassfish,Jboss等的应用程序服务器软件时,您希望能够捕获和日志异常信息 - 而不是让它落在地板上。或更糟糕的是落入运行时,并导致应用程序服务器的突然出口。这就是为什么对于任何可能的例外,都希望拥有一个总体基类。而C++就有这样一个类。std::异常。

自Cfront Days和Java/C#从本十年中的大部分时间开始完成C ++。很清楚地看到,从根本上类似的事情接近的方式方面存在巨大的文化差距。

不,你从来没有学过 C++。您已经完成了 CFront,或者带有类的 C。不是C++。有很大的不同。别再说这些答案蹩脚了,你可能会学到一些你认为你懂的语言的知识。;)

清理功能,本身是彻底跛。他们有低凝聚力,因为他们预计执行一系列只有在发生的时候相关的活动。他们有高耦合,因为它们需要有自己的内部修改时,实际上是做一些功能被改变。正因为如此,它们是容易出错。

在试...最后构建体是用于清除功能的框架。这是写糟糕的代码语言,鼓励的方式。此外,由于它鼓励反复写入相同的清理代码,它破坏了DRY原则。

在C ++方法是远优选用于这些目的。对资源的清理代码编写正是一次,在析构函数。这是在同一个地方,作为该资源的代码的其余部分,因此具有良好的粘结性。清理代码不必投入不相关的模块,因此,这减少了耦合。这是写一次精确,当设计得很好。

此外,C ++方法是均匀得多。 C ++,与智能指针添加,处理以相同的方式的各种资源,而Java处理存储器以及和提供构建体不足以释放其他资源。

有大量的使用C ++的问题,但是这不是它们中的一个。有一些方法,其中Java是比C ++更好,但这不是它们中的一个。

Java的将是一个办法好得多实现RAII,而不是尝试...最后。

要避免,以限定一个包装类为每一个可释放的资源,则可能有兴趣在ScopeGuard( HTTP ://www.ddj.com/cpp/184403758 ),它允许一个动态创建的“清洁工”。

例如:

FILE* fp = SomeExternalFunction();
// Will automatically call fclose(fp) when going out of scope
ScopeGuard file_guard = MakeGuard(fclose, fp);

的是多么困难,最终正确地使用的示例。

打开和关闭两个文件。结果 要确保文件被正确关闭。结果 等待GC并不像文件可以被重新使用的选项。

在C ++

void foo()
{
    std::ifstream    data("plop");
    std::ofstream    output("plep");

    // DO STUFF
    // Files closed auto-magically
}

在没有析构函数一种语言,但是具有最后子句。

void foo()
{
    File            data("plop");
    File            output("plep");

    try
    {
        // DO STUFF
    }
    finally
    {
        // Must guarantee that both files are closed.
        try {data.close();}  catch(Throwable e){/*Ignore*/}
        try {output.close();}catch(Throwable e){/*Ignore*/}
    }
}

这是一个简单的例子,已经将代码是越来越卷积。在这里我们只是试图获取2个简单的资源。但作为需要被管理的增加和/或它们的复杂性增加了使用最后块的变得越来越难在异常的情况下正确地使用的资源的数量。

在使用的最后移动到的对象的用户为正确用法的责任。通过使用++您将正确使用的类的设计者/实施者的责任,用C提供的构造/析构函数机制。这是因为设计人员只需在类级别(而不是有不同的用户尝试以不同的方式做正确的)。

做一次正确的安全inheritanly

使用C ++ 11以其拉姆达表达式,我已经最近开始使用以下代码来模仿finally

class FinallyGuard {
private:
  std::function<void()> f_;
public:
  FinallyGuard(std::function<void()> f) : f_(f) { }
  ~FinallyGuard() { f_(); }
};

void foo() {
  // Code before the try/finally goes here
  { // Open a new scope for the try/finally
    FinallyGuard signalEndGuard([&]{
      // Code for the finally block goes here
    });
    // Code for the try block goes here
  } // End scope, will call destructor of FinallyGuard
  // Code after the try/finally goes here
}

FinallyGuard是被构造为具有一个可调用函数状参数,preferrably lambda表达式的对象。它将简单地记住该函数直到其析构函数被调用,它是这种情况,当对象超出范围,无论是由于正常的控制流或由于堆栈异常处理过程中开卷。在这两种情况下,析构函数将调用函数,从而执行所讨论的代码。

这是一个有点奇怪,你必须编写代码的finally的为try的代码块,但除此之外,它实际上感觉很像从Java真正try / finally 。我想我们不应该滥用这种把与自己的析构函数正确的对象将是更适当的场合,但也有在那里我认为上面比较适合这种方法的情况。我讨论一个这样的场景中这个问题

据我明白的事情,std::function<void()>将使用一些指针间接和至少一个虚拟函数的调用来执行其类型擦除,所以会有一个性能开销。不要在紧密循环在性能是关键使用这种技术。在这些情况下,一个专门的对象,其析构函数确实一件事仅会更合适。

C ++析使finally冗余。可以通过在清理代码从移动终于到对应的析构函数得到同样的效果。

我认为你缺少的是什么catch (...)能做到这一点。

您在你的例子说:“唉,不能检查的异常”。那么,你有没有对异常的类型信息。你甚至不知道这是否是一个多态类型,这样即使你有某种非类型化引用它,你甚至不能安全地尝试dynamic_cast

如果你了解某些例外或异常层次结构,你可以做什么用,那么这是用显式命名的类型catch块的地方。

catch (...)是不经常使用C ++有用。它可以在其中要保证他们不扔,或仅抛出某些合同例外场所。如果你正在使用catch (...)进行清理,然后有一个非常好的机会,你的代码是不是稳健异常安全在任何情况下。

在其他的答案中提到,如果您使用的是本地对象来管理资源(RAII),那么它可以是令人惊讶和启发你如何抓几个街区的需要,经常 - 如果你不需要在例外当地做任何事 - 即使try块可以是多余的,因为你让异常流出到客户端的代码,可以同时仍然保证没有资源问题作出回应

要回答你原来的问题,如果你需要一些一段代码在一个块,异常或也不例外结束时运行,然后配方会。

class LocalFinallyReplacement {
    ~LocalFinallyReplacement() { /* Finally code goes here */ }
};
// ...
{ // some function...
    LocalFinallyReplacement lfr; // must be a named object

    // do something
}

请注意我们如何能完全trycatchthrow做了。

如果你有在原来你在“最后”块需要访问try块外面声明的功能数据,那么你可能需要将它添加到辅助类的构造函数,并将其存储到析构函数。然而,在这一点上我会认真考虑的问题是否能够得到解决通过改变本地资源处理对象,因为它会暗示一些设计歪的设计。

不completetely offtopic。

在Java中锅炉电镀DB资源清理

嘲讽模式:是不是Java成语精彩

我在这15年一样大量一流的设计和模板的包装设计在C ++和所做的这一切的C ++方式析构函数方面的清理。每一个项目,不过,也无不涉及使用与开放提供的资源它,使用它,关闭它使用模型的C函数库。一个try /终于将意味着这样的资源可以只消耗需要它是 - 在一个完全稳健的方式 - 并用它做。最不单调乏味的方法来编程这种情况。可以处理与清理的逻辑过程中的所有其他状态正在进行,而不必在一些包装析构先限定的路程。

我做了我的大部分C ++编码在Windows上,以便随时可以诉诸使用Microsoft的__try / __终于为这种情况。 (他们的结构化异常处理有一些强大的能力与例外的交互。)唉,看起来并不像C语言曾经批准任何便携式异常处理结构。

这不是理想的解决方案,但是,因为它不是简单的try块,其中例外的任何风格可能会抛出融入C和C ++代码。 finally块加入到C ++本来对这些情况有益的,将使便携性。

关于您的附录编辑,是的,C++0x 正在考虑闭包。它们可以与 RAII 范围防护一起使用,以提供易于使用的解决方案,请检查 Pizer 的博客. 。它们也可以用来模仿 try-finally,请参阅 这个答案 ;但 这真的是个好主意吗? .

想我会我自己的解决方案添加到这个 - 一种智能指针包装的,因为当你要处理非RAII类型

用于这样的:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

所以这里的Finaliser执行:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

...这是释放器:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

我有几个不同种类的这样的释放器,包括一个用于免费()和一个用于CloseHandle的()。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top