Effective C++ 7

最后更新于:2022-04-01 15:49:36

7.预先准备好内存不够的情况。 new在无法完成内存分配请求时,会抛出异常,异常了要怎么办,这是一个很现实且以后绝对要碰到的问题。 在c中一般使用宏来分配内存并检测分配是否成功,c++中类似以下函数: ~~~ #define NEW(PTR,TYPE) \ try { (PTR) = new TYPE;} \ catch (std::bad_alloc& ){assert(0);} ~~~ catch 的std::bad_alloc为new 操作符中 不能满足内存分配请求时抛出的异常类型。 assert是在<assert.h>中宏,或者带命名空间的<cassert> 。宏检测传给它的表达式的值是否为0,如果是0,就会发出一条出错信息,并调用abort。assert在没有定义标准宏NDEBUG时,即只在调试状态中这么做,当产品发布后,即定义了NDEBUG后,assert什么也不做。 但使用宏是不够的,因为其只能进行简单的一个基本数据类型的内存分配,而对于数组以及有构造函数的数据类型无法使用。可以考虑重载operator new 。 而常用的简单的出错处理方法是,通过设置一个出错处理函数,使内存分配请求不能满足时,调用这个处理函数。因为operator new 操作符在不能满足请求时,会在抛出异常前调用客户指定的一个出错处理函数,这个函数称为new-handler函数。 指定出错处理函数要用到set_new_handler 函数,该函数位于头文件<new>中,其定义如下: ~~~ new_handler __cdecl set_new_handler(_In_opt_ new_handler) _THROW0(); ~~~ 而new_handler的定义如下: ~~~ typedef void (__cdecl * new_handler) (); ~~~ new_handler为指向一个没有参数没有返回值的函数,而set_new_handler函数是一个输入参数为 operator new 分配内存失败时调用的出错处理函数的指针,返回值为set_new_handler函数没调用之前的已经在起作用的旧的函数处理函数的指针。使用方法如下: ~~~ void noMoreMemory(){ cerr<<"123"; abort(); } int main(){ set_new_handler(noMoreMemory); int *p = new int [ 0x1fffffff ]; ~~~ 真正在程序中,new_handler函数不是简单的检测出错然后停止程序,而是要去处理出错,即通过一些措施,使下次内存分配可以成功。一个好的new_handler函数从以下几个方向入手: 1.产生更多的可用内存。若在程序启动时预留一大块内存。然后失败时释放出这些内存供使用。 2.安装另一个不同版本的new_handler函数。通过一个新的可以获得资源的函数来获得更多资源,或者使用一些全局变量来改变自身的行为。 3.卸载new_handler。分配无法成功,直接返回抛出标准的std::bad_alloc异常。 4.抛出bad_alloc异常或bad_alloc的派生异常。 5.没有返回,即如上所示,调用abort或exit。 类中的内存分配出错可以有自己重载的类的new操作符和 set_new_handler来实现。而这些函数与重载都是类的静态成员。 首先,类中要有一个静态成员 currentHandler来保存当前使用的new_handler函数。 对于set_new_handler,它只是将类内的指针进行更换,以及返回旧的指针,而标准版本也是如此做的。 对于operator new ,类中的重载new操作符,只是简单的调用原有的new操作符操作,只是错误处理用上类中设置的新的出错处理函数,所以其做法只是用全局的set_new_handler()设置当前类中的currentHandler为处理函数,分配内存后或者异常报错前,恢复即可。 代码如下: ~~~ class A{ public: static void* operator new (size_t size); static new_handler set_new_handler(new_handler p); private: static new_handler currentHandler; }; new_handler A::currentHandler; new_handler A::set_new_handler(new_handler p){ new_handler t = currentHandler; currentHandler = p; return t; } void* A::operator new(size_t size){ new_handler globalHandler = std::set_new_handler(currentHandler); void* memory; try{ memory = ::operator new(size);//::operator new 表示为全局的new操作符。 } catch(std::bad_alloc&){ std::set_new_handler(globalHandler); throw; } std::set_new_handler(globalHandler); return memory; } ~~~ 而要做到很好的代码重用,即对任何一个类都能轻松的实现以上代码实现的内容,可以创建一个混合风格的基类,这种基类允许子类继承它的某一特定功能,如这里的new操作符的出错处理。 混合风格?43.
';