当前位置:  开发笔记 > 编程语言 > 正文

C++中的垃圾收集 - 为什么?

如何解决《C++中的垃圾收集-为什么?》经验,为你挑选了11个好方法。

我一直听到人们抱怨C++没有垃圾收集.我还听说C++标准委员会正在考虑将其添加到该语言中.我担心我只是没有看到它的意义...使用智能指针的RAII消除了它的需要,对吧?

我对垃圾收集的唯一经验是在几台便宜的80年代家用电脑上,这意味着系统每隔一段时间就会冻结几秒钟.我确信它从那时起已有所改善,但正如你可以猜到的那样,这并没有让我对此持高度评价.

垃圾收集为经验丰富的C++开发人员提供了哪些优势?



1> paercebal..:

我一直听到人们抱怨C++没有垃圾收集.

我很抱歉.认真.

C++有RAII,我总是抱怨在Garbage Collected语言中找不到RAII(或阉割的RAII).

垃圾收集为经验丰富的C++开发人员提供了哪些优势?

另一个工具.

Matt J在他的帖子中写得非常正确(C++中的垃圾收集 - 为什么?):我们不需要C++功能,因为大多数都可以用C编码,我们不需要C功能,因为大多数功能都可以在汇编等编码.C++必须发展.

作为开发人员:我不关心GC.我尝试了RAII和GC,我发现RAII非常优越.正如Greg Rogers在他的文章(C++中的垃圾收集 - 为什么?)中所说的那样,内存泄漏并不是那么可怕(至少在C++中,如果真的使用C++,它们很少见),以证明GC而不是RAII.GC具有非确定性的释放/终止,并且只是编写一个不关心特定内存选择的代码的方法.

最后一句很重要:编写"juste不关心"的代码非常重要.以同样的方式在C++ RAII中我们不关心资源释放,因为RAII为我们做了,或者为了对象初始化,因为构造函数为我们做了,有时很重要的是只需编写代码而不关心谁是内存的所有者,以及我们需要什么样的指针(共享,弱,等等)或这段代码.在C++中似乎需要GC.(即使我个人没有看到它)

在C++中使用GC的一个例子

有时,在应用程序中,您有"浮动数据".想象一下树状结构的数据,但没有人真的是数据的"拥有者"(没有人真正关心它何时会被破坏).多个对象可以使用它,然后丢弃它.当没有人再使用它时,你希望它被释放.

C++方法使用智能指针.我想到了boost :: shared_ptr.因此,每个数据都由其自己的共享指针拥有.凉.问题是当每条数据都可以引用另一条数据时.您不能使用共享指针,因为它们使用的是引用计数器,它不支持循环引用(A指向B,B指向A).所以你必须知道很多关于在哪里使用弱指针(boost :: weak_ptr),以及何时使用共享指针.

使用GC,您只需使用树结构数据.

缺点是,你必须不在乎 "流动数据"真的会被摧毁.只有它会被摧毁.

结论

所以最后,如果做得恰当,并且与C++的当前习惯用法兼容,GC将成为C++的另一个好工具.

C++是一种多范式语言:添加GC可能会让一些C++粉丝因为叛国而哭泣,但最后,这可能是一个好主意,我猜C++标准委员会不会让这种主要功能打破语言,所以我们可以信任他们做出必要的工作来启用一个不会干扰C++的正确的C++ GC:像在C++中一样,如果你不需要一个功能,不要使用它,它将花费你没有.


