C++,也省点心,从此不再关心 new 内存分配失败

陪她去流浪 桃子 2015年10月19日 编辑 阅读次数:4555

开了个虚拟机,开了5个VS2015,把内存占得只剩不到64M,但发现程序还是跑得蛮正常的,居然没有程序崩(没有看到)。

看几张内存使用过载截图:

4G的内存已经飙到了极限,但是程序没崩。。。

不管他们作了什么手段,脚本语言写得多了,现在我也不想管那么多了。如果 new 失败了,要么就让它抛异常,不处理,直接让程序崩,要么不让 new 抛异常,永远(?)不抛。 不过我还是觉得后者要妥当些。于是就决定这样干下去。脚本语言等那么耗内存都不曾管,我大C++管它干嘛。

要让 new 不抛异常,蛮简单,可以先看看 new 的实现(VS2013,/vc/crt/src/new.cpp):

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                        _THROW_NCEE(_XSTD bad_alloc, );
                }

        return (p);
        }

,可以看到,new 调用C语言标准库的 malloc 进行内存分配,并且在内存分配失败的时候会调用 _callnewh 来调用我们自定义的 new-handler,如果 new-handler 返回非零,new 运算符就会再次尝试内存分配,直到分配内存或 new-handler 返回了零,那么 new 此时将会以抛异常的形式来表示内存分配失败。

所以如果我们设置了一个自定义的 new-handler(默认为空)回调函数,那么每次 new 分配失败的时候,该回调函数会被调用,一次或多次。而如果我们的 new-hander 总是返回非零的话,new 也就不会再抛异常了。放心码代码了!

而 new-handler 的原型、设置、获取也很简单:

typedef int (__cdecl * new_handler) (size_t);
new_handler __cdecl set_new_handler(new_handler pnh);
new_handler __cdecl get_new_handler();

。那么,简单地像下面这样做就可以了(我一般在进入main的函数就这样干):

// C++11 之前
int my_new_handler(size_t) {
    return 1;
}

set_new_handler(my_new_handler);

// C++11及以后可以用lambda了
set_new_handler([](size_t){return 1;});

,new-handler 全局只有一个,也就是说,如果有其它地方调用了设置的话,会被覆盖掉的。除非再设置回来。

标签:C++ · 内存管理