细读《Effective C++》之十四

Chapter 8. Customizing new and delete

正如C#、JAVA声称自己的内置“垃圾回收能力”一样,C++则以其高效的“手动回收”著称。Scott提到heap资源在多线程编程时的访问问题,引起了我的兴趣。

Item 49 - 52

条款49:Understand the behavior of the new-handler

operator new失败时将执行以下操作:

1) 调用指定的错误处理函数(new-handler,实际就是一个无参数、无返回值的函数)。

2) 抛出异常。

// function to call if operator new can't allocate enough memory
void  outOfMem()
{
  std::cerr 
<< "Unable to satisfy request for memory "
;
  std::abort();
}

int
 main()
{
  std::set_new_handler(outOfMem);
  
int *pBigDataArray = new int[100000000L
];
  ...
}

一个设计良好的new-handler应该完成以下事情:

1) Make more memory available.

2) Install a different new-handler.

3) Deinstall the new-handler.

4) Throw an exception of type bad_alloc or some type derived from bad_alloc.

5) Not return, typically by calling abort or exit.

new_handler和operator new可以在普通类中被实现,为了使该方案可以被复合使用,可以将其模板化:

template<typename T>              // "mixin-style" base class for
class NewHandlerSupport{          // class-specific set_new_handler
public:                           // support
  static std::new_handler set_new_handler(std::new_handler p) throw ();
  
static void * operator new(std::size_t size) throw
(std::bad_alloc);
  ...                             
//
 other versions of op. new —
                                  
// see Item 52

private :
  
static
 std::new_handler currentHandler;
};

template
<typename T>

std::new_handler
NewHandlerSupport
<T>::set_new_handler(std::new_handler p) throw ()
{
 std::new_handler oldHandler 
=
 currentHandler;
 currentHandler 
=
 p;
 
return
 oldHandler;
}

template
<typename T>

void* NewHandlerSupport<T>::operator new (std::size_t size)
  
throw
(std::bad_alloc)
{
  NewHandlerHolder h(std::set_new_handler(currentHandler));
  
return ::operator new
(size);
}

// this initializes each currentHandler to null

template<typename T>
std::new_handler NewHandlerSupport
<T>::currentHandler = 0;

需要将new_handler和operator new特别处理的类直接从NewHandlerSupport派生即可:

class Widget: public NewHandlerSupport<Widget>  {
  ...                          
// as before, but without declarations for

};                             // set_new_handler or operator new

有一种“不抛出异常”的operator new的实现:

class  Widget { ... };
Widget 
*pw1 = new Widget;                 //
 throws bad_alloc if
                                          
// allocation fails

if (pw1 == 0) ...                         // this test must fail
Widget *pw2 =new (std::nothrow) Widget;   //  returns 0 if allocation for
                                          
// the Widget fails

if (pw2 == 0) ...                         // this test may succeed

需要注意的是,上述代码只能保证pw2在执行Widget *pw2 =new (std::nothrow) Widget时不抛出异常,却不能保证pw2中的operator new操作不抛出异常。

Things to Remember

1) set_new_handler allows you to specify a function to be called when memory allocation requests cannot be satisfied.

2) Nothrow new is of limited utility, because it applies only to memory allocation; subsequent constructor calls may still throw exceptions.

条款50:Understand when it makes sense to replace new and delete

重写new和delete的8个理由:

1) To detect usage errors.

2) To improve efficiency.

3) To collect usage statistics.

4) To increase the speed of allocation and deallocation.

5) To reduce the space overhead of default memory management.

6) To compensate for suboptimal alignment in the default allocator.

7) To cluster related objects near one another.

8) To obtain unconventional behavior.

Things to Remember

There are many valid reasons for writing custom versions of new and delete, including improving performance, debugging heap usage errors, and collecting heap usage information.

条款51:Adhere to convention when writing new and delete

一段operator new的伪代码:

void * operator new(std::size_t size) throw (std::bad_alloc)
{                                      
// your operator new might

  using namespace std;                 // take additional params
  if (size == 0) {                     // handle 0-byte requests
    size = 1;                          // by treating them as
  }                                    // 1-byte requests

  
while (true ) {
   attempt to allocate size bytes;
    
if
 (the allocation was successful)
       
return
 (a pointer to the memory);

    
//
 allocation was unsuccessful; find out what the
    
// current new-handling function is (see below)

    new_handler globalHandler = set_new_handler(0 );
    set_new_handler(globalHandler);

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

对于base class的operator new操作,在其derived class不重写的情况下,可以这样写base class的operator new:

void * Base::operator new(std::size_t size) throw (std::bad_alloc)
{
  
if (size != sizeof(Base))               // if size is "wrong,"

     return ::operator new(size);         //  have standard operator
                                          
// new handle the request

  ...                                     //  otherwise handle
                                          
// the request here

}

item39中提过:独立对象的size不会为0。operator delete的唯一要求就是“保证delete NULL pointer永远安全”:

void operator delete(void *rawMemory) throw ()
{
  
if (rawMemory == 0return;            //
 do nothing if the null
                                         
// pointer is being deleted

  deallocate the memory pointed to by rawMemory;
}

void Base::operator delete(void *rawMemory, std::size_t size) throw
()
{
  
if (rawMemory == 0return;           // check for null pointer

  if (size != sizeof(Base)) {           // if size is "wrong,"
     ::operator delete(rawMemory);      // have standard operator
     return;                            // delete handle the request
  }

  deallocate the memory pointed to by rawMemory;
  
return
;
}

Things to Remember

1) operator new should contain an infinite loop trying to allocate memory, should call the new-handler if it can't satisfy a memory request, and should handle requests for zero bytes. Class-specific versions should handle requests for larger blocks than expected.

2) operator delete should do nothing if passed a pointer that is null. Class-specific versions should handle blocks that are larger than expected.

条款52:Write placement delete if you write placement new

operator new和operator delete要成对使用,不仅仅是指形式上要搭配,在本质上new和delete也要是对应的,这句话要从signatures来理解:

// normal forms of new & delete
void* operator new(std::size_t) throw (std::bad_alloc);
void operator delete(void *rawMemory) throw();  //
 normal signature at global scope

// placement version of new & delete

void* operator new(std::size_t, void *pMemory) throw();   // "placement new"
void operator delete(void *, std::ostream&throw();

而且在声明他们时,注意不应该掩盖了其他形式的new & delete。

Things to Remember

1) When you write a placement version of operator new, be sure to write the corresponding placement version of operator delete. If you don't, your program may experience subtle, intermittent memory leaks.

2) When you declare placement versions of new and delete, be sure not to unintentionally hide the normal versions of those functions. 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值