@Earwicker:我所知道的GC主要语言(即非脚本非小众语言)是Java和C#.Java没有任何RAII,而C#的RAII在来自C++时远远不能令人满意.不过,我们有一个共同的观点:如果我们能够负担得起,使用在后台处理内存分配的语言可以节省大量时间.
更重要的是,您需要RAII和GC.一个不排除另一个.大多数使用GC的语言从一开始就融入其中也有类似RAII的习语.你可能认为你不需要*GC,但如果可以的话,谁会诚实地拒绝更大的便利和更高的生产力?Pervasive GC使您能够以不同的方式进行设计和编码,从而提高您的工作效率.另一个经常被忽略的优点是它通常表现更好!
@Ctrl Alt D-1337:你能举一些"C++的RAII有限可能更好"的例子吗?有没有更好的RAII语言?
@Thomas Eding:不,我的意思是树.(XML)DOM是类似树的结构,但是除了指向其子节点的指针列表之外,每个节点(通常)还有指向其父节点的指针(甚至可能是指向其文档节点的指针).意味着两个节点始终具有循环关系.可以使用每个根的所有者类(文档?),或者使用shared_ptr/weak_ptr的混合,或者使用GC来处理它,这意味着您可以保留部分或全部DOM,而无需真正关心哪个部分将被销毁或不,只通过设置一些指向null的指针...
@Thomas Eding:...... GC带来的问题当然是你拿着一个小的DOM节点,却没有意识到整个树都附着在它上面.这是一种泄漏.然后,如果由于某种原因,某些隐藏的代码部分包含指向该小节点的指针(例如,某些事件侦听器,如C#中的委托,或Java中的匿名内部类),那么您确实有泄漏,无论GC ......总而言之,所有类型的内存处理都有它们的问题......在C++中使用GC的好处就是拥有**内存处理的选择......: - )

2> Matt J..:

简短的回答是垃圾收集原则上与使用智能指针的RAII非常相似.如果您分配的每一块内存都位于一个对象中,并且该对象仅由智能指针引用,那么您有一些接近垃圾收集的东西(可能更好).优势来自于不必如此明智地确定每个对象的范围和智能指针,并让运行时为您完成工作.

这个问题似乎类似于"C++必须为有经验的装配开发人员提供什么?指令和子程序消除了对它的需要,对吧?"


如果您使用引用计数的智能指针,请注意引用循环.垃圾收集的一个优点是它不会被引用循环混淆.
如果正确使用boost :: weak_ptr,引用循环不是问题.
除了智能指针在它们超出范围时释放资源,GC可以让它们闲置多年.它可以产生很大的不同.

3> Greg Rogers..:

随着像valgrind这样的优秀记忆检查器的出现,我认为垃圾收集并没有太多用作安全网"万一"我们忘了解除某些东西 - 特别是因为它对管理更通用的资源案例没有多大帮助除了记忆(虽然这些不常见).此外,在我看到的代码中,显式分配和释放内存(即使使用智能指针)也是相当罕见的,因为容器通常更简单,更好.

但垃圾收集可能会提供潜在的性能优势,特别是如果很多短期对象正在堆分配.GC还可能为新创建的对象提供更好的引用局部性(与堆栈中的对象相当).



4> MSalters..:

C++中GC支持的激励因素似乎是lambda编程,匿名函数等.事实证明,lambda库可以在不关心清理的情况下分配内存.普通开发人员的好处是更简单,更可靠,更快速地编译lambda库.

GC还有助于模拟无限内存; 您需要删除POD的唯一原因是您需要回收内存.如果您有GC或无限内存,则无需再删除POD.


只有当你将函数式编程视为没有经验的程序员的东西时,是的.gc是一个非常强大的工具,具有成本:像所有强大的抽象一样,它可以专注于手头的问题,但有时,它会中断,你必须低于抽象.
换句话说,它只是缺乏经验的程序员的拐杖?:-)

5> coppro..:

该委员会没有添加垃圾收集,他们正在添加一些功能,以便更安全地实现垃圾收集.只有时间才能证明它们对未来的编译器是否真的有任何影响.具体实现可能有很大差异,但最有可能涉及基于可达性的收集,这可能涉及轻微挂起,具体取决于它是如何完成的.

但有一件事是,没有符合标准的垃圾收集器能够调用析构函数 - 只能静默地重用丢失的内存.



6> David Courna..:

我不明白人们如何争辩RAII取代GC,或者是非常优越的.有很多案例由gc处理,RAII根本无法处理.他们是不同的野兽.

首先,RAII不是防弹:它可以解决一些在C++中普遍存在的常见故障,但在很多情况下RAII根本没有帮助; 异步事件(如UNIX下的信号)很脆弱.从根本上说,RAII依赖于范围界定:当变量超出范围时,它会自动释放(假设析构函数当然正确实现).

这是一个简单的例子,auto_ptr或RAII都不能帮助你:

#include 
#include 
#include 
#include 

#include 

using namespace std;

volatile sig_atomic_t got_sigint = 0;

class A {
        public:
                A() { printf("ctor\n"); };
                ~A() { printf("dtor\n"); };
};

void catch_sigint (int sig)
{
        got_sigint = 1;
}

/* Emulate expensive computation */
void do_something()
{
        sleep(3);
}

void handle_sigint()
{
        printf("Caught SIGINT\n");
        exit(EXIT_FAILURE);
}

int main (void)
{
        A a;
        auto_ptr aa(new A);

        signal(SIGINT, catch_sigint);

        while (1) {
                if (got_sigint == 0) {
                        do_something();
                } else {
                        handle_sigint();
                        return -1;
                }
        }
}

