当前位置:C++技术网 > 资讯 > [C++] 小疑问,运算符函数 new 是否需要使用异常捕获

[C++] 小疑问,运算符函数 new 是否需要使用异常捕获

更新时间:2016-09-24 23:57:44浏览次数:1+次

Windows系统环境下,在编写C++数据操作代码中,由于栈空间只有1MB,我们经常会使用堆空间的内存来储存大量的数据,所以就会使用到运算符函数 new 。

在网络引擎的搜索下,得知使用 new 时,内存不足时会触发异常。

至于在什么情况之下会内存不足呢,通常是电脑的内存不足 或 堆空间的使用超过1G。

1.首先我认为电脑内存不足这点理由可以排除,现在的电脑硬件都是非常发达的了,不像以前那样容量那么低,且很贵。

2.堆空间1G使用起来基本算是很充足的了,除非是些很大图片的数据,需要来回加载释放。

经过以上两点,可以确认 new 时不加异常保护是可以的,但是为了以防万一,我都每次使用 new 时还是会加个异常捕获,但是每次异常捕获都会增加代码的行数,对代码的阅读增加麻烦。

1.这时我就会为每一个 new 都添加个函数,这样又会导致效率会一定程度降低,因为每调用一次函数,在汇编中都是要进行保存寄存器的值,然后进行回调到指定的函数体内。

2.关闭异常,这点我不推荐,这样寻找错误时会变得非常麻烦。

3.malloc() 函数,这个C语言函数申请不到内存时会返回NULL,这点是非常好的,我也很喜欢,但是这样的话,就无法使用构造与析构函数了,这点还是非常伤的。

所以在此我向C++技术网进行提问,运算符函数 new 是否需要使用异常捕获,如果是专业级的程序员是会进行什么样的处理?

最后我先感谢C++技术网的解答。


C++技术网解答:

    系统内存不足这种情况在个人电脑一般不会出现了,个人电脑内存基本上是不会出现不够用的情况的。但是在服务器系统中,因为内存加大会增加经济成本,所以一 些服务器会精打细算,尽量减少成本。像我们C++技术网,为了普及技术,为了帮助更多初学者,虽然无法盈利,成本都无法填补,也就只能精打细算,在购买服 务器选用内存会偏小,然后再运行程序多了之后,后来启动的程序就会报内存分配失败错误。这个也就是new会遇到的内存不足的问题。

    没有见过不代表不存在,内存不足的情况在个人电脑不存在,在云服务器或者嵌入式单片机等设备,因为硬件或者成本限制,还是非常的昂贵的。当以后可穿戴设备越来越多,自然内存也是特别需要精打细算的,内存不足就经常需要考虑到,不能像PC一样充裕。

    看了你写的情况,非常的到位,每一个点都落到了痛点,看得出你思考的非常全面和深入,非常不错。但是你要知道,鱼与熊掌经常是不可兼得。所以,你才出现了困惑,而且还不知道如何解决。不过,你的问题是有突破点的。

    你要达到的是不降低效率、要有异常机制、要new的特性而否定malloc,看似无解了。这也是鱼与熊掌兼得的矛盾。稍加分析,我们是要保留new的,第三点可以忽略。异常自然不要关闭,这个对程序不好,不是一个良好程序应该做的,除非特殊情况采取。那么就针对第一点来突破。

    你的设想是在new使用前加一个检测,然而你绝对这样降低了效率,降低效率是在于添加了函数,函数在调用时确实损失了性能,当然,如果采用异常机制,也会进一步损失性能。但是保护是必须加的,这也是我们的目的。相对于函数调用和异常机制来说,异常处理机制更耗费性能。而我们函数做的保护以预防为主,而不是事后处理。如果是异常机制触发后,再是使用函数来做处理,则是下下策。

    既然如此,我们可以在new之前做一个检测。可以以函数形式来检测,检测合格再new。此时的模式是设计模式中的工厂模式(我没有真正看过设计模式的工厂模式的解释,直接按照名字来理解的,有机会要学一下工厂模式),也就是内存的创建和销毁不是你直接操作的,而是有工厂代工。工厂处理的结果返回给你,你再做进一步的处理。如果你觉得这个方式增加了代码的改动,或者让new变得使用麻烦,也就是说不想使用代理内存操作,那么这个解决方案可以先搁置。我们再来探讨另外一种方案。

    这个方案应该来说是最好的。当然,在内存管理方面,工厂模式是非常好的结构。就你这个问题,工厂模式可以暂时不考虑。关键得要有更好的方案,幸运的是,我们确实有。不仅的性能上更好,而且代码改动几乎没有,只需要写一些代码即可实现。是什么呢?

    那就是重载new。new作为C++一个基本操作符,是可以重载的。而我们现在的需求就是保持new原有的特性,然后再加上自己的检测。这样很好实现的。下面来说说实现思路。

    在重载new的函数中,我们先获取当前系统可用的内存数量,再将数量和要分配的内存大小对比,如果足够,那么就可以继续分配内存。如果不够则返回失败。此时的分配内存失败,并没有真正分配过内存,而是我们主动检测的结果。这也是预防机制,此时的失败很快就可以处理,完全由我们自己控制。极大提高了效率。

    另外,我们直接在new重载时做了检测,不需要工厂模式的第三方检测等函数调用,也不需在new之前调函数或者加几句代码检测,既能确保效率不降低很多,被函数调用拖低,也不会增加代码改动,基本不用改原有代码。在new重载函数中,我们的检测直接写代码检测,而不是调用一个自己写的检测函数来检测,也就是直接检测代码写在new重载函数里,这样也可以避免函数调用带来的性能损失。

    检测通过之后,我们再调用原生的new来操作,这也轻松保持了new原有的特性,也增加了我们自己需要的检测,可谓是完美的解决办法。省时省力效率降低的最小,而且是必要的检测,也不算什么效率损失。

    感谢对C++技术网的信任和支持,如果必要,可以开通会员,不限制提问次数,而且开通会员也是支持网站的发展。非会员也是可以一天一次的提问,解答的质量也是毫无折扣的。如果还有问题,可以继续在文章底部留言。

