当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:
不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。
我不太确定他这么说是什么意思。
为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?
当前回答
以上所有正确答案还有一点,这取决于你在做什么类型的编程。在Windows中开发内核,例如->堆栈受到严重限制,您可能无法像在用户模式中那样处理页面错误。
在这样的环境中,新的或类似c的API调用是首选的,甚至是必需的。
当然,这只是规则的一个例外。
其他回答
两个原因:
在这种情况下没有必要。您正在使代码不必要地变得更加复杂。 它在堆上分配空间,这意味着您必须记住稍后删除它,否则将导致内存泄漏。
避免过度使用堆的一个值得注意的原因是为了性能——特别是涉及c++使用的默认内存管理机制的性能。虽然在简单的情况下分配可以非常快,但是在没有严格顺序的情况下对大小不一致的对象执行大量的新建和删除操作不仅会导致内存碎片,而且还会使分配算法复杂化,并且在某些情况下绝对会破坏性能。
这就是创建内存池要解决的问题,可以减轻传统堆实现的固有缺点,同时仍然允许您在必要时使用堆。
不过,最好还是完全避免这个问题。如果可以将它放到堆栈中,那么就这样做。
核心原因是堆上的对象总是比简单值更难使用和管理。编写易于阅读和维护的代码始终是任何认真的程序员的首要任务。
另一种情况是,我们使用的库提供了值语义,使动态分配变得不必要。Std::string就是一个很好的例子。
然而,对于面向对象的代码,使用指针——这意味着使用new来预先创建指针——是必须的。为了简化资源管理的复杂性,我们有几十种工具使其尽可能简单,比如智能指针。基于对象的范式或泛型范式假设值语义,并需要更少或不需要新的,正如其他地方的海报所述。
传统的设计模式,尤其是GoF书中提到的那些,大量使用new,因为它们是典型的面向对象代码。
新就是新目标。
Recall why goto is so reviled: while it is a powerful, low-level tool for flow control, people often used it in unnecessarily complicated ways that made code difficult to follow. Furthermore, the most useful and easiest to read patterns were encoded in structured programming statements (e.g. for or while); the ultimate effect is that the code where goto is the appropriate way to is rather rare, if you are tempted to write goto, you're probably doing things badly (unless you really know what you're doing).
New也类似——它经常被用来使事情变得不必要的复杂和难以阅读,最有用的使用模式可以被编码到各种各样的类中。此外,如果您需要使用任何新的使用模式,而这些模式还没有标准类,您可以编写自己的类来编码它们!
我甚至认为new比goto更糟糕,因为需要对new和delete语句进行配对。
像goto一样,如果您认为需要使用new,那么您可能做得很糟糕——特别是如果您在一个类的实现之外这样做,这个类的目的是封装您需要做的任何动态分配。
Pre-C + + 17:
因为即使您将结果包装在智能指针中,它也容易发生细微的泄漏。
考虑一个“小心”的用户,他记得在智能指针中包装对象:
foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));
这段代码很危险,因为不能保证在T1或T2之前构造shared_ptr。因此,如果新T1()或新T2()中的一个在另一个成功后失败,那么第一个对象将被泄露,因为不存在shared_ptr来销毁和释放它。
解决方法:使用make_shared。
Post-C + + 17:
这不再是一个问题:c++ 17对这些操作的顺序施加了约束,在这种情况下,确保每次调用new()必须立即构造相应的智能指针,中间没有其他操作。这意味着,在调用第二个new()时,可以保证第一个对象已经被包装在其智能指针中,从而防止在抛出异常时发生任何泄漏。
Barry在另一个回答中提供了关于c++ 17引入的新求值顺序的更详细的解释。
感谢@Remy Lebeau指出这在c++ 17中仍然是一个问题(尽管不是那么严重):shared_ptr构造函数可能无法分配它的控制块和抛出,在这种情况下,传递给它的指针不会被删除。
解决方法:使用make_shared。