Item 51:写new和delete时请遵循惯例
Item 51: Adhere to convention when writing new and delete.
Item 50介绍了怎样自己定义new
和delete
但没有解释你必须遵循的惯例。
这些惯例中有些并不直观,所以你须要记住它们!
operator
须要无限循环地获取资源。假设没能获取则调用”new handler”。不存在”new handler”时应该抛出异常。
newoperator
应该处理
newsize
的情况。
== 0operator
应该兼容空指针。
deleteoperator
作为成员函数应该处理
new/deletesize
的情况(由于继承的存在)。
> sizeof(Base)
外部operator new
Item 49指出了怎样将operator
重载为类的成员函数,在此我们先看看怎样实现一个外部(非成员函数)的
newoperator
:
newoperator
应当有正确的返回值。在内存不足时应当调用”new handler”,请求申请大小为0的内存时也能够正常运行。避免隐藏全局的(”normal form”)
newnew
。
- 给出返回值非常easy。
当内存足够时。返回申请到的内存地址;当内存不足时。依据Item
49描写叙述的规则返回空或者抛出bad_alloc
异常。 - 每次失败时调用”new handler”,并反复申请内存却不太easy。仅仅有当”new handler”为空时才应抛出异常。
- 申请大小为零时也应返回合法的指针。同意申请大小为零的空间确实会给编程带来方便。
考虑到上述目标,一个非成员函数的operator
大致实现例如以下:
new
void * operator new(std::size_t size) throw(std::bad_alloc){
if(size == 0) size = 1;
while(true){
// 尝试申请
void *p = malloc(size);
// 申请成功
if(p) return p;
// 申请失败,获得new handler
new_handler h = set_new_handler(0);
set_new_handler(h);
if(h) (*h)();
else throw bad_alloc();
}
}
size
时申请大小为
== 01
看起来不太合适,但它很easy并且能正常工作。况且你不会常常申请大小为0的空间吧?- 两次
set_new_handler
调用先把全局”new
handler”设置为空再设置回来,这是由于无法直接获取”new handler”,多线程环境下这里一定须要锁。 while(true)
意味着这可能是一个死循环。所以Item
49提到,”new handler”要么释放很多其它内存、要么安装一个新的”new handler”,假设你实现了一个没用的”new handler”这里就是死循环了。
成员operator new
重载operator
为成员函数一般是为了对某个特定的类进行动态内存管理的优化,而不是用来给它的子类用的。 由于在实现
newBase::operator
时,是基于对象大小为
new()sizeof(Base)
来进行内存管理优化的。
当然。有些情况你写的
Base::operator
是通用于整个class及其子类的,这时这一条规则不适用。
new
class Base{
public:
static void* operator new(std::size_t size) throw(std::bad_alloc);
};
class Derived: public Base{...};
Derived *p = new Derived; // 调用了 Base::operator new !
子类继承Base::operator
之后,由于当前对象不再是如果的大小。该方法不再适合管理当前对象的内存了。
new()
能够在Base::operator
中推断參数
newsize
。当大小不为sizeof(Base)
时调用全局的new
:
void *Base::operator new(std::size_t size) throw(std::bad_alloc){
if(size != sizeof(Base)) return ::operator new(size);
...
}
上面的代码没有检查size
。这是C++奇妙的地方,大小为0的独立对象会被插入一个
== 0char
(见Item
39)。 所以sizeof(Base)
永远不会是0,所以size
的情况交给
== 0::operator
去处理了。
new(size)
这里提一下operator
。它和
new[]operator
具有相同的參数和返回值, 要注意的是你不要如果当中有几个对象。以及每一个对象的大小是多少,所以不要操作这些还不存在的对象。
new
由于:
- 你不知道对象大小是什么。上面也提到了当继承发生时
size
不一定等于sizeof(Base)
。 size
实參的值可能大于这些对象的大小之和。由于Item
16中提到。数组的大小可能也须要存储。
外部operator delete
相比于new
,实现delete
的规则要简单非常多。唯一须要注意的是C++保证了delete
一个NULL
总是安全的,你尊重该惯例就可以。
相同地,先实现一个外部(非成员)的delete
:
void operator delete(void *rawMem) throw(){
if(rawMem == 0) return;
// 释放内存
}
成员operator delete
成员函数的delete也非常easy,但要注意假设你的new
转发了其它size
的申请,那么delete
也应该转发其它size
的申请。
class Base{
public:
static void * operator new(std::size_t size) throw(std::bad_alloc);
static void operator delete(void *rawMem, std::size_t size) throw();
};
void Base::operator delete(void *rawMem, std::size_t size) throw(){
if(rawMem == 0) return; // 检查空指针
if(size != sizeof(Base)){
::operator delete(rawMem);
}
// 释放内存
}
注意上面的检查的是
rawMem
为空,size
是不会为空的。
事实上size
实參的值是通过调用者的类型来推导的(假设没有虚析构函数的话):
Base *p = new Derived; // 如果Base::~Base不是虚函数
delete p; // 传入`delete(void *rawMem, std::size_t size)`的`size == sizeof(Base)`。
假设Base::~Base()
声明为virtual
,则上述size
就是正确的sizeof(Derived)
。
这也是为什么Item 7指出析构函数一定要声明virtual
。
Item 51:写new和delete时请遵循惯例的更多相关文章
- 读书笔记 effective c++ Item 51 实现new和delete的时候要遵守约定
Item 50中解释了在什么情况下你可能想实现自己版本的operator new和operator delete,但是没有解释当你实现的时候需要遵守的约定.遵守这些规则并不是很困难,但是它们其中有一些 ...
- 条款八: 写operator new和operator delete时要遵循常规
自己重写operator new时(条款10解释了为什么有时要重写它),很重要的一点是函数提供的行为要和系统缺省的operator new一致.实际做起来也就是:要有正确的返回值:可用内存不够时要调用 ...
- 读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式
1. 一个错误释放内存的例子 下面的场景会有什么错? std::]; ... delete stringArray 一切看上去都是有序的.new匹配了一个delete.但有一些地方确实是错了.程序的行 ...
- 覆盖equals方法时请遵守通用约定
覆盖equals方法时请遵守通用约定 覆盖equals方法看起来很简单,但是有许多覆盖方式会导致错误,并且后果很严重.最容易避免这种类问题的方法就是不覆盖equals方法,在这种情况下,类的每个实 ...
- 条款16:成对使用new和delete时要使用相同的形式
请牢记: 如果在new表达式中使用[],必须在相应的delete表达式中也使用[]. new[] 对应 delete[] 如歌在new表达式中不适用[],一定不要在相应的delete表达式中使用[ ...
- 条款16:成对使用new和delete时,采取相同的形式
问题聚焦: 我们都知道,new和delete要成对使用,但是有时候,事情往往不是按我们预期的那样发展. 对于单一对象和对象数组,我们要分开考虑. 遇到typedef时,也需要搞 ...
- Effective C++(16) 成对使用new和delete时要采取相同的形式
问题聚焦: 我们都知道,new和delete要成对使用,但是有时候,事情往往不是按我们预期的那样发展. 对于单一对象和对象数组,我们要分开考虑 遇到typedef时,也需要 ...
- 将目录下面所有的 .cs 文件合并到一个 code.cs 文件中,写著作权复制代码时的必备良药
将目录下面所有的 .cs 文件合并到一个 code.cs 文件中,写著作权复制代码时的必备良药 @echo off echo 将该目录下所有.cs文件的内容合并到一个 code.cs 文件中! pau ...
- 重载delete时的那点事
重载delete时的那点事 C++的异常处理机制就会自动用与被使用 的operator new匹配的operator delete来释放内存(补充一点:在operator new中抛出异常不会导致这样 ...
随机推荐
- rocketmq源码分析3-consumer消息获取
使用rocketmq的大体消息发送过程如下: 在前面已经分析过MQ的broker接收生产者客户端发过来的消息的过程,此文主要讲述订阅者获取消息的过程,或者说broker是怎样将消息传递给消费者客户端的 ...
- JavaScript 专题系列第六篇,讲解深浅拷贝的技巧和以及实现深浅拷贝的思路
拷贝也是面试经典呐! 数组的浅拷贝 如果是数组,我们可以利用数组的一些方法比如:slice.concat 返回一个新数组的特性来实现拷贝. 比如: var arr = ['old', 1, tru ...
- [git 学习篇]远程创库
实际情况往往是这样,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交. 完全 ...
- NOJ——1665夜神的思考(YY+组合问题+分类讨论)
[1665] 夜神的思考 时间限制: 1000 ms 内存限制: 65535 K 问题描述 最近夜神对二进制很感兴趣,于是他每次看到一串只包含1和0的字符串的时候就会想,这串字符串有多少子串是含有k个 ...
- Java面试之JVM原理总结
1.什么是JVM? 答:JVM是Java Virual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,他是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟计算机功能来实现 ...
- webRTC windows demo1(转)
// setup video engine char cCameraName[MAX_CAMERA_NAME_LENGTH]; memset(cCameraName, , MAX_CAMERA_NAM ...
- hdu 4932 BestCoder Round #4 1002
这题真是丧心病狂,引来今天的hack狂潮~ Miaomiao's Geometry Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65 ...
- hdu 5438(类似拓扑排序)
Ponds Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Sub ...
- AC日记——最优贸易 codevs 1173
题目描述 Description [问题描述]C 国有n 个大城市和m 条道路,每条道路连接这n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这m 条道路中有一部分为单向通行的道路 ...
- 广播broadcast的使用
很多时候我们有这样的需求,比如说,订单支付成功,需要更新订单列表或订单详情的订单状态,这时候我们就可以用到广播. 首先我们要使用Intent来发送一个广播 定义一个全局的广播名字 public sta ...