当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当前回答
核心原因是堆上的对象总是比简单值更难使用和管理。编写易于阅读和维护的代码始终是任何认真的程序员的首要任务。
另一种情况是,我们使用的库提供了值语义,使动态分配变得不必要。Std::string就是一个很好的例子。
然而,对于面向对象的代码,使用指针——这意味着使用new来预先创建指针——是必须的。为了简化资源管理的复杂性,我们有几十种工具使其尽可能简单,比如智能指针。基于对象的范式或泛型范式假设值语义,并需要更少或不需要新的,正如其他地方的海报所述。
传统的设计模式,尤其是GoF书中提到的那些,大量使用new,因为它们是典型的面向对象代码。
其他回答
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.
我想海报的意思是,你不必把所有的东西都分配到堆上,而不是堆栈上。
基本上,对象是在堆栈上分配的(当然,如果对象大小允许的话),因为堆栈分配的成本较低,而不是基于堆的分配,后者涉及分配器的大量工作,并增加了冗长的内容,因为这样您就必须管理分配在堆上的数据。
我发现有几个重要的原因让我们忽略了尽量少做新事情:
Operator new的执行时间不确定
调用new可能会导致操作系统为进程分配新的物理页,也可能不会。如果你经常这样做,会很慢。或者它可能已经准备好了一个合适的内存位置;我们不知道。如果你的程序需要具有一致且可预测的执行时间(如在实时系统或游戏/物理模拟中),你需要避免在时间关键型循环中添加新的元素。
操作符new是一个隐式线程同步
是的,你听到了。你的操作系统需要确保你的页表是一致的,因此调用new会导致你的线程获得一个隐式互斥锁。如果你一直从许多线程调用new,你实际上是在序列化你的线程(我用32个cpu做过这个,每个cpu都调用new来获得几百个字节,哎呦!那是一个需要调试的皇家p.i.t.a.。)
其余的,比如速度慢、碎片化、容易出错等,其他答案已经提到了。
由new创建的对象必须最终删除,以免泄漏。析构函数不会被调用,内存不会被释放,整个比特。由于c++没有垃圾收集,这是一个问题。
由值创建的对象(即在堆栈上)在超出作用域时自动死亡。析构函数调用由编译器插入,并且在函数返回时自动释放内存。
像unique_ptr、shared_ptr这样的智能指针解决了悬空引用问题,但它们需要编码规则,并有其他潜在的问题(可复制性、引用循环等)。
此外,在大量多线程的场景中,new是线程之间的争用点;过度使用new可能会影响性能。堆栈对象的创建根据定义是线程本地的,因为每个线程都有自己的堆栈。
值对象的缺点是,一旦宿主函数返回,它们就会死亡——你不能将它们的引用传递给调用者,只能通过复制、返回或按值移动。
New()不应该尽可能少地使用。应该尽可能小心地使用它。而且,它应该在实用主义的要求下,在必要时经常使用。
对象在堆栈上的分配依赖于它们的隐式销毁,这是一个简单的模型。如果对象所需的作用域符合该模型,则不需要使用new(),以及相关的delete()和NULL指针检查。 在你有很多短期对象的情况下,在堆栈上分配应该可以减少堆碎片的问题。
但是,如果对象的生命周期需要扩展到当前范围之外,那么new()是正确的答案。只要确保您注意何时以及如何调用delete()以及NULL指针的可能性,使用已删除对象以及使用指针所带来的所有其他问题。