《Effective C++》:条款49:了解new

C++内存是由程序员手动管理的,不像Java或.net有垃圾回收机制。C++内存管理主要是分配例程和归还例程(allocation and deallocation routines),即operator new和operator delete,还有一个配合的角色new-handler。当涉及到数组时,上面提到的operator new和operator delete就会变为operator new[]和operator delete[]。

内存管理在多线程环境下更为复杂,因为heap是一个可被改动的全局性资源,多个线程访问同一个资源会有race conditions(竞速状态)。使用static类型变量更要注意。如果没有适当同步控制(synchronization),使用无锁(lock-free)算法或精心防止并发访问(concurrent access)时,可能会破坏heap数据结构。

最后要注意一点,STL容器使用的heap是由容器所拥有的分配器对象(allocator objects)管理,不是用new和delete直接管理。

条款49:了解new-handler的行为

当operator new无法满足某一内存分配时,就会抛出一次。以前它会返回NULL指针,现在某些旧式编译器也还这么做。

在operator new抛出异常以前,会先调用一个客户指定的错误处理函数:new-handler。(这不是全部,operator new更复杂,见**条款**51)。客户用set_new_handler来指定这个“用以处理内存不足”的函数,这是声明于new的标准程序库函数

namespace std{typedef void(*new_handler)();new_handler set_new_handler(new_handler p) throw();}

new_handler是个函数指针,该函数没有参数也不返回任何东西。set_new_handler是设置一个new_handler并返回一个new_handler函数,返回的new_handler是指向set_new_handler被调用前正在执行的那个new-handler函数。后面的throw是一份异常明细,表示该函数不抛出异常。可以这样使用

void outOfMem(){std::cerr<<“Unable to satisfy request for memoryn”;std::abort();}int main(){std::set_new_handler(outOfMem);int *pBigDataArray=new int[100000000L];……}

如果operator new无法为100000000个整数分配足够空间,outOfMem会被调用。

当operator new无法满足内存申请时,它会不断调用new-handler,直到找到足够内存。反复调用的代码在**条款**51讨论。这里先说一下,设计良好的new-handler必须做好以下事情;

有时候,我们希望处理内存分配失败的情况和class相关。例如

class X{public:();……};class Y{public:();……};X* p1=new X;//分配不成功,调用X::outOfMemoryY* p2=new Y;//分配不成功,调用Y::outOfMemory

C++并不支持class专属的new-handler,但是我们自己可以实现这种行为。令每一个class提供自己的set_new_handler和operator new即可。……………………………………………………………………………………

现在打算处理Widget class内存分配失败的情况。首先要有一个operator new无法为Widget分配足够内存时的调用函数,即new_handler函数

class Widget{public:static std::new_handler set_new_handler(std::new_handler p) throw();(std::size_t size) throw(std::bad_alloc);private:static std::new_handler currentHandler;};std::new_handler Widget::currentHandler=0;std::new_handler Widget::set_new_handler(std::new_handler p) throw(){std::new_handler oldHandler=currentHandler;currentHandler=p;reutrn oldHandler;}

Widget的operator new做以下事情: 1、调用标准set_new_handler,告知Widget错误处理函数。这会将Widget的new-handler安装为global new-handler。 2、调用global operator new,如果失败,global operator new会调用Widget的new-handler,因为第一步。如果global operator new最终无法分配足够内存,会抛出一个bad_alloc异常。这时Widget的operator new要恢复原本的global new-handler,之后在传播异常。 3、如果global operator new调用成功,Widget的operator new会返回一个指针,指向分配的内存。Widget析构函数会管理global new-handler,它会将Widget’s operator new被调用前的那个global new-handler恢复回来。

class NewHandlerHolder{public:explicit NewHandlerHolder(std::new_handler nh):handlere(nh){}~NewHandlerHolder(){ std::set_new_handler(handler); }private:std::new_handler handler;NewHandlerHolder&(const NewHandlerHolder&);//防止copyingNewHandlerHolder& operator-(const NewHandlerHolder&);};

这使得Widget’s operator new的实现变得简单

void* Widget::operator new(std::size_t size) throw(std::bad_alloc){NewHandlerHolder h((size);}

Widget客户应该类似这样使用其new-handling

void outOfMem();Widget::set_new_handler(outOfMem);//设定outOfmem为Widget的new-handling函数Widget* pw1=new Widget;//内存分配失败,则调用outOfMEMstdstd::string;//内存分配失败则调用global new-handling(如果有)Widget::set_new_handler(0);//设定Widget专属new-handling为nullWidget* pw2=new Widget;//内存分配失败则立刻抛出异常画龙画虎难画骨,知人知面不知心。

《Effective C++》:条款49:了解new

相关文章:

你感兴趣的文章:

标签云: