C++ 的new 操作符抛不抛出异常的问题

July 17, 2013 | 1 Minute Read

    




  下载LOFTER我的照片书  |
最近碰到一个问题,vc 6.0的new是不抛出异常的,只是返回空指针。但后来c++ 标准规定new造作符分配不到哦啊内存时抛出std::bad_alloc异常了,vc后来的版本也就跟着改了。但之前的vc6.0过来的代码,很多都是以前的旧风格判断返回的是不是NULL的。明显这个地方需要改一下了,如果要在新的vc比如 vc2010里面正常工作的话。

c++ 标准里面也是可以进行不抛出异常的new的,要写成 new (std::nothrow) 这样。另外vc还可以让你链接到旧风格的不抛出异常的new函数去。但stl的代码里面默认都是加载new操作符会抛出异常的,如果通过链接参数 nothrownew.obj禁止new抛出异常的话,stl的代码里面不进行空指针的判断,应该是会导致非法内存访问的。比如 vector.resize这种操作是会抛出异常的,如果禁止了话,应该会导致resize里面使用空指针吧。



在vc2010 windows xp下面release版的测试了一下,看看上面说的集中情况的调用的new函数的代码.



1. 默认的会抛出异常的new

========================

char * temp = new char[3 *1024 *1024 *1024];

默认的new会抛出 std::bad_alloc异常,他使用的是下面这个代码。



std::set_new_handler可以设置一个new分配内存失败时的回调函数。可以自己在那里回收内存或者抛出其他类型的异常。

http://www.cplusplus.com/reference/new/set_new_handler/

“Effective C++(第三版 中文翻译) 条款7:预先准备好内存不够的情况”

提到这个std::set_new_handler的用法,不过感觉用处不大。在这个回调函数里面也只能退出程序或者抛出异常吧。一般都不能自己回收内存的了ba ?

http://www.kuqin.com/effectivec2e/ch02b.htm





new.cpp 

-------------------

/***
*new.cxx - defines C++ new routine
*
*       Copyright (c) Microsoft Corporation.  All rights reserved.
*
*Purpose:
*       Defines C++ new routine.
*
*******************************************************************************/

#include <cstdlib>
#include <new>

_C_LIB_DECL
int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc);
_END_C_LIB_DECL

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)  ///循环调用你std::set_new_handler 设置的回调函数直到分配成功为止。 
                {       // report no memory
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }

        return (p);
        }

/*
 * Copyright (c) 1992-2002 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
 V3.13:0009 */




2.显示的调用不抛出异常的new

=============================

char * temp = new (std::nothrow) char[30];

如果像上面这样,调用不抛出异常的new,可以看到调用的是下面这个代码。

太帮你做了try catch这些操作了。



newopnt.cpp  





void * __CRTDECL operator new(size_t count, const std::nothrow_t&)
	_THROW0()
	{	// try to allocate count bytes
	void *p;
	_TRY_BEGIN
	p = operator new(count);
	_CATCH_ALL
	p = 0;
	_CATCH_END
	return (p);
	}



3. 通过nothrownew.obj链接选项链接到旧的不抛出异常的new

===========================================

如果给项目属性的Link Options ->command line -> additional option,加上 nothrownew.obj 选项。

“nothrownew.obj” 是特殊选项,就会链接到旧的不遵守c++规范的不抛出异常的new,



new and delete Operators

http://msdn.microsoft.com/en-us/library/kftdy56f.aspx

http://msdn.microsoft.com/en-us/library/ms235330.aspx





他使用的是这个代码 nothrownew.cpp



void * operator new( size_t cb )
{
    void *res;

    for (;;) {

        //  allocate memory block
        res = malloc(cb);

        //  if successful allocation, return pointer to memory

        if (res)
            break;

        //  call installed new handler
        if (!_callnewh(cb))
            break;

        //  new handler was successful -- try to allocate again
    }

    RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0));

    return res;
}




这样改了是不会抛出异常了二十返回空指针了,但是也是不能使用这个配置的。因为c++ stl库假设 new 是会抛出异常的,stl是不会判断返回的是不是空指针的,如果使用了这个选项,stl的代码就会出现非法内存访问了。