当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当前回答
因为堆栈速度更快,而且防漏
在c++中,只需要一条指令就可以为给定函数中的每个局部作用域对象在堆栈上分配空间,而且不可能泄漏任何内存。该评论意图(或应该意图)说“使用堆栈而不是堆”之类的话。
其他回答
New在堆上分配对象。否则,对象分配在堆栈上。查一下两者的区别。
我发现有几个重要的原因让我们忽略了尽量少做新事情:
Operator new的执行时间不确定
调用new可能会导致操作系统为进程分配新的物理页,也可能不会。如果你经常这样做,会很慢。或者它可能已经准备好了一个合适的内存位置;我们不知道。如果你的程序需要具有一致且可预测的执行时间(如在实时系统或游戏/物理模拟中),你需要避免在时间关键型循环中添加新的元素。
操作符new是一个隐式线程同步
是的,你听到了。你的操作系统需要确保你的页表是一致的,因此调用new会导致你的线程获得一个隐式互斥锁。如果你一直从许多线程调用new,你实际上是在序列化你的线程(我用32个cpu做过这个,每个cpu都调用new来获得几百个字节,哎呦!那是一个需要调试的皇家p.i.t.a.。)
其余的,比如速度慢、碎片化、容易出错等,其他答案已经提到了。
在很大程度上,这是某人将自己的弱点提升为普遍准则。使用new操作符创建对象本身没有任何问题。有一些争论是,你必须遵循一些规则:如果你创建了一个对象,你需要确保它会被销毁。
最简单的方法是在自动存储中创建对象,这样c++就知道在它超出作用域时销毁它:
{
File foo = File("foo.dat");
// Do things
}
现在,观察一下,当你在结束大括号之后离开那个块时,foo超出了作用域。c++会自动调用它的析构函数。与Java不同,您不需要等待垃圾回收来找到它。
你写过
{
File * foo = new File("foo.dat");
你需要显式地匹配它
delete foo;
}
或者更好的是,将你的File *分配为“智能指针”。如果你不小心,它可能会导致泄漏。
答案本身做了一个错误的假设,如果你不使用new,你就不会在堆上分配;事实上,在c++中你不知道这一点。最多,您知道少量的内存,比如一个指针,肯定分配在堆栈上。但是,考虑一下File的实现是否像这样:
class File {
private:
FileImpl * fd;
public:
File(String fn){ fd = new FileImpl(fn);}
然后FileImpl仍然会被分配到堆栈上。
是的,你最好确保你有
~File(){ delete fd ; }
在课堂上也是如此;如果没有它,即使根本没有在堆上分配内存,也会从堆中泄漏内存。
由new创建的对象必须最终删除,以免泄漏。析构函数不会被调用,内存不会被释放,整个比特。由于c++没有垃圾收集,这是一个问题。
由值创建的对象(即在堆栈上)在超出作用域时自动死亡。析构函数调用由编译器插入,并且在函数返回时自动释放内存。
像unique_ptr、shared_ptr这样的智能指针解决了悬空引用问题,但它们需要编码规则,并有其他潜在的问题(可复制性、引用循环等)。
此外,在大量多线程的场景中,new是线程之间的争用点;过度使用new可能会影响性能。堆栈对象的创建根据定义是线程本地的,因为每个线程都有自己的堆栈。
值对象的缺点是,一旦宿主函数返回,它们就会死亡——你不能将它们的引用传递给调用者,只能通过复制、返回或按值移动。
两个原因:
在这种情况下没有必要。您正在使代码不必要地变得更加复杂。 它在堆上分配空间,这意味着您必须记住稍后删除它,否则将导致内存泄漏。