注意,RAII是一种编程习惯,而GC是一种内存管理技术。因此,我们正在将苹果与橙子进行比较。
但是,我们可以限制RAII到它的内存管理方面唯一的和比较,为GC技术。
所谓的基于RAII内存管理技术之间的主要区别(这实际上意味着引用计数,在当你考虑内存资源,而忽略了其他的如文件至少)和真正的垃圾回收技术是处理循环引用(用于循环图) 。
使用引用计数时,您需要为它们专门编码(使用弱引用或其他内容)。
在许多有用的情况下(认为std::vector<std::map<std::string,int>>
),引用计数是隐式的(因为它只能为0或1),实际上被省略了,但是构造函数和析构函数(对于RAII来说是必需的)的行为就像有一个引用计数位(实际上不存在)。在std::shared_ptr
有确切的引用计数器。但内存仍然隐含 手动管理(与new
和delete
引发的内部构造和析构),但“隐性” delete
(在析构函数)给出了自动内存管理的错觉。然而,电话new
和delete
还是发生了(而且花费的时间)。
顺便说一句,GC实现可能(并且经常)以某种特殊方式处理循环性,但是您将负担留给了GC(例如,阅读有关Cheney算法的信息)。
一些GC算法(尤其是代复制垃圾收集器)也懒得释放内存单独的对象,它是释放集体副本之后。实际上,Ocaml GC(或SBCL)比真正的C ++ RAII编程风格(对于某些(不是全部)算法)要快。
某些GC提供了终结处理(主要用于管理非内存外部资源,如文件),但是您很少使用它(因为大多数值仅消耗内存资源)。缺点是最终确定不提供任何时序保证。实际上,使用终结处理的程序将其作为最后的手段(例如,关闭文件仍应在终结处理之外或与它们明确地或多或少地明确地发生)。
使用GC(至少在使用不当时,至少在使用RAII时)仍然会出现内存泄漏,例如,某个值保留在某个变量或某个字段中,但以后再也不会使用。他们只是很少发生。
我建议阅读垃圾收集手册。
在您的C ++代码中,您可以使用Boehm的GC或Ravenbrook的MPS或编写您自己的跟踪垃圾收集器。当然,使用GC是一个折衷(存在一些不便,例如不确定性,缺乏时序保证等)。
我不认为RAII在所有情况下都是处理内存的最终方法。在很多情况下,比起用C ++ 17用花哨的RAII风格进行编码,用真正有效的GC实现(例如Ocaml或SBCL)对程序进行编码可能更容易(开发)和更快(执行)。在其他情况下则不是。YMMV。
例如,如果您以最奇特的RAII风格用C ++ 17编写Scheme解释器,您仍然需要在其中编码(或使用)显式GC(因为Scheme堆具有循环性)。而且,出于充分的理由,大多数证明助手都是用GC编码的语言编码的,通常是函数式的(我知道的唯一用C ++编码的就是Lean)。
顺便说一句,我感兴趣的是找到这样的Scheme的C ++ 17实现(但对自己编码不那么感兴趣),最好具有一些多线程能力。