理解术语的含义和概念 - RAII(资源获取是初始化) [英] Understanding the meaning of the term and the concept - RAII (Resource Acquisition is Initialization)

查看:275
本文介绍了理解术语的含义和概念 - RAII(资源获取是初始化)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您能C ++开发人员给我们一个很好的描述RAII是什么,为什么它很重要,以及它是否可能与其他语言有什么关系?

Could you C++ developers please give us a good description of what RAII is, why it is important, and whether or not it might have any relevance to other languages?

知道一点。我相信它代表资源获取是初始化。然而,这个名字不与我的(可能不正确)理解RAII是什么:我得到的印象是RAII是一种初始化堆栈上的对象的方式,这样当这些变量超出范围,析构函数将自动

I do know a little bit. I believe it stands for "Resource Acquisition is Initialization". However, that name doesn't jive with my (possibly incorrect) understanding of what RAII is: I get the impression that RAII is a way of initializing objects on the stack such that, when those variables go out of scope, the destructors will automatically be called causing the resources to be cleaned up.

那么为什么不叫使用堆栈触发清理(UTSTTC :)?你如何从那里到RAII?

So why isn't that called "using the stack to trigger cleanup" (UTSTTC:)? How do you get from there to "RAII"?

你如何在栈上创建一些东西来清理堆上的东西?此外,有没有情况下,你不能使用RAII?你发现自己希望收集垃圾吗?

And how can you make something on the stack that will cause the cleanup of something that lives on the heap? Also, are there cases where you can't use RAII? Do you ever find yourself wishing for garbage collection? At least a garbage collector you could use for some objects while letting others be managed?

谢谢。

推荐答案


那么为什么不叫使用堆栈触发清除(UTSTTC:)?

So why isn't that called "using the stack to trigger cleanup" (UTSTTC:)?

RAII告诉你该怎么做:在构造函数中获取你的资源!我会添加:一个资源,一个构造函数。 UTSTTC只是其中的一个应用程序,RAII是更多。

RAII is telling you what to do: Acquire your resource in a constructor! I would add: one resource, one constructor. UTSTTC is just one application of that, RAII is much more.

资源管理糟糕。在这里,资源是需要清理后使用。许多平台上的项目研究显示,大多数错误都与资源管理相关 - 在Windows上尤为糟糕(由于许多类型的对象和分配器)。

Resource Management sucks. Here, resource is anything that needs cleanup after use. Studies of projects across many platforms show the majority of bugs are related to resource management - and it's particularly bad on Windows (due to the many types of objects and allocators).

在C ++中,由于异常和(C ++样式)模板的组合,资源管理特别复杂。要了解详情,请参阅 GOTW8 )。

In C++, resource management is particularly complicated due to the combination of exceptions and (C++ style) templates. For a peek under the hood, see GOTW8).

C ++保证当且仅当构造函数成功时,析构函数才会调用。基于这一点,RAII可以解决许多令人讨厌的问题,普通程序员甚至可能不知道。这里有一些例子,我的局部变量将在每次返回时被销毁。

C++ guarantees that the destructor is called if and only if the constructor succeeded. Relying on that, RAII can solve many nasty problems the average programmer might not even be aware of. Here are a few examples beyond the "my local variables will be destroyed whenever I return".

让我们从一个过分简单的开始 FileHandle 使用RAII的类:

Let us start with an overly simplistic FileHandle class employing RAII:

class FileHandle
{
    FILE* file;

public:

    explicit FileHandle(const char* name)
    {
        file = fopen(name);
        if (!file)
        {
            throw "MAYDAY! MAYDAY";
        }
    }

    ~FileHandle()
    {
        // The only reason we are checking the file pointer for validity
        // is because it might have been moved (see below).
        // It is NOT needed to check against a failed constructor,
        // because the destructor is NEVER executed when the constructor fails!
        if (file)
        {
            fclose(file);
        }
    }

    // The following technicalities can be skipped on the first read.
    // They are not crucial to understanding the basic idea of RAII.
    // However, if you plan to implement your own RAII classes,
    // it is absolutely essential that you read on :)



    // It does not make sense to copy a file handle,
    // hence we disallow the otherwise implicitly generated copy operations.

    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;



    // The following operations enable transfer of ownership
    // and require compiler support for rvalue references, a C++0x feature.
    // Essentially, a resource is "moved" from one object to another.

    FileHandle(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
    }

    FileHandle& operator=(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
        return *this;
    }
}

如果构建失败成员函数 - 甚至没有析构函数 - 被调用。

If construction fails (with an exception), no other member function - not even the destructor - gets called.

RAII避免使用处于无效状态的对象。它已经使我们甚至更容易使用对象。

RAII avoids using objects in an invalid state. it already makes life easier before we even use the object.

现在,让我们看看临时对象:

Now, let us have a look at temporary objects:

void CopyFileData(FileHandle source, FileHandle dest);

void Foo()
{
    CopyFileData(FileHandle("C:\\source"), FileHandle("C:\\dest"));
}

有三种错误处理方式:无法打开文件文件可以打开,两个文件都可以打开,但复制文件失败。在非RAII实现中, Foo 必须明确处理所有这三种情况。

There are three error cases to handled: no file can be opened, only one file can be opened, both files can be opened but copying the files failed. In a non-RAII implementation, Foo would have to handle all three cases explicitly.

即使在一个语句中获取多个资源,RAII也会释放已获取的资源。

现在,让我们聚合一些对象:

Now, let us aggregate some objects:

class Logger
{
    FileHandle original, duplex;   // this logger can write to two files at once!

public:

    Logger(const char* filename1, const char* filename2)
    : original(filename1), duplex(filename2)
    {
        if (!filewrite_duplex(original, duplex, "New Session"))
            throw "Ugh damn!";
    }
}

Logger 将失败,因为原始的构造函数失败(因为无法打开 filename1 ), code> duplex 的构造函数失败(因为 filename2 无法打开),或写入 Logger 的构造函数体失败。在任何这些情况下, Logger 的析构函数将不会被调用 - 所以我们不能依赖 Logger 的析构函数来释放文件。但是如果 original 被构造,它的析构函数将在 Logger 构造函数的清理期间被调用。

The constructor of Logger will fail if original's constructor fails (because filename1 could not be opened), duplex's constructor fails (because filename2 could not be opened), or writing to the files inside Logger's constructor body fails. In any of these cases, Logger's destructor will not be called - so we cannot rely on Logger's destructor to release the files. But if original was constructed, its destructor will be called during cleanup of the Logger constructor.

RAII简化部分建造后的清理。

strong>否定点:

Negative points:

否定点?所有问题都可以用RAII和智能指针解决;-)

Negative points? All problems can be solved with RAII and smart pointers ;-)

当你需要延迟采集,将聚合对象推到堆上时,RAII有时会很笨重。

想象Logger需要一个 SetTargetFile(const char * target)。在这种情况下,仍然需要是 Logger 的成员的句柄需要驻留在堆上(例如,在智能指针中,以适当地触发句柄的销毁)。 )

RAII is sometimes unwieldy when you need delayed acquisition, pushing aggregated objects onto the heap.
Imagine the Logger needs a SetTargetFile(const char* target). In that case, the handle, that still needs to be a member of Logger, needs to reside on the heap (e.g. in a smart pointer, to trigger the handle's destruction appropriately.)

我从来不希望垃圾收集真的。当我做C#我有时觉得幸福的时刻,我只是不需要关心,但更多的我错过所有可以通过确定性破坏创建的酷玩具。 (使用 IDisposable 只是不剪切它。)

I have never wished for garbage collection really. When I do C# I sometimes feel a moment of bliss that I just do not need to care, but much more I miss all the cool toys that can be created through deterministic destruction. (using IDisposable just does not cut it.)

我有一个特别复杂的结构, GC,其中简单智能指针将引起多个类的循环引用。我们通过仔细平衡强和弱指针混乱了,但是任何时候我们想改变一些东西,我们必须学习一个大的关系图。 GC可能更好,但一些组件持有应尽快发布的资源。

I have had one particularly complex structure that might have benefited from GC, where "simple" smart pointers would cause circular references over multiple classes. We muddled through by carefully balancing strong and weak pointers, but anytime we want to change something, we have to study a big relationship chart. GC might have been better, but some of the components held resources that should be release ASAP.

有关FileHandle的注意事项样品:它不是完整的,只是一个样品 - 但结果不正确。感谢Johannes Schaub指出和FredOverflow将其转换为正确的C ++ 0x解决方案。随着时间的推移,我已经解决了这里记录的方法。

A note on the FileHandle sample: It was not intended to be complete, just a sample - but turned out incorrect. Thanks Johannes Schaub for pointing out and FredOverflow for turning it into a correct C++0x solution. Over time, I've settled with the approach documented here.

这篇关于理解术语的含义和概念 - RAII(资源获取是初始化)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