为何C ++程序员应尽可能减小对“新”的使用?

在使用std :: list <std :: string>时偶然发现了std :: string的内存溢出问题内存泄漏其中一条评论说: 程序员

中止使用new这么多。 我看不到您在任何地方使用新产品的任何缘由。 您能够在C ++中按值建立对象,这是使用该语言的巨大优点之一。 您没必要在堆上分配全部内容。 不要像Java程序员那样思考。 编程

我不肯定他的意思。 为何要在C ++中尽量频繁地经过值建立对象,它在内部有什么不一样? 我是否误解了答案? 多线程


#1楼

C ++ 17以前的版本:

由于即便将结果包装在智能指针中,它也容易发生细微的泄漏。

考虑一个“谨慎”的用户,他记得将对象包装在智能指针中: 函数

foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));

该代码很危险,由于不能保证 T1T2 以前构造了shared_ptr 。 所以,若是new T1()new T2()的一个在另外一个成功以后失败,则第一个对象将被泄漏,由于不存在用于破坏和从新分配它的shared_ptr工具

解决方案:使用make_shared编码

后C ++ 17:

这再也不是问题:C ++ 17对这些操做的顺序施加了约束,在这种状况下,确保对 new()每次调用都必须当即跟随着相应智能指针的构造,而在其中没有其余操做之间。 这意味着,在调用第二个 new() ,能够确保第一个对象已经包装在其智能指针中,从而防止在引起异常的状况下发生任何泄漏。 spa

\n

Barry 在另外一个答案中提供了对C ++ 17引入的新评估顺序的更详细说明。 操作系统

感谢@Remy Lebeau指出,这在C ++ 17下仍然是一个问题(尽管更少): shared_ptr构造函数可能没法分配其控制块并抛出,在这种状况下,传递给它的指针不会被删除。 .net

解决方案:使用make_shared线程


#2楼

我发现错过了一些尽量少的新事物的重要缘由:

new运算符的执行时间不肯定

调用new可能会或可能不会致使操做系统向您的进程分配新的物理页面,若是您常常这样作,可能会很慢。 或者它可能已经准备好合适的存储位置,咱们不知道。 若是你的程序须要有一致的和可预测的执行时间(如在一个实时系统或游戏/物理模拟),你须要避免new在你的时间临界循环。

运算符new是一个隐式线程同步

是的,您据说过,您的操做系统须要确保页面表是一致的,所以调用new将致使您的线程获取隐式互斥锁。 若是您一直从许多线程中调用new ,那么您其实是在对线程进行序列化(我已经用32个CPU进行了此操做,每一个CPU都按下new以得到每一个数百字节的数据,哎呀!这是调试的皇家皮塔饼)

诸如慢,碎片,易于出错等之类的其余问题已经在其余答案中说起。


#3楼

new是新的goto

回想一下为何goto如此受到谴责:尽管goto是功能强大的底层控制流的工具,但人们常常以没必要要的复杂方式使用它,这使得代码难以遵循。 此外,最有用和最容易阅读的模式是在结构化的编程语句中编码的(例如forwhile ); 最终的结果是,使用goto做为适当方法的代码不多见,若是您很想编写goto ,则可能作得很差(除非您真的知道本身在作什么)。

new与此相似-它一般用于使事情变得没必要要地复杂和难以阅读,而且能够编码的最有用的用法模式已编码为各类类。 此外,若是您须要使用尚没有标准类的任何新用法模式,则能够编写本身的对它们进行编码的类!

我什至认为new是比goto 更糟糕的 ,由于须要将newdelete语句配对。

goto同样,若是您曾经认为须要使用new ,则可能作得很差-尤为是在类的实现以外,这样作的目的是封装全部您须要执行的动态分配。


#4楼

还有一个以上全部正确答案的要点,这取决于您正在执行哪一种编程。 例如,在Windows中开发内核->堆栈受到严格限制,您可能没法像在用户模式下那样出现页面错误。

在这样的环境中,新的或相似C的API调用是首选的,甚至是必需的。

固然,这仅仅是规则的例外。


#5楼

使用new时,对象将分配给堆。 一般在预期扩展时使用。 当您声明诸如

Class var;

它放在堆栈上。

您将始终必须使用new对放置在堆上的对象调用destroy。 这为内存泄漏打开了可能。 放置在堆栈上的对象不容易发生内存泄漏!

相关文章
相关标签/搜索