为什么 C++ 没有垃圾收集器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/147130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:13:41  来源:igfitidea点击:

Why doesn't C++ have a garbage collector?

c++garbage-collectionc++11

提问by Jason Baker

I'm not asking this question because of the merits of garbage collection first of all. My main reason for asking this is that I do know that Bjarne Stroustrup has said that C++ will have a garbage collector at some point in time.

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

With that said, why hasn't it been added? There are already some garbage collectors for C++. Is this just one of those "easier said than done" type things? Or are there other reasons it hasn't been added (and won't be added in C++11)?

说了这么多,怎么还没加呢?已经有一些用于 C++ 的垃圾收集器。这只是“说起来容易做起来难”类型的事情之一吗?还是有其他原因没有添加它(并且不会在 C++11 中添加)?

Cross links:

交叉链接:

Just to clarify, I understand the reasons why C++ didn't have a garbage collector when it was first created. I'm wondering why the collector can't be added in.

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

采纳答案by Brian R. Bondy

Implicit garbage collection could have been added in, but it just didn't make the cut. Probably due to not just implementation complications, but also due to people not being able to come to a general consensus fast enough.

可以添加隐式垃圾收集,但它只是没有成功。可能不仅是由于实施的复杂性,还因为人们无法足够快地达成普遍共识。

A quote from Bjarne Stroustrup himself:

引用 Bjarne Stroustrup 本人的话:

I had hoped that a garbage collector which could be optionally enabled would be part of C++0x, but there were enough technical problems that I have to make do with just a detailed specification of how such a collector integrates with the rest of the language, if provided. As is the case with essentially all C++0x features, an experimental implementation exists.

我曾希望可以选择启用的垃圾收集器将成为 C++0x 的一部分,但有足够多的技术问题,我只需要详细说明此类收集器如何与语言的其余部分集成,如果提供。与基本上所有 C++0x 功能的情况一样,存在实验性实现。

There is a good discussion of the topic here.

有话题商量好了这里

General overview:

总体概述:

C++ is very powerful and allows you to do almost anything. For this reason it doesn't automatically push many things onto you that might impact performance. Garbage collection can be easily implemented with smart pointers (objects that wrap pointers with a reference count, which auto delete themselves when the reference count reaches 0).

C++ 非常强大,几乎可以让您做任何事情。出于这个原因,它不会自动将许多可能影响性能的事情推送给您。垃圾收集可以使用智能指针(用引用计数包装指针的对象,当引用计数达到 0 时自动删除自己)轻松实现。

C++ was built with competitors in mind that did not have garbage collection. Efficiency was the main concern that C++ had to fend off criticism from in comparison to C and others.

C++ 是为没有垃圾收集的竞争对手而构建的。与 C 和其他语言相比,效率是 C++ 必须抵御批评的主要问题。

There are 2 types of garbage collection...

有两种类型的垃圾收集...

Explicit garbage collection:

显式垃圾收集:

C++0x will have garbage collection via pointers created with shared_ptr

C++0x 将通过使用 shared_ptr 创建的指针进行垃圾收集

If you want it you can use it, if you don't want it you aren't forced into using it.

如果你想要它,你可以使用它,如果你不想要它,你不会被迫使用它。

You can currently use boost:shared_ptr as well if you don't want to wait for C++0x.

如果您不想等待 C++0x,您目前也可以使用 boost:shared_ptr。

Implicit garbage collection:

隐式垃圾收集:

It does not have transparent garbage collection though. It will be a focus point for future C++ specs though.

虽然它没有透明的垃圾收集。不过,它将成为未来 C++ 规范的重点。

Why Tr1 doesn't have implicit garbage collection?

为什么 Tr1 没有隐式垃圾收集?

There are a lot of things that tr1 of C++0x should have had, Bjarne Stroustrup in previous interviews stated that tr1 didn't have as much as he would have liked.

C++0x 的 tr1 应该有很多东西,Bjarne Stroustrup 在之前的采访中表示 tr1 没有他想要的那么多。

回答by Matthieu M.

To add to the debate here.

在这里添加辩论。

There are known issues with garbage collection, and understanding them helps understanding why there is none in C++.

垃圾收集存在一些已知问题,了解它们有助于理解为什么 C++ 中没有这些问题。

1. Performance ?

1. 性能?

The first complaint is often about performance, but most people don't really realize what they are talking about. As illustrated by Martin Beckettthe problem may not be performance per se, but the predictability of performance.

第一个抱怨通常是关于性能的,但大多数人并没有真正意识到他们在谈论什么。Martin Beckett问题所说明的可能不是性能本身,而是性能的可预测性。

There are currently 2 families of GC that are widely deployed:

目前有 2 个 GC 系列被广泛部署:

  • Mark-And-Sweep kind
  • Reference-Counting kind
  • 标记和清除类型
  • 引用计数类型

The Mark And Sweepis faster (less impact on overall performance) but it suffers from a "freeze the world" syndrome: i.e. when the GC kicks in, everything else is stopped until the GC has made its cleanup. If you wish to build a server that answers in a few milliseconds... some transactions will not live up to your expectations :)

Mark And Sweep快(对整体性能的影响较小),但它从一个“冻结世界”综合症患有:即当在GC踢,一切停止,直到GC作出了清理。如果您希望构建一个在几毫秒内响应的服务器......有些事务不会达到您的期望:)

The problem of Reference Countingis different: reference-counting adds overhead, especially in Multi-Threading environments because you need to have an atomic count. Furthermore there is the problem of reference cycles so you need a clever algorithm to detect those cycles and eliminate them (generally implement by a "freeze the world" too, though less frequent). In general, as of today, this kind (even though normally more responsive or rather, freezing less often) is slower than the Mark And Sweep.

问题Reference Counting是不同的:引用计数增加了开销,尤其是在多线程环境中,因为您需要有一个原子计数。此外,还有参考循环的问题,因此您需要一种巧妙的算法来检测这些循环并消除它们(通常也通过“冻结世界”来实现,尽管频率较低)。一般来说,截至今天,这种(即使通常响应更快或更确切地说,冻结的频率更低)比Mark And Sweep.

I have seen a paper by Eiffel implementers that were trying to implement a Reference CountingGarbage Collector that would have a similar global performance to Mark And Sweepwithout the "Freeze The World" aspect. It required a separate thread for the GC (typical). The algorithm was a bit frightening (at the end) but the paper made a good job of introducing the concepts one at a time and showing the evolution of the algorithm from the "simple" version to the full-fledged one. Recommended reading if only I could put my hands back on the PDF file...

我看过 Eiffel 实现者的一篇论文,他们试图实现一个Reference Counting垃圾收集器,该垃圾收集器将具有与Mark And Sweep没有“冻结世界”方面相似的全局性能。它需要一个单独的 GC 线程(典型)。算法有点吓人(最后),但论文很好地一次介绍了一个概念,并展示了算法从“简单”版本到成熟版本的演变。推荐阅读,如果我能把手放回PDF文件...

2. Resources Acquisition Is Initialization (RAII)

2.资源获取即初始化(RAII)

It's a common idiom in C++that you will wrap the ownership of resources within an object to ensure that they are properly released. It's mostly used for memory since we don't have garbage collection, but it's also useful nonetheless for many other situations:

这是一个常见的习惯用法C++,您将资源的所有权包装在一个对象中以确保它们被正确释放。它主要用于内存,因为我们没有垃圾收集,但它在许多其他情况下也很有用:

  • locks (multi-thread, file handle, ...)
  • connections (to a database, another server, ...)
  • 锁(多线程,文件句柄,...)
  • 连接(到数据库,另一台服务器,...)

The idea is to properly control the lifetime of the object:

这个想法是正确控制对象的生命周期:

  • it should be alive as long as you need it
  • it should be killed when you're done with it
  • 只要你需要它就应该活着
  • 当你完成它时它应该被杀死

The problem of GC is that if it helps with the former and ultimately guarantees that later... this "ultimate" may not be sufficient. If you release a lock, you'd really like that it be released now, so that it does not block any further calls!

GC 的问题在于,如果它对前者有所帮助,并最终保证了后者……这个“终极”可能还不够。如果你释放一个锁,你真的希望它现在就被释放,这样它就不会阻塞任何进一步的调用!

Languages with GC have two work arounds:

带有 GC 的语言有两种解决方法:

  • don't use GC when stack allocation is sufficient: it's normally for performance issues, but in our case it really helps since the scope defines the lifetime
  • usingconstruct... but it's explicit (weak) RAII while in C++ RAII is implicit so that the user CANNOT unwittingly make the error (by omitting the usingkeyword)
  • 当堆栈分配足够时不要使用 GC:它通常是为了性能问题,但在我们的例子中它确实有帮助,因为范围定义了生命周期
  • using构造...但它是显式的(弱)RAII,而在 C++ RAII 中是隐式的,因此用户不能在不知不觉中犯错误(通过省略using关键字)

3. Smart Pointers

3. 智能指针

Smart pointers often appear as a silver bullet to handle memory in C++. Often times I have heard: we don't need GC after all, since we have smart pointers.

智能指针通常作为处理内存的灵丹妙药出现C++。我经常听到:毕竟我们不需要 GC,因为我们有智能指针。

One could not be more wrong.

一个大错特错了。

Smart pointers do help: auto_ptrand unique_ptruse RAII concepts, extremely useful indeed. They are so simple that you can write them by yourself quite easily.

智能指针确实有帮助:auto_ptrunique_ptr使用 RAII 概念,确实非常有用。它们非常简单,您可以很容易地自己编写它们。

When one need to share ownership however it gets more difficult: you might share among multiple threads and there are a few subtle issues with the handling of the count. Therefore, one naturally goes toward shared_ptr.

然而,当需要共享所有权时,它会变得更加困难:您可能会在多个线程之间共享,并且在处理计数方面存在一些微妙的问题。于是,自然而然的走向了shared_ptr

It's great, that's what Boost for after all, but it's not a silver bullet. In fact, the main issue with shared_ptris that it emulates a GC implemented by Reference Countingbut you need to implement the cycle detection all by yourself... Urg

这很棒,毕竟这就是 Boost 的用途,但它不是灵丹妙药。事实上,主要的问题shared_ptr是它模拟了一个 GC 实现,Reference Counting但你需要自己实现循环检测...... Urg

Of course there is this weak_ptrthingy, but I have unfortunately already seen memory leaks despite the use of shared_ptrbecause of those cycles... and when you are in a Multi Threaded environment, it's extremely difficult to detect!

当然有这个weak_ptr东西,但不幸的是我已经看到内存泄漏,尽管shared_ptr由于这些循环而使用...而且当您处于多线程环境中时,它极难检测!

4. What's the solution ?

4. 解决办法是什么?

There is no silver bullet, but as always, it's definitely feasible. In the absence of GC one need to be clear on ownership:

没有灵丹妙药,但一如既往,它绝对可行。在没有 GC 的情况下,需要明确所有权:

  • prefer having a single owner at one given time, if possible
  • if not, make sure that your class diagram does not have any cycle pertaining to ownership and break them with subtle application of weak_ptr
  • 如果可能的话,更喜欢在某个特定时间拥有一个所有者
  • 如果没有,请确保您的类图没有任何与所有权相关的循环,并通过微妙的应用打破它们 weak_ptr

So indeed, it would be great to have a GC... however it's no trivial issue. And in the mean time, we just need to roll up our sleeves.

所以确实,有一个 GC 会很棒……但这不是小问题。与此同时,我们只需要卷起袖子。

回答by Martin Beckett

