set_new_handle

 一、 析构函数里对指针成员调用delete

       分析: 在析构函数中 调用delete 的好处在于 在超出作用域的时候 其会自动调用这个析构函数 。这样就避免了内存的泄露等问题的出现!不会由于某些原因造成程序跳过delete而造成内存泄露!删除空指针是安全的 ,因此可以使程序写起来更加的规整 !

二、  预先准备好内存不够的情况

        方法 1:#define new(ptr, type) \
                        try { (ptr) = new type; } \
                            catch (std::bad_alloc&) { assert(0); }

                对于这种做法没什么多说的 !

        方法 2:指定出错处理函数

               typedef void (*new_handler)();
                       new_handler set_new_handler(new_handler p) throw();

                 当operator new不能满足内存分配请求时,new-handler函数不只调用一次!而是反复进行调用,直到找到足够的内存!这样就要求这个new-handle函数应该满足下列条件 :

                    ·产生更多的可用内存。

                    ·安装另一个不同的new-handler函数。如果当前的new-handler函数不能产生更多的可用内存,可能它会知道另一个new-handler函数可以提供更多的资源。这样的话,当前的new-handler可以安装另一个new-handler来取代它(通过调用set_new_handler)。下一次operator new调用new-handler时,会使用最近安装的那个。(这一策略的另一个变通办法是让new-handler可以改变它自己的运行行为,那么下次调用时,它将做不同的事。方法是使new-handler可以修改那些影响它自身行为的静态或全局数据。)
    ·卸除new-handler。也就是传递空指针给set_new_handler。没有安装new-handler,operator new分配内存不成功时就会抛出一个标准的std::bad_alloc类型的异常。
    ·抛出std::bad_alloc或从std::bad_alloc继承的其他类型的异常。这样的异常不会被operator new捕捉,所以它们会被送到最初进行内存请求的地方。(抛出别的不同类型的异常会违反operator new异常规范。规范中的缺省行为是调用abort,所以new-handler要抛出一个异常时,一定要确信它是从std::bad_alloc继承来的。想更多地了解异常规范,参见条款m14。)
    ·没有返回。典型做法是调用abort或exit。abort/exit可以在标准c库中找到。

                方法 3: 设计一个公用的基类 , 在类中设计自己的set_new_handle

template<class t> // 提供类set_new_handler支持的
class newhandlersupport { // 混合风格”的基类
public:
 static new_handler set_new_handler(new_handler p);
 static void * operator new(size_t size);
private:
 static new_handler currenthandler;
};
template<class t>
new_handler newhandlersupport<t>::set_new_handler(new_handler p) {
 new_handler oldhandler = currenthandler;
 currenthandler = p;
 return oldhandler;
}
template<class t>
void * newhandlersupport<t>::operator new(size_t size)
{
 new_handler globalhandler = std::set_new_handler(currenthandler);
 void *memory; try { memory = ::operator new(size); }
 catch (std::bad_alloc&) {
 std::set_new_handler(globalhandler); throw;
 } std::set_new_handler(globalhandler);
 return memory;
}
// this sets each currenthandler to 0
template<class t>
new_handler newhandlersupport<t>::currenthandler;

有了这个模板类,对类x加上set_new_handler功能就很简单了:只要让x从newhandlersupport<x>继承:

// note inheritance from mixin base class template. (see // my article on counting objects

for information on why// private inheritance might be preferable here.) class x: public

 

newhandlersupport<x> { ... // as before, but no declarations for

}; // set_new_handler or operator new

 

利用模板是为了使每一个继承这个基类的派生类都有自己的实例 , 因为大家知道 set_new_handle 每次设

置都是全局的 , 影响类的封装性 同 高内聚低耦合的性质!因此采用这种方式 去处理 !

  
class widget { ... }; widget *pw1 = new widget;// 分配失败抛出std::bad_alloc if if (pw1 == 0) ... // 这个检查一定失败 widget *pw2 = new (nothrow) widget; // 若分配失败返回0 if (pw2 == 0) ... // 这个检查可能会成功

在前面的文章中有介绍这种情况 ,这里不再多说 !

三、写operator new和operator delete时要遵循常规

    非类成员形式的operator new的伪代码看起来会象下面这样:
 void * operator new(size_t size)        // operator new还可能有其它参数
{                                      

  if (size == 0) {                      // 处理0字节请求时,
    size = 1;                           // 把它当作1个字节请求来处理
  }                                    
  while (1) {
    分配size字节内存;

    if (分配成功)
      return (指向内存的指针);

    // 分配不成功,找出当前出错处理函数
    new_handler globalhandler = set_new_handler(0);
    set_new_handler(globalhandler);

    if (globalhandler) (*globalhandler)();
    else throw std::bad_alloc();
  }
}

在设计自己的 new 的情况时 需要注意继承这种情况 例如:

class base {
public:
  static void * operator new(size_t size);
  ...
};

class derived: public base       // derived类没有声明operator new
{ ... };                         //

derived *p = new derived;        // 调用base::operator new

处理这种情况 应该 采取这样的办法 :

 void * base::operator new(size_t size)
{
  if (size != sizeof(base))             // 如果数量“错误”,让标准operator new
    return ::operator new(size);        // 去处理这个请求
                                        //

  ...                                   // 否则处理这个请求
}
 在delete中也同样采取这种方式 :

正常实现operator delete 的伪代码:

void operator delete(void *rawmemory)
{
  if (rawmemory == 0) return;    file://如/果指针为空,返回
                                 //

  释放rawmemory指向的内存;

  return;
}

在继承情况下 应该写成的处理代码 :

class base {                       // 和前面一样,只是这里声明了
public:                            // operator delete
  static void * operator new(size_t size);
  static void operator delete(void *rawmemory, size_t size);
  ...
};

void base::operator delete(void *rawmemory, size_t size)
{
  if (rawmemory == 0) return;      // 检查空指针

  if (size != sizeof(base)) {      // 如果size"错误",
    ::operator delete(rawmemory);  // 让标准operator来处理请求
    return;                       
  }

}

有关operator new和operator delete(以及他们的数组形式)的规定不是那么麻烦,重要的是必须遵守它。只要内存分配程序支持new-handler函数并正确地处理了零内存请求,就差不多了;如果内存释放程序又处理了空指针,那就没其他什么要做的了。至于在类成员版本的函数里增加继承支持,那将很快就可以完成。

posted @ 2012-11-10 10:52  dyhui1992  阅读(630)  评论(0编辑  收藏  举报