当使用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 */ }
明白了吗?
其他回答
因为堆栈速度更快,而且防漏
在c++中,只需要一条指令就可以为给定函数中的每个局部作用域对象在堆栈上分配空间,而且不可能泄漏任何内存。该评论意图(或应该意图)说“使用堆栈而不是堆”之类的话。
使用new时,对象被分配到堆中。它通常用于预期扩展时。当你声明一个对象,比如,
Class var;
它被放置在堆栈上。
你总是需要对你用new放在堆上的对象调用destroy。这就有可能导致内存泄漏。放在堆栈上的对象不容易发生内存泄漏!
核心原因是堆上的对象总是比简单值更难使用和管理。编写易于阅读和维护的代码始终是任何认真的程序员的首要任务。
另一种情况是,我们使用的库提供了值语义,使动态分配变得不必要。Std::string就是一个很好的例子。
然而,对于面向对象的代码,使用指针——这意味着使用new来预先创建指针——是必须的。为了简化资源管理的复杂性,我们有几十种工具使其尽可能简单,比如智能指针。基于对象的范式或泛型范式假设值语义,并需要更少或不需要新的,正如其他地方的海报所述。
传统的设计模式,尤其是GoF书中提到的那些,大量使用new,因为它们是典型的面向对象代码。
由new创建的对象必须最终删除,以免泄漏。析构函数不会被调用,内存不会被释放,整个比特。由于c++没有垃圾收集,这是一个问题。
由值创建的对象(即在堆栈上)在超出作用域时自动死亡。析构函数调用由编译器插入,并且在函数返回时自动释放内存。
像unique_ptr、shared_ptr这样的智能指针解决了悬空引用问题,但它们需要编码规则,并有其他潜在的问题(可复制性、引用循环等)。
此外,在大量多线程的场景中,new是线程之间的争用点;过度使用new可能会影响性能。堆栈对象的创建根据定义是线程本地的,因为每个线程都有自己的堆栈。
值对象的缺点是,一旦宿主函数返回,它们就会死亡——你不能将它们的引用传递给调用者,只能通过复制、返回或按值移动。
我想海报的意思是,你不必把所有的东西都分配到堆上,而不是堆栈上。
基本上,对象是在堆栈上分配的(当然,如果对象大小允许的话),因为堆栈分配的成本较低,而不是基于堆的分配,后者涉及分配器的大量工作,并增加了冗长的内容,因为这样您就必须管理分配在堆上的数据。