What type? should it be optimised for embedded washing machine controllers, cell phones, workstations or supercomputers?
Should it prioritise gui responsiveness or server loading?
should it use lots of memory or lots of CPU?

什么类型?是否应该针对嵌入式洗衣机控制器、手机、工作站或超级计算机进行优化?
它应该优先考虑 gui 响应性还是服务器加载?
它应该使用大量内存还是大量 CPU?

C/c++ is used in just too many different circumstances. I suspect something like boost smart pointers will be enough for most users

C/c++ 用于太多不同的情况。我怀疑像 boost 智能指针这样的东西对于大多数用户来说就足够了

Edit - Automatic garbage collectors aren't so much a problem of performance (you can always buy more server) it's a question of predicatable performance.
Not knowing when the GC is going to kick in is like employing a narcoleptic airline pilot, most of the time they are great - but when you really need responsiveness!

编辑 - 自动垃圾收集器不是性能问题(您总是可以购买更多服务器),而是可预测性能问题。
不知道 GC 何时开始工作就像雇用一名患有发作性睡病的航空公司飞行员一样,大多数时候他们都很棒——但是当您真的需要响应时!

回答by Greg Rogers

One of the biggest reasons that C++ doesn't have built in garbage collection is that getting garbage collection to play nice with destructors is really, really hard. As far as I know, nobody really knows how to solve it completely yet. There are alot of issues to deal with:

C++ 没有内置垃圾收集的最大原因之一是让垃圾收集与析构函数很好地配合是非常非常困难的。据我所知,目前还没有人真正知道如何彻底解决它。有很多问题需要处理:

  • deterministic lifetimes of objects (reference counting gives you this, but GC doesn't. Although it may not be that big of a deal).
  • what happens if a destructor throws when the object is being garbage collected? Most languages ignore this exception, since theres really no catch block to be able to transport it to, but this is probably not an acceptable solution for C++.
  • How to enable/disable it? Naturally it'd probably be a compile time decision but code that is written for GC vs code that is written for NOT GC is going to be very different and probably incompatible. How do you reconcile this?
  • 对象的确定性生命周期(引用计数为您提供了这一点,但 GC 没有。尽管这可能不是什么大问题)。
  • 如果在对象被垃圾收集时析构函数抛出会发生什么?大多数语言忽略这个异常,因为真的没有 catch 块能够将它传输到,但这可能不是 C++ 可接受的解决方案。
  • 如何启用/禁用它?当然,这可能是一个编译时的决定,但是为 GC 编写的代码与为 NOT GC 编写的代码将非常不同,并且可能不兼容。你如何调和这一点?

These are just a few of the problems faced.

这些只是面临的一些问题。

回答by Jerry Coffin

Though this is an oldquestion, there's still one problem that I don't see anybody having addressed at all: garbage collection is almost impossible to specify.

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

In particular, the C++ standard is quite careful to specify the language in terms of externally observable behavior, rather than how the implementation achieves that behavior. In the case of garbage collection, however, there isvirtually no externally observable behavior.

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

The general ideaof 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++ 的情况下尤其如此,因为(可能)不可能使用复制收集器(或任何类似的东西)在收集周期期间移动内存中的对象。

If you can't move objects, you can't create a single, contiguous memory space from which to do your allocations -- and that means your heap (or free store, or whatever you prefer to call it) can, and probably will, become fragmented over time. This, in turn, can prevent an allocation from succeeding, even when there's more memory free than the amount being requested.

如果你不能移动对象,你就不能创建一个单一的、连续的内存空间来进行你的分配——这意味着你的堆(或自由存储,或者你喜欢的任何称呼)可以,而且可能会,随着时间的推移变得支离破碎。这反过来又会阻止分配成功,即使可用内存多于请求的数量。

While it might be possible to come up with someguarantee that says (in essence) that if you repeat exactly the same pattern of allocation repeatedly, and it succeeded the first time, it will continue to succeed on subsequent iterations, provided that the allocated memory became inaccessible between iterations. That's such a weak guarantee it's essentially useless, but I can't see any reasonable hope of strengthening it.

虽然有可能提出一些保证(本质上)如果你重复重复完全相同的分配模式,并且第一次成功,它会在后续迭代中继续成功,前提是分配的内存在迭代之间变得无法访问。这是一个如此薄弱的保证,它本质上是无用的,但我看不到任何加强它的合理希望。

Even so, it's stronger than what has been proposed for C++. The previous proposal[warning: PDF] (that got dropped) didn't guarantee anything at all. In 28 pages of proposal, what you got in the way of externally observable behavior was a single (non-normative) note saying:

即便如此,它还是比为 C++ 提出的建议更强大。在先前的提案[警告:PDF](即得到下降)根本不保证任何事情。在 28 页的提案中,阻碍外部可观察行为的是一个(非规范性)注释说:

[ Note: For garbage collected programs, a high quality hosted implementation should attempt to maximize the amount of unreachable memory it reclaims. —end note ]

[注意:对于垃圾收集程序,高质量的托管实现应该尝试最大化它回收的无法访问的内存量。——尾注]

At least for me, this raises a seriousquestion about return on investment. We're going to break existing code (nobody's sure exactly how much, but definitely quite a bit), place new requirements on implementations and new restrictions on code, and what we get in return is quite possibly nothing at all?

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

