C++编程规范之45:总是一起提供new和delete
2014-02-20 22:27
288 查看
摘要:
它们是一揽子交易:每个类专门的重载void*operator new(parms)都必须与对应的重载void operator delete(void*, params)相随相伴,其中parms是额外参数类型的一个列表(第一个总是std:size_t)。数组形式的new[]和delete[]也同样如此。
很少需要提供自定义的new或者delete,但是如果需要其中一个,那么通常两个都需要。如果定义了类专门的T::operator new进行某种特殊的分配操作,很可能还需要定义一个类专门的T::operator delete进行相应的释放操作。
这些阐述可能有些过于基础了,但是之所以要加入本条款,有一个更加微妙的原因:编译器可能需要T::operator delete的重载,即使实际上从来也不会调用它。这才是为什么要成对提供operator new和operator delete(以及operator new[]和operator delete[])的原因。
假设定义了一个带有自定义分配操作的类:
class T
{
//……
Static void* operator new(std::size_t)
Static void* operator new(std::size_t,CustomAllocator&)
Static void operator delete(void*,std::size_t)
};
这样就为分配和释放建立了一个简单的协议。
1.调用者能够用默认的分配器(使用new T)或者自定义的分配器,(使用new(alloc)T,其中alloc是一个customAllactor类型的对象)来分配类型T的对象。
2.唯一调用者可能调用的operatordelete是默认的operator delete(size_t),因此当然应该实现,从而能够正确地释放已分配的内存。
到目前为止,一切正常。
但是编译器冉冉需要秘密地调用另一个delete重载,即T::operatordelete(size_t, CustomAllocator&)。这是因为语句
T* p = new(alloc)T;
实际上将扩展为类似下面的代码:
//编译器为T* p =new(alloc)T;生成的代码
//
Void*_compilerTemp = T::operator new (sizeof(T),alloc);
T* p;
Try
{
P = new(_compilerTemp)T;
}
Catch(…)
{
T::operator delete(_compilerTemp,sizeof(T), alloc);
throw
}
因此,如果分配成功,但是构造函数失败了,那么编译器将顺理成章地自动插入代码,为重载的T::operator new调用对应的T::operator delete。对应的签名是void operator delete(void*,whatever-parameters-new-takes).
下面是煞风景的部分了。C++ Standard规定,当且仅当operatordelete的重载实际退出时,以上代码才能生成。否则,在构造函数失败的情况下,代码不会调用任何operator delete。也就是说,如果构造函数失败,内存将泄漏。
正因为如此,重载void*operator new(parms)必须伴有与其对应的重载void operator delete(void* ,params)——因为编译器自己要调用它们。
它们是一揽子交易:每个类专门的重载void*operator new(parms)都必须与对应的重载void operator delete(void*, params)相随相伴,其中parms是额外参数类型的一个列表(第一个总是std:size_t)。数组形式的new[]和delete[]也同样如此。
很少需要提供自定义的new或者delete,但是如果需要其中一个,那么通常两个都需要。如果定义了类专门的T::operator new进行某种特殊的分配操作,很可能还需要定义一个类专门的T::operator delete进行相应的释放操作。
这些阐述可能有些过于基础了,但是之所以要加入本条款,有一个更加微妙的原因:编译器可能需要T::operator delete的重载,即使实际上从来也不会调用它。这才是为什么要成对提供operator new和operator delete(以及operator new[]和operator delete[])的原因。
假设定义了一个带有自定义分配操作的类:
class T
{
//……
Static void* operator new(std::size_t)
Static void* operator new(std::size_t,CustomAllocator&)
Static void operator delete(void*,std::size_t)
};
这样就为分配和释放建立了一个简单的协议。
1.调用者能够用默认的分配器(使用new T)或者自定义的分配器,(使用new(alloc)T,其中alloc是一个customAllactor类型的对象)来分配类型T的对象。
2.唯一调用者可能调用的operatordelete是默认的operator delete(size_t),因此当然应该实现,从而能够正确地释放已分配的内存。
到目前为止,一切正常。
但是编译器冉冉需要秘密地调用另一个delete重载,即T::operatordelete(size_t, CustomAllocator&)。这是因为语句
T* p = new(alloc)T;
实际上将扩展为类似下面的代码:
//编译器为T* p =new(alloc)T;生成的代码
//
Void*_compilerTemp = T::operator new (sizeof(T),alloc);
T* p;
Try
{
P = new(_compilerTemp)T;
}
Catch(…)
{
T::operator delete(_compilerTemp,sizeof(T), alloc);
throw
}
因此,如果分配成功,但是构造函数失败了,那么编译器将顺理成章地自动插入代码,为重载的T::operator new调用对应的T::operator delete。对应的签名是void operator delete(void*,whatever-parameters-new-takes).
下面是煞风景的部分了。C++ Standard规定,当且仅当operatordelete的重载实际退出时,以上代码才能生成。否则,在构造函数失败的情况下,代码不会调用任何operator delete。也就是说,如果构造函数失败,内存将泄漏。
正因为如此,重载void*operator new(parms)必须伴有与其对应的重载void operator delete(void* ,params)——因为编译器自己要调用它们。
相关文章推荐
- C++编程规范指46:如果提供专门的new,应该提供所有标准形式(普通、就地和不抛出)
- Effective C++ 条款15、16 在资源管理类中提供对原始资源的访问||成对使用new 与 delete要采取相同形式
- C++的指针的坑:“new[]的指针需要delete[]”和“子类指针可以转父类指针”的两条规则成功的冲突到了一起
- 跟我一起学C++之new、delete运算符
- C++中的new和delete
- C++动态分配内存(new)和撤销内存(delete)
- 内存分配方式,堆区,栈区,new/delete/malloc/free
- new 和delete的讲解
- 如何用new动态申请二维数组,然后又如何用delete释放
- more effective c++第二章读书笔记:c++的运算符,运算符转换,前缀自增和后缀自增,operatpr new和operator delete.
- Vector的new与delete问题
- Effective C++笔记(11)—定制new和delete
- new malloc delete free的区别
- Malloc/new和delete/free的区别(百度2012实习生招聘)
- new[]上面居然有一个内存计数,怪不得delete[]从来不出错
- 【C++总结】C++的new和delete
- new、delete与malloc、free
- C/C++ 内存分配方式,堆区,栈区,new/delete/malloc/free
- 示例解析 C++ 的 new / delete 和 new [] / delete [] 之用法
- [Boolan] C++第五周 重载const,new,delete