A的析构函数永远不会被调用.当然,这是一个人为的,有点人为的例子,但实际上也会发生类似的情况; 例如,当你的代码被处理SIGINT的另一个代码调用时,你完全没有控制权(具体的例子:matlab中的mex扩展).这也是为什么最终在python中不能保证执行某些东西的原因.在这种情况下,Gc可以帮助您.

其他成语不能很好地解决这个问题:在任何非平凡的程序中,你都需要有状态的对象(我在这里广泛使用对象这个词,它可以是语言允许的任何结构); 如果你需要在一个函数之外控制状态,那么用RAII就不能轻易做到这一点(这就是RAII对异步编程没有帮助的原因).OTOH,gc可以看到进程的整个内存,也就是它知道它分配的所有对象,并且可以异步清理.

使用gc也可以快得多,原因相同:如果你需要分配/解除分配许多对象(特别是小对象),gc将远远超过RAII,除非你编写自定义分配器,因为gc可以分配/一次通过清理许多物体.一些众所周知的C++项目使用gc,即使性能很重要(例如参见Tim Sweenie关于在虚幻竞技场中使用gc:http://lambda-the-ultimate.org/node/1277).GC以延迟为代价基本上增加了吞吐量.

当然,有些情况下RAII优于gc; 特别是,gc概念主要关注的是内存,而这并不是唯一的资源.像文件等等......可以用RAII很好地处理.没有像python或ruby这样的内存处理的语言确实有像RAII那样的情况,BTW(在python中使用语句).当您需要控制何时释放资源时,RAII非常有用,例如,文件或锁通常就是这种情况.


@cournape:对不起,但这个例子看起来很虚伪.在信号处理程序中调用exit()不会允许垃圾收集来清理任何东西.
更接近事实的是,有很多案例RAII可以处理垃圾收集不能.GC专注于记忆; RAII处理任何类型的资源.据我所知,智能指针消除了你的"脆弱性"论点.
我同意RAII可以帮助线程锁定,但不是灵丹妙药.我想我真的很担心RAII是一个神奇的解决方案,它可以神奇地防止死锁,内存泄漏等......这绝对是有用的,但它不是一个魔术棒.
毫无疑问,RAII有其缺陷.但我没有看到GC解决它们,它只是将一组缺陷换成了另一组.
嗯,你的例子和GC一样糟糕,除了GC,即使在退出时清理了对象,它仍然不会调用它的终结器(因为终结线程第二次运行它被收集).

7> JohnMcG..:

垃圾收集为经验丰富的C++开发人员提供了哪些优势?

无需在经验较少的同事代码中追查资源泄漏.


但是,建立和执行这些规则会产生成本,只是将它们作为指导并不意味着它们将始终被遵循.
无论如何,在使用RAII和智能指针时很容易泄漏内存.例如,一个信号处理程序改变代码路径并且永远不会返回被调用者:在这种情况下,RAII或智能指针都不会帮助你.
如果你坚持每个人都使用RAII和智能指针,那么资源泄漏就不会发生.
对于它的价值,即使像.NET这样的智能标记和扫描垃圾收集器也无法阻止所有资源泄漏.

8> xtofl..:

垃圾收集允许推迟关于谁拥有对象的决定.

C++使用值语义,因此对于RAII,实际上,当超出范围时会重新收集对象.这有时被称为"立即GC".

当您的程序开始使用引用语义(通过智能指针等...)时,语言不再支持您,您将留下智能指针库的智慧.

关于GC的棘手问题在于决定何时不再需要某个对象.


@Head Geek:不完全是.如果您有两个对象,A和B,通过智能指针指向,那么你是对的.现在,如果A也指向B,并且B也指向A,那么您就有问题,并且必须通过使用weak_ptr和/或shared_ptr来决定谁拥有该对象.
智能指针完全消除了决定谁拥有对象的需要.

9> tragomaskhal..:

假设由于C++没有将垃圾收集整合到语言中,因此不能在C++期间使用垃圾收集,这是一个常见错误.这是无稽之谈.我知道精英C++程序员使用Boehm收集器当然是他们的工作.



10> Suma..:
更简单的线程安全性和可扩展性

GC的一个属性在某些情况下可能非常重要.指针的赋值在大多数平台上自然是原子的,而创建线程安全的引用计数("智能")指针非常困难并且引入了显着的同步开销.因此,智能指针经常在多核架构上被告知"不能很好地扩展".



11> Ben Voigt..:

垃圾收集使RCU无锁同步更容易正确有效地实现.

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有