我问这个问题,首先不是因为垃圾收集的优点。我问这个问题的主要原因是我知道Bjarne Stroustrup说过c++在某个时间点上会有一个垃圾收集器。

话虽如此,为什么还没有加入呢?c++已经有了一些垃圾收集器。这是那种“说起来容易做起来难”的事情吗?或者还有其他原因没有添加它(并且不会在c++ 11中添加)?

交叉链接:

c++的垃圾收集器

澄清一下,我理解c++最初创建时没有垃圾收集器的原因。我想知道为什么不能添加收集器。


当前回答

简短的回答: 我们不知道如何高效地(在很少的时间和空间开销下)并且始终(在所有可能的情况下)正确地进行垃圾收集。

长一点的回答: 就像C一样,c++是一种系统语言;这意味着当您编写系统代码时,例如操作系统时,将使用它。换句话说,c++就像C一样,以尽可能好的性能作为主要目标。该语言标准不会增加任何可能阻碍性能目标的特性。

这暂停了这个问题:为什么垃圾收集会影响性能?主要原因是,当涉及到实现时,我们(计算机科学家)不知道如何在所有情况下以最小的开销进行垃圾收集。因此,c++编译器和运行时系统不可能一直有效地执行垃圾收集。另一方面,c++程序员应该了解他的设计/实现,他是决定如何最好地进行垃圾收集的最佳人选。

最后,如果控制(硬件、细节等)和性能(时间、空间、电源等)不是主要的限制,那么c++就不是合适的工具。其他语言可能会更好,并提供更多[隐藏的]运行时管理,以及必要的开销。

其他回答

tl;dr:因为现代c++不需要垃圾收集。

Bjarne Stroustrup对此问题的常见问题解答如下:

我不喜欢垃圾。我不喜欢乱扔垃圾。我的理想是通过不产生任何垃圾来消除对垃圾收集器的需求。现在这是可能的。


现在编写的代码(c++ 17和遵循官方核心指南)的情况如下:

Most memory ownership-related code is in libraries (especially those providing containers). Most use of code involving memory ownership follows the CADRe or RAII pattern, so allocation is made on construction and deallocation on destruction, which happens when exiting the scope in which something was allocated. You do not explicitly allocate or deallocate memory directly. Raw pointers do not own memory (if you've followed the guidelines), so you can't leak by passing them around. If you're wondering how you're going to pass the starting addresses of sequences of values in memory - you can and should prefer span's, obviating the need for raw pointers. You can still use such pointers, they'll just be non-owning. If you really need an owning "pointer", you use C++' standard-library smart pointers - they can't leak, and are decently efficient (although the ABI can get in the way of that). Alternatively, you can pass ownership across scope boundaries with "owner pointers". These are uncommon and must be used explicitly; but when adopted - they allow for nice static checking against leaks.

“哦,是吗?但是……

... 如果我只是像以前写c++那样写代码?”

实际上,您可以忽略所有的指导方针,编写有漏洞的应用程序代码——它将像往常一样编译和运行(并泄漏)。

但这并不是一种“不要这么做”的情况,即开发者应该保持良好的自我控制;编写不符合规范的代码并不简单,也没有更快,也没有更好的性能。慢慢地,它也会变得更加难以编写,因为你将面临与符合规范的代码所提供和期望的“阻抗不匹配”的增加。

... 如果我retrepret_cast ?或者做复杂的指针运算?还是其他类似的黑客?”

事实上,如果你用心去做,你可以编写一些代码,尽管你很好地遵循了指导原则。但是:

您很少会这样做(就代码中的位置而言,而不一定是就执行时间的比例而言) 你只会故意这么做,而不是意外。 这样做将在符合准则的代码库中脱颖而出。 在这种代码中,您无论如何都可以在另一种语言中绕过GC。

... 图书馆发展?”

如果你是c++库开发人员,那么你确实会编写包含原始指针的不安全代码,并且你被要求谨慎而负责地编码——但这些是由专家编写的自包含代码片段(更重要的是,由专家评审)。

所以,就像Bjarne说的:一般来说,没有动机去收集垃圾,因为你只是确保不产生垃圾。GC正在成为c++的一个不成问题的问题。

这并不是说,当您希望使用自定义分配和反分配策略时,GC对于某些特定的应用程序不是一个有趣的问题。对于那些您想要自定义分配和反分配的对象,而不是语言级GC。

If you want automatic garbage collection, there are good commercial and public-domain garbage collectors for C++. For applications where garbage collection is suitable, C++ is an excellent garbage collected language with a performance that compares favorably with other garbage collected languages. See The C++ Programming Language (4rd Edition) for a discussion of automatic garbage collection in C++. See also, Hans-J. Boehm's site for C and C++ garbage collection (archive). Also, C++ supports programming techniques that allow memory management to be safe and implicit without a garbage collector. I consider garbage collection a last choice and an imperfect way of handling for resource management. That does not mean that it is never useful, just that there are better approaches in many situations.

来源:http://www.stroustrup.com/bs_faq.html垃圾收集

至于为什么它没有内置它,如果我没记错的话,它是在GC出现之前发明的,我不相信这种语言有GC,有几个原因(我不相信它有GC)。C)向后兼容。

