RAII的有用性无例外

时间:2013-07-17 19:26:44

标签: c++ exception embedded raii

我最近在c ++中找到了RAII,大多数RAII的例子都谈到了异常安全问题。即使抛出异常,如何始终释放资源。

我的问题是,如果您没有启用例外,那么RAII是值得的。在我们公司,我们致力于嵌入式项目,默认情况下关闭异常,我们并不认为有任何需要。

感谢所有答案!

4 个答案:

答案 0 :(得分:15)

RAII有例外基本上是一项要求。

RAII无例外意味着您可以将资源分配与处理资源的代码相结合。

这使您可以使用具有多个退出点的函数,简化了析构函数的编写(通常在RAII繁重环境中的析构函数为空或默认),可以简化对象分配和移动(再次,通常为空或默认具有足够的RAII工作)。

嵌入式环境的一个典型示例是锁定和解锁一些互斥锁。您希望保证不锁定互斥锁并忘记解锁它。要做到这一点,代码规则意味着你必须从你的功能基本上有一个退出点,你必须有时参加体操以确保这种情况发生。

使用RAII,您只需创建一个拥有锁的RAII资源持有者。现在您可以随时返回,并且在返回站点自动注入解锁资源的代码。代码流程得到简化,资源泄漏也不常见。

RAII也是令人惊叹的文档。具有Foo*的结构或类可能意味着什么:您应该如何以及何时处理该资源尚不清楚。具有std::unique_ptr<Foo>的结构或类显然拥有该指针。采用std::unique_ptr<Foo>的函数显然取决于传入的指针。返回std::unique_ptr<Foo>的函数显然为您提供了该指针的所有权。

答案 1 :(得分:7)

C ++中的RAII是一个更广泛的概念。这是一个成语,允许您编写更安全的代码。 RAII的资源获取部分是您开始必须稍后结束的事情,例如打开文件并稍后关闭它,分配一些内存并释放它,获取和释放锁定。 RAII涉及如下重要概念:智能指针,线程安全(在多线程程序中控制互斥锁 - http://www.boost.org/doc/libs/1_49_0/doc/html/boost/interprocess/scoped_lock.html这里是RAII的一个例子),与文件的交互,对象所有权(当你使用智能指针时) unique_ptr你已经使用过RAII了)等等。

因此,无论例外情况如何,RAII总是值得用在好的C ++代码中。

答案 2 :(得分:2)

  

我的问题是,如果您没有启用例外,RAII是值得的。

当然值得!例外安全只是RAII的一个方面。另一个例子是避免泄漏动态创建的实例。

答案 3 :(得分:0)

我实际上认为它取决于,也许我会在我的C ++同行中争夺最奇怪的答案。也就是说,如果您已经在使用C ++和C ++的丰富类型系统,我认为即使没有例外,也不会大部分使用RAII。

设计和使用接口时缺乏RAII的滋扰

您通常不需要编写icky函数来返回您在客户端必须手动和外部释放/关闭/销毁的函数内分配的资源。同时,反转设计并让客户端分配资源并通过参数传递它们以供使用的函数(填写客户端分配的缓冲区,e..g),并且仍然与客户自由/关闭/销毁的责任(因为客户至少自己创建/打开/分配资源,因此可以说有点清洁)。只需设计一个函数,该函数返回一个可变长度的字符串,其内容在函数调用时在函数内部确定是耗时的,并且总是有点icky使用或实现或缺少RAII。

如果您有资源在超出范围后自行清理,那么这些是设计和使用C语言中的界面的日常麻烦,您不必在C ++中处理这些界面。只需给出一些构造函数和析构函数所花费的时间往往远远超过必须处理上述内容的额外麻烦。

缺乏RAII的危险

类似于基本的东西,比如一个范围的互斥体。当它超出范围时,它肯定会保护您免受一些潜在的未来错误,使互斥锁明确解锁。在第一次编写代码并对其进行测试时,在没有例外的情况下亲自避免这种情况下的错误可能很容易,但是一些同事可能会在关键时刻匆匆地将一些早期return语句引入到该函数中如果明确要求,请忘记unlock互斥锁。无论如何可能或不可能的是,拥有那个范围的互斥体是件好事。

哑型系统的好处

但我喜欢奇怪的类型,它喜欢C和C ++,并且在两者之间来回反弹,而且我发现在C中更容易做的一件事就是实现低可以使用几乎任何数据类型的级别数据结构。这是因为在C中你不具备丰富的类型系统,其中包含可以拥有vtable,dtors和ctors的对象,并且你没有例外。您通常可以将数据类型视为此处memcpy的位和字节以及memmove那里,malloc内存以及此处realloc。我们可以在C中自信地做到这一点的原因是因为类型系统如此愚蠢并缺乏所有这些功能。

当然,我在C中设计的数据结构不方便使用。它们缺乏类型安全性并且经常处理void*指针,它们必须被手动销毁等等。它们实现起来非常方便,并且在最小化堆分配的同时使缓存友好,因为它们让我真正专注于硬核当我可以将数据类型看作要改组的位和字节时,在内存布局和表示上。与此同时,在C ++中,在提供异常安全性并使用std::vector和析构函数的手动调用等同时分配和释放时,即使正确实现像placement new这样的可扩展数组容器也不容易通过std::allocator。但是,与我在C中设计的任何数据结构相比,使用std::vector方式更方便,更安全。

因此,拥有一个缺少析构函数和构造函数等类型系统的特殊方便性,如果它获得了析构函数和构造函数,那么它不一定会更好地表现它所擅长的东西。允许符合RAII标准的资源,因为突然出现各种现有的日常C函数,如memcpy,将不再能够正常使用。他们突然成为这种类型系统中最致命,容易出错的函数,就像它们在C ++中一样。

因此,我认为有一个论点可以说,在某些低级域名中,RAII实际上可能是一个障碍,尽管只是针对这些非常低级别的域名。同时,无论是否有例外,它对其他一切都非常有益。例外情况使RAII几乎成为一项要求,但不包括这些非常低级别的域名,无论如何它们仍然非常有用。

简单易于构建/可破坏的UDT

有时候我希望C ++中的struct关键字将结构简化为一个简单可构造且可破坏的普通旧数据类型(我们可以安全memcpy周围的事物类型,例如),编译器保护措施,防止它存储任何不属于的数据成员。如果是这样,我会发现使用C的原因较少,因为在那时我们可能会编写通用容器,这些容器只能使用structs而不是classes,而不依赖于类型特征来确定是否泛型类型是可以构造/可破坏的。因为对C的吸引力从来没有像RAII的缺乏那么多,因为能够轻易地假设我们存储在数据结构中的许多数据类型并且每天都不需要它。在实现数据结构时,知道您可以free N memcpy元素的连续内存块而不必循环遍历它们并调用析构函数,这很好。在C ++中,你经常不得不在更安全的方面犯错,并假设几乎所有东西都需要它(或将来需要它),以至于{{1}}强烈,强烈不鼓励在任何地方使用它任何人都可以。