Even at best, what we get are programs that, based on testing with Java, will probably require around six times as much memory to run at the same speed they do now. Worse, garbage collection was part of Java from the beginning -- C++ places enough more restrictions on the garbage collector that it will almost certainly have an even worsecost/benefit ratio (even if we go beyond what the proposal guaranteed and assume there would be some benefit).

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

I'd summarize the situation mathematically: this a complex situation. As any mathematician knows, a complex number has two parts: real and imaginary. It appears to me that what we have here are costs that are real, but benefits that are (at least mostly) imaginary.

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

回答by Rayne

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.

如果您想要自动垃圾收集,C++ 有很好的商业和公共领域的垃圾收集器。对于适合垃圾回收的应用,C++是一种优秀的垃圾回收语言,其性能与其他垃圾回收语言相比毫不逊色。有关C++中自动垃圾收集的讨论,请参阅C++ 编程语言(第 4 版)。另见汉斯-J。Boehm 的C 和 C++ 垃圾收集站点( archive)。

此外,C++ 支持编程技术,允许在没有垃圾收集器的情况下安全和隐式地进行内存管理。我认为垃圾收集是最后的选择,也是一种不完美的资源管理处理方式。这并不意味着它永远没有用,只是在许多情况下有更好的方法。

Source: http://www.stroustrup.com/bs_faq.html#garbage-collection

来源:http: //www.stroustrup.com/bs_faq.html#garbage-collection

As for why it doesnt have it built in, If I remember correctly it was invented before GC was the thing, and I don't believe the language could have had GC for several reasons(I.E Backwards compatibilty with C)

至于为什么它没有内置,如果我没记错的话,它是在 GC 出现之前发明,而且我不相信由于多种原因,该语言可能具有 GC(IE 向后兼容 C)

Hope this helps.

希望这可以帮助。

回答by Aaron McDaid

Stroustrup made some good comments on this at the 2013 Going Native conference.

Stroustrup 在 2013 年 Going Native 会议上对此发表了一些很好的评论。