希望这能有所帮助。

原始C语言背后的一个基本原则是,内存是由一系列字节组成的,代码只需要关心这些字节在被使用的确切时刻意味着什么。现代C语言允许编译器施加额外的限制,但C语言包括——c++保留了——将指针分解为字节序列,将包含相同值的任何字节序列组装为指针,然后使用该指针访问先前的对象。

While that ability can be useful--or even indispensable--in some kinds of applications, a language that includes that ability will be very limited in its ability to support any kind of useful and reliable garbage collection. If a compiler doesn't know everything that has been done with the bits that made up a pointer, it will have no way of knowing whether information sufficient to reconstruct the pointer might exist somewhere in the universe. Since it would be possible for that information to be stored in ways that the computer wouldn't be able to access even if it knew about them (e.g. the bytes making up the pointer might have been shown on the screen long enough for someone to write them down on a piece of paper), it may be literally impossible for a computer to know whether a pointer could possibly be used in the future.

An interesting quirk of many garbage-collected frameworks is that an object reference not defined by the bit patterns contained therein, but by the relationship between the bits held in the object reference and other information held elsewhere. In C and C++, if the bit pattern stored in a pointer identifies an object, that bit pattern will identify that object until the object is explicitly destroyed. In a typical GC system, an object may be represented by a bit pattern 0x1234ABCD at one moment in time, but the next GC cycle might replace all references to 0x1234ABCD with references to 0x4321BABE, whereupon the object would be represented by the latter pattern. Even if one were to display the bit pattern associated with an object reference and then later read it back from the keyboard, there would be no expectation that the same bit pattern would be usable to identify the same object (or any object).

要回答关于c++的大多数“为什么”问题,请阅读c++的设计与进化

虽然这是一个老问题,但仍然有一个问题我没有看到任何人解决过:垃圾收集几乎不可能指定。

特别是,c++标准非常谨慎地根据外部可观察到的行为来指定语言,而不是实现如何实现该行为。然而,在垃圾收集的情况下,实际上没有外部可观察到的行为。

The general idea of garbage collection is that it should make a reasonable attempt at assuring that a memory allocation will succeed. Unfortunately, it's essentially impossible to guarantee that any memory allocation will succeed, even if you do have a garbage collector in operation. This is true to some extent in any case, but particularly so in the case of C++, because it's (probably) not possible to use a copying collector (or anything similar) that moves objects in memory during a collection cycle.

如果不能移动对象,就不能创建一个单独的、连续的内存空间来进行分配——这意味着您的堆(或自由存储区,或任何您喜欢称呼它的地方)可能会随着时间的推移而变得碎片化。这反过来又会阻止分配成功,即使空闲内存比请求的内存多。

尽管有可能提出某种保证,即(本质上)如果您重复完全相同的分配模式,并且它在第一次成功,那么它将在后续迭代中继续成功,前提是分配的内存在迭代之间变得不可访问。这是一个非常微弱的保证,基本上毫无用处,但我看不到任何加强它的合理希望。

即便如此,它也比为c++所提议的更强大。之前的提议[警告:PDF](被放弃了)根本不能保证任何东西。在28页的提案中,你在外部可观察到的行为中看到的是一个单一的(不规范的)注释:

[注意:对于垃圾收集程序,高质量的托管实现应该尝试最大限度地回收不可访问的内存量。-结束注释]

至少对我来说,这引发了一个关于投资回报的严肃问题。我们将破坏现有的代码(没有人知道具体破坏了多少,但肯定是相当多),对实现提出新的要求,对代码提出新的限制,而我们得到的回报很可能是什么都没有?

即使在最好的情况下,我们得到的是基于Java测试的程序,以现在相同的速度运行可能需要大约6倍的内存。更糟糕的是,垃圾收集从一开始就是Java的一部分——c++对垃圾收集器施加了足够多的限制,以至于它几乎肯定会有更糟糕的成本/收益比(即使我们超出了提案所保证的范围,并假设会有一些收益)。

我要用数学方法总结一下情况:这是一个复杂的情况。数学家都知道,复数有两部分:实数和虚数。在我看来,我们这里的成本是真实的,但收益(至少大部分)是虚构的。