当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当前回答
New()不应该尽可能少地使用。应该尽可能小心地使用它。而且,它应该在实用主义的要求下,在必要时经常使用。
对象在堆栈上的分配依赖于它们的隐式销毁,这是一个简单的模型。如果对象所需的作用域符合该模型,则不需要使用new(),以及相关的delete()和NULL指针检查。 在你有很多短期对象的情况下,在堆栈上分配应该可以减少堆碎片的问题。
但是,如果对象的生命周期需要扩展到当前范围之外,那么new()是正确的答案。只要确保您注意何时以及如何调用delete()以及NULL指针的可能性,使用已删除对象以及使用指针所带来的所有其他问题。
其他回答
核心原因是堆上的对象总是比简单值更难使用和管理。编写易于阅读和维护的代码始终是任何认真的程序员的首要任务。
另一种情况是,我们使用的库提供了值语义,使动态分配变得不必要。Std::string就是一个很好的例子。
然而,对于面向对象的代码,使用指针——这意味着使用new来预先创建指针——是必须的。为了简化资源管理的复杂性,我们有几十种工具使其尽可能简单,比如智能指针。基于对象的范式或泛型范式假设值语义,并需要更少或不需要新的,正如其他地方的海报所述。
传统的设计模式,尤其是GoF书中提到的那些,大量使用new,因为它们是典型的面向对象代码。
许多答案都涉及到各种性能考虑因素。我想解决让OP困惑的评论:
不要像Java程序员那样思考。
事实上,在Java中,正如这个问题的答案所解释的那样,
第一次显式创建对象时使用new关键字。
但在c++中,类型为T的对象是这样创建的:T{}(或T{ctor_argument1,ctor_arg2}对于带参数的构造函数)。这就是为什么通常你没有理由想要使用new。
那么,为什么要用它呢?有两个原因:
您需要创建许多值,这些值的数量在编译时是未知的。 由于c++实现在普通机器上的限制-通过分配太多空间来防止堆栈溢出,以常规方式创建值。
现在,除了你引用的评论暗示的内容之外,你应该注意到,即使是上面的两种情况也已经涵盖得很好了,而不必“求助”使用新的自己:
您可以使用来自标准库的容器类型,它们可以保存运行时可变数量的元素(如std::vector)。 您可以使用智能指针,它为您提供一个类似于new的指针,但确保在“指针”超出作用域的地方释放内存。
因此,在c++社区编码指南中,避免显式的new和delete是一个正式的条款:指南R.11。
由new创建的对象必须最终删除,以免泄漏。析构函数不会被调用,内存不会被释放,整个比特。由于c++没有垃圾收集,这是一个问题。
由值创建的对象(即在堆栈上)在超出作用域时自动死亡。析构函数调用由编译器插入,并且在函数返回时自动释放内存。
像unique_ptr、shared_ptr这样的智能指针解决了悬空引用问题,但它们需要编码规则,并有其他潜在的问题(可复制性、引用循环等)。
此外,在大量多线程的场景中,new是线程之间的争用点;过度使用new可能会影响性能。堆栈对象的创建根据定义是线程本地的,因为每个线程都有自己的堆栈。
值对象的缺点是,一旦宿主函数返回,它们就会死亡——你不能将它们的引用传递给调用者,只能通过复制、返回或按值移动。
在很大程度上,这是某人将自己的弱点提升为普遍准则。使用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 ; }
在课堂上也是如此;如果没有它,即使根本没有在堆上分配内存,也会从堆中泄漏内存。
两个原因:
在这种情况下没有必要。您正在使代码不必要地变得更加复杂。 它在堆上分配空间,这意味着您必须记住稍后删除它,否则将导致内存泄漏。