以下是【百度@CAD~若见我喊 我闭关】的补充,非常到位:
    引入了重载NEW的概念,讲得很到位了。
    不过文中有两点没有提到:
    这两点我个人也不太了解,只是听说过:
    1.个人电脑不会内存不足,并不光是因为内存足够,而是有虚拟内存的关系,只要有虚拟内存,WIN总能分配足够的内存给程序使用,虽然会降低效率.而在服务器上则不同,不光是服务器配置精打细算,为了节省资金而选择低配电脑导致内存少.其实还有一个重要原因是,服务器管理员为了保证服务器的性能,可能会关闭虚拟内存.然后系统发现内存不足了,又无法从硬盘上分配虚拟内存,自然就只能抛内存不足异常了.
    2.内存不足也不一定是物理内存不足,而是可能内存碎片太多.因为系统分配内存的时候,往往是需要分配连续空间的.而因为内存碎片太多,导致凑不出足够的连续内存空间的话,也会报内存分配失败的.
    其中第二点跟系统的内存管理能力有关。展开来说一下,比如一台电脑安装了4G内存.因为某种不稳定因素,导致虽然电脑还空余2G内存,却分配不出500M连续的内存空间.那么程序申请500M空间的话,也可能会遇到内存分配失败的异常。然后这一点比较复杂.WIN系统的内存管理器是有碎片整理功能的,这个能力随系统不同而有区别。从VISTA系统开始,内存管理器增加了一个专门针对内存碎片的列表,能有效优化内存碎片很多时,内存分配的效率。所以,我们常用的WIN7系统,应该不用太担心内存分配异常。而且有人实测,同样申请100M内存,操作系统的表现是不一样的。一段代码,开了100个线程,每个线程申请1M空间,然后在XP下这个程序占用100M内存,在WIN7却只占用了28M内存..具体情况我没实测,只是听说,大家有兴趣可以测试一下。我忘了是指线程默认的1M栈空间,还是申请的1M堆空间。反正内存管理学问是很大的。如果要搞长年在线的服务器程序,是不可以掉以轻心的。