猿问

理解术语和概念的含义 - RAII(资源获取是初始化)

理解术语和概念的含义 - RAII(资源获取是初始化)

您能否请C ++开发人员详细介绍RAII是什么,为什么重要,以及它是否与其他语言有任何关联?

知道一点点。我相信它代表“资源获取是初始化”。但是,这个名称并不符合我对RAII的理解(可能不正确):我得到的印象是RAII是一种初始化堆栈上对象的方式,当这些变量超出范围时,析构函数会自动被称为导致资源被清理。

那么为什么不称为“使用堆栈触发清理”(UTSTTC :)?你怎么从那里到“RAII”?

你怎么能在堆栈上创建一些东西来清理堆上的东西呢?此外,是否有不能使用RAII的情况?你有没有发现自己希望收集垃圾?至少一个垃圾收集器,你可以使用一些对象,同时让其他人管理?

谢谢。


慕桂英546537
浏览 627回答 3
3回答

慕桂英3389331

那么为什么不称为“使用堆栈触发清理”(UTSTTC :)?RAII告诉你该怎么做:在构造函数中获取你的资源!我会添加:一个资源,一个构造函数。UTSTTC只是其中的一个应用,RAII更多。资源管理很糟糕。在这里,资源是在使用后需要清理的任何东西。对许多平台上的项目进行的研究表明,大多数错误都与资源管理有关 - 而且在Windows上尤其糟糕(由于有许多类型的对象和分配器)。在C ++中,由于异常和(C ++样式)模板的组合,资源管理特别复杂。如需了解引擎盖,请参阅GOTW8)。C ++保证当且仅当构造函数成功时才调用析构函数。依靠这一点,RAII可以解决普通程序员可能甚至不知道的许多令人讨厌的问题。除了“每当我返回时我的局部变量将被销毁”之外,还有一些例子。让我们从FileHandle使用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;     }}如果构造失败(有例外),则不会调用其他成员函数 - 甚至是析构函数。RAII避免在无效状态下使用对象。在我们使用对象之前,它已经让生活更轻松。现在,让我们看看临时对象:void CopyFileData(FileHandle source, FileHandle dest);void Foo(){     CopyFileData(FileHandle("C:\\source"), FileHandle("C:\\dest"));}要处理三种错误情况:无法打开文件,只能打开一个文件,可以打开这两个文件但复制文件失败。在非RAII实现中,Foo必须明确处理所有三种情况。即使在一个声明中获得多个资源,RAII也会释放已获取的资源。现在,让我们聚合一些对象: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将失败original的构造失败(因为filename1无法打开)duplex的构造失败(因为filename2无法打开),或内写入文件Logger的构造体失败。在任何这些情况下,Logger都不会调用析构函数- 所以我们不能依赖Logger析构函数来释放文件。但是如果original被构造,它的析构函数将在Logger构造函数的清理期间被调用。RAII简化了部分施工后的清理工作。否定点:否定点?使用RAII和智能指针可以解决所有问题;-)当您需要延迟获取时,RAII有时会变得难以处理,将聚合对象推送到堆上。想象一下Logger需要一个SetTargetFile(const char* target)。在这种情况下,仍然需要成为其成员的句柄Logger需要驻留在堆上(例如,在智能指针中,以适当地触发句柄的破坏。)我真的不希望收集垃圾。当我做C#时,我有时会感到一阵幸福,我不需要关心,但更多的是我想念所有可以通过确定性破坏创造的酷玩具。(使用IDisposable只是不削减它。)我有一个特别复杂的结构可能从GC中获益,其中“简单”智能指针会导致多个类的循环引用。我们通过仔细平衡强弱指针而陷入困境,但无论何时我们想要改变某些东西,我们都必须研究一个大关系图。GC可能会更好,但是一些组件拥有应该尽快发布的资源。关于FileHandle示例的注释:它不是完整的,只是一个示例 - 但结果不正确。感谢Johannes Schaub指出并将FredOverflow转变为正确的C ++ 0x解决方案。随着时间的推移,我已经解决了这里记录的方法。

慕容708150

