通过构造函数和析构函数实现RAII是否被认为是“现代C ++”?

时间:2016-09-01 02:36:06

标签: c++ c++11 raii

随着C ++中智能指针的出现,是否通过构造函数和析构函数手动实现RAII被认为是糟糕的“现代C ++”实践?或者是否存在仍然相关的应用程序?

3 个答案:

答案 0 :(得分:5)

通过分配,内存并不是唯一可以获取的资源,因此指针并不是RAII所用的唯一一种野兽。

例如,考虑一个范围锁:

template <class Lockable>
class lock_guard {
    Lockable& lck;
public:
    lock_guard(Lockable& lck)
        : lck(lck)
    {
        lck.lock();
    }

    ~lock_guard()
    {
        lck.unlock()
    }
};

没有指针。 RAII。仍然闪亮,现代,超级实用。

答案 1 :(得分:4)

这个问题基于危险意见,可能会被关闭。但是,我会尝试提供一些事实和信息来决定手动RAII何时适合。

当我们可以避免 - 手动 - RAII

确实,如果我们的类中使用的所有堆内存都通过智能指针或某个容器(如向量)分配,我们可以避免显式创建析构函数,还可以避免手动定义繁琐的方法,如复制构造函数和赋值运算符。如果有可能,这是一件好事,但并不总是合适的。现在我们将简要讨论一下这种方法本身不够灵活的几种情况。

当我们无法轻易避免手动RAII

C代码周围的包装

C代码通常使用库提供的函数分配内存和其他资源,然后通常提供销毁这些资源的函数。在这种情况下,我们需要创建某种容器来代表RAII中的那些资源。这个容器不可避免地会造成人工破坏等。

技术上可能通过创建具有自定义销毁功能的智能指针来实现一些返回适当指针的包装器。然而,这并不仅仅比定义一个类更整洁。

非内存资源

并非计算机具有的所有资源都是内存,并非​​所有资源都适用于智能指针。我们可能需要操作系统提供的文件,套接字,系统范围的互斥锁和各种其他资源对象。

并非所有这些都可以很好地在智能指针中表示,即使它们被强制使用自定义销毁函数,也可能比制作一个合适的类来包装这些资源更为丑陋。

具有相互依赖性破坏的资源

虽然像内存这样的资源通常可以按任何顺序分配和释放。有些资源不是。例如,如果我们正在与硬件设备通信,我们可能将设备表示为一个资源,然后将其作为单独的资源表示。

在这种情况下,不可能方便地使用隐式RAII,因为我们需要控制销毁顺序,并且不希望让我们的API用户必须记住以正确的顺序销毁所有内容。相反,最简单的方法是使用带引用计数的类或其他内部跟踪相互关联资源的方法。

单一责任原则

通常在上述以及其他需要手动实施RAII的情况下。最简单的方法是创建一个简单的对象来包装资源并在其上提供低级操作。

然后,我们可以拥有更高级别更复杂和功能更强大的对象,而不必担心管理内存,从根本上将销毁和资源管理代码分离到自己的单元中。这消除了复杂的析构函数等的许多痛苦。如果这些资源具有良好的低级包装来管理其生命周期和低级别功能,那么让对象使用10个资源要容易得多。

答案 2 :(得分:3)

如果您可以使用标准RAII包装,请使用它们,不要重新发明轮子。除了std::unique_ptr之外,还有其他内容,例如std::lock_guard

在我的代码库中,我为绑定的opengl对象创建了一个RAII包装器。绑定也可以是资源!

简而言之,尽可能使用标准的RAII包装。否则就实施你的。尽可能多地使用RAII。