当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:

不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。

我不太确定他这么说是什么意思。

为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?


当前回答

许多答案都涉及到各种性能考虑因素。我想解决让OP困惑的评论:

不要像Java程序员那样思考。

事实上,在Java中,正如这个问题的答案所解释的那样,

第一次显式创建对象时使用new关键字。

但在c++中,类型为T的对象是这样创建的:T{}(或T{ctor_argument1,ctor_arg2}对于带参数的构造函数)。这就是为什么通常你没有理由想要使用new。

那么,为什么要用它呢?有两个原因:

您需要创建许多值,这些值的数量在编译时是未知的。 由于c++实现在普通机器上的限制-通过分配太多空间来防止堆栈溢出,以常规方式创建值。

现在,除了你引用的评论暗示的内容之外,你应该注意到,即使是上面的两种情况也已经涵盖得很好了,而不必“求助”使用新的自己:

您可以使用来自标准库的容器类型,它们可以保存运行时可变数量的元素(如std::vector)。 您可以使用智能指针,它为您提供一个类似于new的指针,但确保在“指针”超出作用域的地方释放内存。

因此,在c++社区编码指南中,避免显式的new和delete是一个正式的条款:指南R.11。

其他回答

Pre-C + + 17:

因为即使您将结果包装在智能指针中,它也容易发生细微的泄漏。

考虑一个“小心”的用户,他记得在智能指针中包装对象:

foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));

这段代码很危险,因为不能保证在T1或T2之前构造shared_ptr。因此,如果新T1()或新T2()中的一个在另一个成功后失败,那么第一个对象将被泄露,因为不存在shared_ptr来销毁和释放它。

解决方法:使用make_shared。

Post-C + + 17:

这不再是一个问题:c++ 17对这些操作的顺序施加了约束,在这种情况下,确保每次调用new()必须立即构造相应的智能指针,中间没有其他操作。这意味着,在调用第二个new()时,可以保证第一个对象已经被包装在其智能指针中,从而防止在抛出异常时发生任何泄漏。

Barry在另一个回答中提供了关于c++ 17引入的新求值顺序的更详细的解释。

感谢@Remy Lebeau指出这在c++ 17中仍然是一个问题(尽管不是那么严重):shared_ptr构造函数可能无法分配它的控制块和抛出,在这种情况下,传递给它的指针不会被删除。

解决方法:使用make_shared。

避免过度使用堆的一个值得注意的原因是为了性能——特别是涉及c++使用的默认内存管理机制的性能。虽然在简单的情况下分配可以非常快,但是在没有严格顺序的情况下对大小不一致的对象执行大量的新建和删除操作不仅会导致内存碎片,而且还会使分配算法复杂化,并且在某些情况下绝对会破坏性能。

这就是创建内存池要解决的问题,可以减轻传统堆实现的固有缺点,同时仍然允许您在必要时使用堆。

不过,最好还是完全避免这个问题。如果可以将它放到堆栈中,那么就这样做。

New在堆上分配对象。否则,对象分配在堆栈上。查一下两者的区别。

New()不应该尽可能少地使用。应该尽可能小心地使用它。而且,它应该在实用主义的要求下,在必要时经常使用。

对象在堆栈上的分配依赖于它们的隐式销毁,这是一个简单的模型。如果对象所需的作用域符合该模型,则不需要使用new(),以及相关的delete()和NULL指针检查。 在你有很多短期对象的情况下,在堆栈上分配应该可以减少堆碎片的问题。

但是,如果对象的生命周期需要扩展到当前范围之外,那么new()是正确的答案。只要确保您注意何时以及如何调用delete()以及NULL指针的可能性,使用已删除对象以及使用指针所带来的所有其他问题。

C++ doesn't employ any memory manager by its own. Other languages like C# and Java have a garbage collector to handle the memory C++ implementations typically use operating system routines to allocate the memory and too much new/delete could fragment the available memory With any application, if the memory is frequently being used it's advisable to preallocate it and release when not required. Improper memory management could lead memory leaks and it's really hard to track. So using stack objects within the scope of function is a proven technique The downside of using stack objects are, it creates multiple copies of objects on returning, passing to functions, etc. However, smart compilers are well aware of these situations and they've been optimized well for performance It's really tedious in C++ if the memory being allocated and released in two different places. The responsibility for release is always a question and mostly we rely on some commonly accessible pointers, stack objects (maximum possible) and techniques like auto_ptr (RAII objects) The best thing is that, you've control over the memory and the worst thing is that you will not have any control over the memory if we employ an improper memory management for the application. The crashes caused due to memory corruptions are the nastiest and hard to trace.