当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当前回答
我想海报的意思是,你不必把所有的东西都分配到堆上,而不是堆栈上。
基本上,对象是在堆栈上分配的(当然,如果对象大小允许的话),因为堆栈分配的成本较低,而不是基于堆的分配,后者涉及分配器的大量工作,并增加了冗长的内容,因为这样您就必须管理分配在堆上的数据。
其他回答
两个原因:
在这种情况下没有必要。您正在使代码不必要地变得更加复杂。 它在堆上分配空间,这意味着您必须记住稍后删除它,否则将导致内存泄漏。
原因很复杂。
首先,c++不进行垃圾收集。因此,对于每一个new,必须有一个对应的delete。如果您没有将此删除,那么您就有内存泄漏。现在,对于这样一个简单的例子:
std::string *someString = new std::string(...);
//Do stuff
delete someString;
这很简单。但是如果“Do stuff”抛出异常会发生什么?哎呀:内存泄漏。如果“做事情”问题提前回归会发生什么?哎呀:内存泄漏。
这是最简单的情况。如果你碰巧将这个字符串返回给某人,现在他们必须删除它。如果他们把它作为参数传递,接收它的人需要删除它吗?什么时候删除?
或者,你可以这样做:
std::string someString(...);
//Do stuff
没有删除。对象是在“堆栈”上创建的,一旦超出作用域就会被销毁。您甚至可以返回对象,从而将其内容传递给调用函数。你可以将对象传递给函数(通常作为引用或const-reference: void SomeFunc(std::string &iCanModifyThis, const std::string &iCantModifyThis)。等等。
全部不需要new和delete。不存在谁拥有内存或谁负责删除它的问题。如果你有:
std::string someString(...);
std::string otherString;
otherString = someString;
可以理解为otherString拥有someString数据的副本。它不是指针;它是一个单独的对象。它们可能碰巧具有相同的内容,但你可以在不影响另一个的情况下更改其中一个:
someString += "More text.";
if(otherString == someString) { /*Will never get here */ }
明白了吗?
New在堆上分配对象。否则,对象分配在堆栈上。查一下两者的区别。
我发现有几个重要的原因让我们忽略了尽量少做新事情:
Operator new的执行时间不确定
调用new可能会导致操作系统为进程分配新的物理页,也可能不会。如果你经常这样做,会很慢。或者它可能已经准备好了一个合适的内存位置;我们不知道。如果你的程序需要具有一致且可预测的执行时间(如在实时系统或游戏/物理模拟中),你需要避免在时间关键型循环中添加新的元素。
操作符new是一个隐式线程同步
是的,你听到了。你的操作系统需要确保你的页表是一致的,因此调用new会导致你的线程获得一个隐式互斥锁。如果你一直从许多线程调用new,你实际上是在序列化你的线程(我用32个cpu做过这个,每个cpu都调用new来获得几百个字节,哎呦!那是一个需要调试的皇家p.i.t.a.。)
其余的,比如速度慢、碎片化、容易出错等,其他答案已经提到了。
New()不应该尽可能少地使用。应该尽可能小心地使用它。而且,它应该在实用主义的要求下,在必要时经常使用。
对象在堆栈上的分配依赖于它们的隐式销毁,这是一个简单的模型。如果对象所需的作用域符合该模型,则不需要使用new(),以及相关的delete()和NULL指针检查。 在你有很多短期对象的情况下,在堆栈上分配应该可以减少堆碎片的问题。
但是,如果对象的生命周期需要扩展到当前范围之外,那么new()是正确的答案。只要确保您注意何时以及如何调用delete()以及NULL指针的可能性,使用已删除对象以及使用指针所带来的所有其他问题。