Just skip to about 25m50s in this video. (I'd recommend watching the whole video actually, but this skips to the stuff about garbage collection.)

这个视频中跳到大约 25 分 50 秒。(我建议实际上观看整个视频,但这会跳到有关垃圾收集的内容。)

When you have a really great language that makes it easy (and safe, and predictable, and easy-to-read, and easy-to-teach) to deal with objects and values in a direct way, avoiding (explicit) use of the heap, then you don't even wantgarbage collection.

当您拥有一种非常棒的语言,可以轻松(且安全、可预测、易于阅读且易于教授)以直接方式处理对象和值,避免(显式)使用堆,那么你甚至不需要垃圾收集。

With modern C++, and the stuff we have in C++11, garbage collection is no longer desirable except in limited circumstances. In fact, even if a good garbage collector is built into one of the major C++ compilers, I think that it won't be used very often. It will be easier, not harder, to avoid the GC.

使用现代 C++ 以及我们在 C++11 中拥有的东西,除非在有限的情况下,垃圾收集不再是可取的。事实上,即使在主要的 C++ 编译器之一中内置了一个好的垃圾收集器,我认为它也不会经常使用。避免 GC会更容易,而不是更难。

He shows this example:

他展示了这个例子:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

This is unsafe in C++. But it's also unsafe in Java! In C++, if the function returns early, the deletewill never be called. But if you had full garbage collection, such as in Java, you merely get a suggestion that the object will be destructed "at some point in the future" (Update:it's even worse that this. Java does notpromise to call the finalizer ever - it maybe never be called). This isn't good enough if Gadget holds an open file handle, or a connection to a database, or data which you have buffered for write to a database at a later point. We want the Gadget to be destroyed as soon as it's finished, in order to free these resources as soon as possible. You don't want your database server struggling with thousands of database connections that are no longer needed - it doesn't know that your program is finished working.

这在 C++ 中是不安全的。但它在 Java 中也是不安全的!在 C++ 中,如果函数提前返回,delete将永远不会被调用。但是如果你有完整的垃圾收集,比如在 Java 中,你只会得到一个建议,即该对象将在“将来的某个时候”被破坏(更新:更糟糕的是,Java没有承诺永远调用终结器——它可能永远不会被调用)。如果 Gadget 持有一个打开的文件句柄,或者一个数据库连接,或者你为了稍后写入数据库而缓冲的数据,这还不够好。我们希望小工具在完成​​后立即销毁,以便尽快释放这些资源。您不希望您的数据库服务器为数以千计不再需要的数据库连接而苦苦挣扎 - 它不知道您的程序已完成工作。

So what's the solution? There are a few approaches. The obvious approach, which you'll use for the vast majority of your objects is:

那么有什么解决办法呢?有几种方法。您将用于绝大多数对象的显而易见的方法是:

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

This takes fewer characters to type. It doesn't have newgetting in the way. It doesn't require you to type Gadgettwice. The object is destroyed at the end of the function. If this is what you want, this is very intuitive. Gadgets behave the same as intor double. Predictable, easy-to-read, easy-to-teach. Everything is a 'value'. Sometimes a big value, but values are easier to teach because you don't have this 'action at a distance' thing that you get with pointers (or references).

这需要更少的字符来输入。它没有new妨碍。它不需要您输入Gadget两次。对象在函数结束时被销毁。如果这是您想要的,这是非常直观的。 Gadgets 的行为与int或相同double。可预测、易读、易教。一切都是“价值”。有时一个很大的价值,但价值更容易教授,因为你没有这种“远距离行动”的东西,你可以通过指针(或引用)获得。

Most of the objects you make are for use only in the function that created them, and perhaps passed as inputs to child functions. The programmer shouldn't have to think about 'memory management' when returning objects, or otherwise sharing objects across widely separated parts of the software.

您创建的大多数对象仅在创建它们的函数中使用,并且可能作为输入传递给子函数。程序员在返回对象时不应该考虑“内存管理”,或者在软件的广泛分离的部分之间共享对象。

Scope and lifetime are important. Most of the time, it's easier if the lifetime is the same as the scope. It's easier to understand and easier to teach. When you want a different lifetime, it should be obvious reading the code that you're doing this, by use of shared_ptrfor example. (Or returning (large) objects by value, leveraging move-semantics or unique_ptr.

