【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
我在使用std :: list <std :: string>时偶然发现了std :: string的内存溢出问题内存泄漏 , 其中一条评论说:
停止使用
new
这么多。 我看不到您在任何地方使用新产品的任何原因。 您可以在C ++中按值创建对象,这是使用该语言的巨大优势之一。 您不必在堆上分配所有内容。 不要像Java程序员那样思考。
我不确定他的意思。 为什么要在C ++中尽可能频繁地通过值创建对象,它在内部有什么不同? 我是否误解了答案?
#1楼
C ++ 17之前的版本:
因为即使将结果包装在智能指针中,它也容易发生细微的泄漏。
考虑一个“谨慎”的用户,他记得将对象包装在智能指针中:
foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));
该代码很危险,因为不能保证 在 T1
或T2
之前构造了shared_ptr
。 因此,如果new T1()
或new T2()
的一个在另一个成功之后失败,则第一个对象将被泄漏,因为不存在用于破坏和重新分配它的shared_ptr
。
解决方案:使用make_shared
。
后C ++ 17:
这不再是问题:C ++ 17对这些操作的顺序施加了约束,在这种情况下,确保对
new()
每次调用都必须立即跟随着相应智能指针的构造,而在其中没有其他操作之间。
这意味着,在调用第二个
new()
,可以确保第一个对象已经包装在其智能指针中,从而防止在引发异常的情况下发生任何泄漏。
Barry
在另一个答案中提供了对C ++ 17引入的新评估顺序的更详细说明。
感谢@Remy Lebeau指出,这在C ++ 17下仍然是一个问题(尽管更少): shared_ptr
构造函数可能无法分配其控制块并抛出,在这种情况下,传递给它的指针不会被删除。
解决方案:使用make_shared
。
#2楼
我发现错过了一些尽可能少的新事物的重要原因:
new
运算符的执行时间不确定
调用new
可能会或可能不会导致操作系统向您的进程分配新的物理页面,如果您经常这样做,可能会很慢。 或者它可能已经准备好合适的存储位置,我们不知道。 如果你的程序需要有一致的和可预测的执行时间(如在一个实时系统或游戏/物理模拟),你需要避免new
在你的时间临界循环。
运算符new
是一个隐式线程同步
是的,您听说过,您的操作系统需要确保页面表是一致的,因此调用new
将导致您的线程获取隐式互斥锁。 如果您一直从许多线程中调用new
,那么您实际上是在对线程进行序列化(我已经用32个CPU进行了此操作,每个CPU都按下new
以获得每个数百字节的数据,哎呀!这是调试的皇家皮塔饼)
诸如慢,碎片,易于出错等之类的其他问题已经在其他答案中提及。
#3楼
new
是新的goto
。
回想一下为什么goto
如此受到谴责:尽管goto
是功能强大的底层控制流的工具,但人们经常以不必要的复杂方式使用它,这使得代码难以遵循。 此外,最有用和最容易阅读的模式是在结构化的编程语句中编码的(例如for
或while
); 最终的结果是,使用goto
作为适当方法的代码很少见,如果您很想编写goto
,则可能做得不好(除非您真的知道自己在做什么)。
new
与此类似-它通常用于使事情变得不必要地复杂和难以阅读,并且可以编码的最有用的用法模式已编码为各种类。 此外,如果您需要使用尚没有标准类的任何新用法模式,则可以编写自己的对它们进行编码的类!
我什至认为new
是比goto
更糟糕的 ,因为需要将new
和delete
语句配对。
像goto
一样,如果您曾经认为需要使用new
,则可能做得不好-尤其是在类的实现之外,这样做的目的是封装所有您需要执行的动态分配。
#4楼
还有一个以上所有正确答案的要点,这取决于您正在执行哪种编程。 例如,在Windows中开发内核->堆栈受到严格限制,您可能无法像在用户模式下那样出现页面错误。
在这样的环境中,新的或类似C的API调用是首选的,甚至是必需的。
当然,这仅仅是规则的例外。
#5楼
使用new时,对象将分配给堆。 通常在预期扩展时使用。 当您声明诸如
Class var;
它放在堆栈上。
您将始终必须使用new对放置在堆上的对象调用destroy。 这为内存泄漏打开了可能。 放置在堆栈上的对象不容易发生内存泄漏!
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3151305