那里有很好的答案,所以我只是添加了一些被遗忘的东西。0. RAII是关于范围的RAII是关于两者:获取构造函数中的资源(无论什么资源),并在析构函数中取消它。在声明变量时执行构造函数,并在变量超出范围时自动执行析构函数。其他人已经回答了这个问题,所以我不会详细说明。1.使用Java或C#编码时,您已使用RAII ...MONSIEUR JOURDAIN:什么!当我说,“妮可,把我的拖鞋带给我,给我睡帽,”这是散文?哲学硕士:是的,先生。MONSIEUR JOURDAIN:四十多年来,我一直在讲述散文而不知道任何事情,我非常感谢你教我这个。- 莫里哀:中产阶级绅士,第2幕,场景4正如Jourdain先生用散文所做的那样,C#甚至Java人已经使用RAII,但却是隐藏的方式。例如,下面的Java代码(这是通过替换在C#编写的相同方式synchronized用lock):void foo(){    // etc.    synchronized(someObject)    {       // if something throws here, the lock on someObject will       // be unlocked    }    // etc.}...已经在使用RAII:互斥锁获取在关键字(synchronized或lock)中完成,取消将在退出范围时完成。它的符号非常自然,即使是从未听说过RAII的人也几乎不需要解释。C ++在Java和C#方面的优势在于可以使用RAII进行任何操作。例如,有没有直接内建等效的synchronized,也没有lock在C ++中,但我们仍然可以拥有它们。在C ++中,它将写成:void foo(){    // etc.    {       Lock lock(someObject) ; // lock is an object of type Lock whose                               // constructor acquires a mutex on                               // someObject and whose destructor will                               // un-acquire it        // if something throws here, the lock on someObject will       // be unlocked    }    // etc.}这可以很容易地用Java / C#方式编写(使用C ++宏):void foo(){    // etc.    LOCK(someObject)    {       // if something throws here, the lock on someObject will       // be unlocked    }    // etc.}2. RAII有其他用途白兔:[唱歌]我迟到/我迟到/非常重要的约会。/没时间说“你好。” / 再见。/我迟到了,我迟到了,我迟到了。- 爱丽丝梦游仙境(迪士尼版,1951年)你知道什么时候会调用构造函数(在对象声明中),并且你知道何时会调用它相应的析构函数(在作用域的出口处),所以你可以用一行来编写几乎神奇的代码。欢迎来到C ++仙境(至少从C ++开发人员的角度来看)。例如,您可以编写一个计数器对象(我将其作为练习)并仅通过声明其变量来使用它,就像上面使用的锁对象一样:void foo(){    double timeElapsed = 0 ;    {       Counter counter(timeElapsed) ;       // do something lengthy    }    // now, the timeElapsed variable contain the time elapsed    // from the Counter's declaration till the scope exit}当然,可以使用宏来编写Java / C#方式:void foo(){    double timeElapsed = 0 ;    COUNTER(timeElapsed)    {       // do something lengthy    }    // now, the timeElapsed variable contain the time elapsed    // from the Counter's declaration till the scope exit}3.为什么C ++缺乏finally?[SHOUTING]这是最后的倒计时!- 欧洲:最后的倒计时(抱歉,我没有引号,这里...... :-)该finally子句在C#/ Java中用于处理范围退出时的资源处理(通过return抛出异常或抛出异常)。精明的规范读者会注意到C ++没有finally子句。这不是错误,因为C ++不需要它,因为RAII已经处理了资源处理。(相信我,编写C ++析构函数比编写正确的Java finally子句,甚至是C#的正确Dispose方法更容易)。不过,有时,一个finally条款会很酷。我们可以用C ++做吗?我们可以!再次使用RAII。结论:RAII不仅仅是C ++中的哲学:它是C ++RAII?这是C ++ !!!- C ++开发人员愤怒的评论,被一位不起眼的斯巴达国王和他的300个朋友无耻地复制当您在C ++中达到某种程度的经验时,就开发人员和析构函数自动执行而言,您开始考虑RAII。你开始在思维范围,以及{和}人物成为在代码中最重要的人。几乎所有东西都适合RAII:异常安全,互斥,数据库连接,数据库请求,服务器连接,时钟,操作系统句柄等,以及最后但并非最不重要的内存。数据库部分是不可忽略的,因为,如果您接受支付价格,您甚至可以用“ 事务编程 ”方式编写,执行代码行和代码行,直到最终确定是否要提交所有更改,或者,如果不可能,将所有更改都还原(只要每行至少满足强异常保证)。(参见Herb的Sutter关于事务编程的文章的第二部分)。就像拼图一样,一切都很合适。RAII是C ++的重要组成部分,如果没有它,C ++就不可能是C ++。这解释了为什么有经验的C ++开发人员如此迷恋RAII,以及为什么RAII是他们在尝试使用其他语言时首先搜索的内容。它解释了为什么垃圾收集器本身就是一项非常出色的技术,从C ++开发人员的角度来看并不那么令人印象深刻:RAII已经处理了GC处理的大多数案例GC在纯托管对象上使用循环引用比RAII更好(通过智能使用弱指针缓解)GC仍然限于内存,而RAII可以处理任何类型的资源。如上所述,RAII可以做很多事情......

一只甜甜圈

请参阅:除了C ++之外,其他语言的程序员是否使用,了解或了解RAII?RAII和C ++中的智能指针C ++是否支持'finally'块?(我听到的'RAII'是什么?)RAII与例外等等..
随时随地看视频慕课网APP
我要回答