范围和生命周期很重要。大多数情况下,如果生命周期与范围相同,则更容易。更容易理解,也更容易教。当你想要一个不同的生命周期时,阅读你正在做的代码应该很明显shared_ptr,例如使用。(或按值返回(大)对象,利用移动语义或unique_ptr.

This might seem like an efficiency problem. What if I want to return a Gadget from foo()? C++11's move semantics make it easier to return big objects. Just write Gadget foo() { ... }and it will just work, and work quickly. You don't need to mess with &&yourself, just return things by value and the language will often be able to do the necessary optimizations. (Even before C++03, compilers did a remarkably good job at avoiding unnecessary copying.)

这似乎是一个效率问题。如果我想从 返回小工具foo()怎么办?C++11 的移动语义使得返回大对象更容易。只需编写Gadget foo() { ... },它就会起作用,并且会很快起作用。你不需要惹恼&&自己,只需按值返回事物,语言通常能够进行必要的优化。(即使在 C++03 之前,编译器在避免不必要的复制方面做得非常好。)

As Stroustrup said elsewhere in the video (paraphrasing): "Only a computer scientist would insist on copying an object, and then destroying the original. (audience laughs). Why not just move the object directly to the new location? This is what humans (not computer scientists) expect."

正如 Stroustrup 在视频的其他地方所说(释义):“只有计算机科学家才会坚持复制一个对象,然后销毁原始对象。(观众笑)。为什么不直接将对象移动到新位置?这就是人类(不是计算机科学家)期待。”

When you can guarantee only one copy of an object is needed, it's much easier to understand the lifetime of the object. You can pick what lifetime policy you want, and garbage collection is there if you want. But when you understand the benefits of the other approaches, you'll find that garbage collection is at the bottom of your list of preferences.

当您可以保证只需要一个对象的一个​​副本时,就更容易理解对象的生命周期。你可以选择你想要的生命周期策略,如果你愿意,垃圾收集就在那里。但是当您了解其他方法的好处时,您会发现垃圾收集位于您的首选项列表的底部。

If that doesn't work for you, you can use unique_ptr, or failing that, shared_ptr. Well written C++11 is shorter, easier-to-read, and easier-to-teach than many other languages when it comes to memory management.

如果这对您不起作用,您可以使用unique_ptr,否则,shared_ptr. 在内存管理方面,编写良好的 C++11 比许多其他语言更短、更易于阅读且更易于教授。

回答by einpoklum

Because modern C++ doesn't need garbage collection.

因为现代 C++ 不需要垃圾收集。

Bjarne Stroustrup's FAQ answer on this matter says:

Bjarne Stroustrup对此事的常见问题解答说

I don't like garbage. I don't like littering. My ideal is to eliminate the need for a garbage collector by not producing any garbage. That is now possible.

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



The situation, for code written these days (C++17 and following the official Core Guidelines) is as follows:

这些天编写的代码(C++17 并遵循官方 核心指南)的情况如下:

  • Most memory ownership-related code is in libraries (especially those providing containers).
  • Most useof code involving memory ownership follows the 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'll be doing that with a span; no raw pointer needed.
  • 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 wayof 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.
  • 大多数与内存所有权相关的代码都在库中(尤其是那些提供容器的)。
  • 大多数涉及内存所有权的代码使用遵循 RAII模式,因此分配是在构造时进行,在销毁时进行释放,这发生在退出分配内容的范围时。
  • 没有直接显式分配或释放内存
  • 原始指针不拥有内存(如果您遵循了指导方针),因此您不能通过传递它们来泄漏。
  • 如果您想知道如何在内存中传递值序列的起始地址 - 您将使用span;不需要原始指针。
  • 如果你真的需要一个拥有的“指针”,你可以使用 C++ 的 标准库智能指针——它们不会泄漏,而且效率很高(尽管ABI 会妨碍它)。或者,您可以使用“所有者指针”跨越范围边界传递所有权。这些是不常见的,必须明确使用;但是当采用时 - 它们允许对泄漏进行很好的静态检查。

"Oh yeah? But what about...

“哦,是吗?但是……

... if I just write code the way we used to write C++ in the old days?"

...如果我只是像过去那样写 C++ 那样写代码?”

Indeed, you couldjust disregard all of the guidelines and write leaky application code - and it will compile and run (and leak), same as always.

实际上,您可以无视所有准则并编写泄漏的应用程序代码 - 它会像往常一样编译和运行(和泄漏)。

But it's not a "just don't do that" situation, where the developer is expected to be virtuous and exercise a lot of self control; it's just not simpler to write non-conforming code, nor is it faster to write, nor is it better-performing. Gradually it will also become more difficult to write, as you would face an increasing "impedance mismatch" with what conforming code provides and expects.

但这不是“只是不要那样做”的情况,在这种情况下,开发人员应该是有道德的并进行大量自我控制;编写不符合规范的代码并不简单,编写速度也不快,性能也不好。逐渐地,它也将变得更加难以编写,因为您将面临与符合代码提供和期望的内容日益增加的“阻抗不匹配”。

... if I reintrepret_cast? Or do complex pointer arithmetic? Or other such hacks?"

……如果我reintrepret_cast?或者做复杂的指针算术?或者其他类似的黑客?”

Indeed, if you put your mind to it, you can write code that messes things up despite playing nice with the guidelines. But:

确实,如果您下定决心,您可以编写代码,尽管对指南很友好。但:

  1. You would do this rarely (in terms of places in the code, not necessarily in terms of fraction of execution time)
  2. You would only do this intentionally, not accidentally.
  3. Doing so will stand out in a codebase conforming to the guidelines.
  4. It's the kind of code in which you would bypass the GC in another language anyway.
  1. 您很少会这样做(就代码中的位置而言,不一定就执行时间的部分而言)
  2. 你只会故意这样做,而不是意外。
  3. 这样做将在符合准则的代码库中脱颖而出。
  4. 在这种代码中,您无论如何都会绕过另一种语言的 GC。

... library development?"

……图书馆发展?”

If you're a C++ library developer then you do write unsafe code involving raw pointers, and you are required to code carefully and responsibly - but these are self-contained pieces of code written by experts (and more importantly, reviewed by experts).

如果您是 C++ 库开发人员,那么您确实会编写涉及原始指针的不安全代码,并且您需要仔细负责地编写代码——但这些是由专家编写的独立代码段(更重要的是,由专家)。


So, it's just like Bjarne said: There's really no motivation to collect garbage generally, as you all but make sure not to produce garbage. GC is becoming a non-problem with C++.


所以,就像 Bjarne 说的:一般来说,真的没有动力去收集垃圾,因为你们都要确保不要产生垃圾。GC 正在成为 C++ 的一个非问题。

That is not to say GC isn't an interesting problem for certain specific applications, when you want to employ custom allocation and de-allocations strategies. For those you would want custom allocation and de-allocation, not a language-level GC.

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

回答by Uri

The idea behind C++ was that you would not pay any performance impact for features that you don't use. So adding garbage collection would have meant having some programs run straight on the hardware the way C does and some within some sort of runtime virtual machine.

C++ 背后的想法是,您不会为不使用的功能支付任何性能影响。因此,添加垃圾收集意味着让一些程序像 C 一样直接在硬件上运行,而另一些则在某种运行时虚拟机中运行。

Nothing prevents you from using some form of smart pointers that are bound to some third-party garbage collection mechanism. I seem to recall Microsoft doing something like that with COM and it didn't go to well.

没有什么可以阻止您使用某种形式的绑定到某些第三方垃圾收集机制的智能指针。我似乎记得微软对 COM 做了类似的事情,但进展并不顺利。

回答by Nemanja Trifunovic

To answer most "why" questions about C++, read Design and Evolution of C++

要回答有关 C++ 的大多数“为什么”问题,请阅读 C++ 的设计和演化