您的位置:首页 > 编程语言 > C语言/C++

细读《Effective C++》之十四

2007-06-21 07:02 274 查看

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 == 0) return; // 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 == 0) return; // 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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: