Effective C++ 第二版 8) 写operator new 和operator delete 9) 避免隐藏标准形式的new
条款8 写operator new 和operator delete 时要遵循常规
重写operator new时, 函数提供的行为要和系统缺省的operator new一致:
1)正确的返回值; 2)可用内存不够时调用出错处理函数; 3)处理0字节内存请求的情况; 避免隐藏标准形式的new;
1)如果内存分配请求成功, 返回指向内存的指针, 失败抛出std::bad_alloc异常;
operator new实际上不止一次尝试分配内存, 每次失败会调用出错处理函数(期望释放别处的内存), 只有在出错处理函数的指针为空的情况下才抛出异常.
Note 按C++标准要求, 在请求分配0字节的内存时, operator new也要返回一个合法指针.
非类成员形式的operator new伪代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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();
}
}
|
>处理零字节的请求的技巧是把他作为请求一个字节来处理;
>把handler置为0然后再恢复是因为没法直接得到handler的指针, 必须调用set_new_handler;
>operator new包含一个无限循环: while(1), 跳出循环的条件是内存分配成功或出错处理函数完成事件中的一种:
得到可用内存; 安装了新的new-handler; 卸除了new-handler; 抛出bad_alloc类型的异常; 返回失败; 所以new-hander必须做到其中一件事, 否则循环无法结束;
operator new经常会被子类继承, 引出复杂度; 大多数指针对类所写的operator new只为特定的类设计的, 不是为其他类或子类设计的;
对于一个类X的operator new来说, 函数内部的行为在涉及到对象的大小时, 都是sizeof(X). 但由于存在继承, 基类中的operator new可能被调用给子类对象分配内存;
|
1
2
3
4
5
6
7
8
|
class Base {
public:
static void * operator new(size_t size);
...
};
class Derived: public Base // Derived 类没有声明operator new
{ ... }; //
Derived *p = new Derived; // 调用Base::operator new
|
>如果Base类的operator new不想处理这种情况, 简单的方法是把内存分配请求转给标准operator new来处理:
|
1
2
3
4
5
6
7
|
void * Base::operator new(size_t size)
{
if (size != sizeof(Base)) // 如果数量“错误”,让标准operator new
return ::operator new(size); // 去处理这个请求
//
... // 否则处理这个请求
}
|
>size != sizeof(Base)处理了size等于零的情况: C++标准规定独立的freestanding类的大小都是非零值; 即使Base没有成员, sizeof(Base)也总是非0; (非独立的类sizeof可能为零) [嵌套类??] size为零时, 请求就会转到::operator new来处理;
如果想控制基于类的数组的内存分配, 必须实现operator new的数组形式: operator new[]("数组new");
写operator new[]时, 面对的是"原始"内存, 不能对数组里还不存在的对象进行操作; 还不知道数组对象的个数和大小;
基类的operator new[]会通过继承的方式被用来为子类对象的数组分配内存, 但子类对象一般比基类的大;
Base::operator new平[]里的每个对象大小不一定都是sizeof(Base), 数组对象的数量不一定就是 (请求字节数)/sizeof(Base);
对于operator delete和operator delete[], 要记住C++保证删除空指针总是安全的;
|
1
2
3
4
5
6
7
|
void operator delete(void *rawMemory)
{
if (rawMemory == 0)
return; file://如果指针为空,返回
//释放 rawMemory指向的内存;
return;
}
|
假设类的operator new将"错误"大小分配请求转给::operator new, 那么必须将"错误"大小的删除请求转给::operator delete;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class Base { // 和前面一样,只是这里声明了operator delete
public:
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 来处理请求
::operator delete(rawMemory);
return;
}
"释放指向 rawMemory的内存;"
return;
}
|
必须遵守operator new和operator delete的规定; 需要内存分配程序支持new-handler函数, 并正确处理零内存请求;
条款9 避免隐藏标准形式的new
内部范围声明的名称会隐藏掉外部范围的相同名称, 所以对于在类的内部和全局声明的同名函数f来说, 类成员函数会隐藏掉全局函数;
|
1
2
3
4
5
6
7
8
|
void f(); // 全局函数
class X {
public:
void f(); // 成员函数
};
X x;
f(); // 调用 f
x.f(); // 调用 X::f
|
>调用全局函数和成员函数时采用的是不同的语法形式;
但是如果在类里增加一个带多个参数的operator new函数:
|
1
2
3
4
5
6
7
8
9
10
|
class X {
public:
void f();
// operator new 的参数指定一个 new-hander(new 的出错处理)函数
static void * operator new(size_t size, new_handler p);
};
void specialErrorHandler(); // 定义在别的地方
X *px1 = new (specialErrorHandler) X; // 调用X::operator new
X *px2 = new X; //错误
|
>类里定义了"operator new"函数后, 会阻止对标准new的访问;
Solution 1)在类里写一个支持标准new调用方式的operator new, 和标准new做同样的事情;
|
1
2
3
4
5
6
7
8
9
|
class X {
public:
void f();
static void * operator new(size_t size, new_handler p);
static void * operator new(size_t size) { return ::operator new(size); }
};
X *px1 = new (specialErrorHandler) X; // 调用 X::operator new(size_t, new_handler)
X* px2 = new X; // 调用 X::operator new(size_t)
|
>使用内联实现;
Solution 2)为每一增加到operator new的参数提供缺省值;
|
1
2
3
4
5
6
7
|
class X {
public:
void f();
static void * operator new(size_t size, new_handler p = 0); // p 缺省值为0
};
X *px1 = new (specialErrorHandler) X; // 正确
X* px2 = new X; // 也正确
|
>以后想对"标准"形式的new定制新的功能, 只需重写这函数;
Effective C++ 第二版 8) 写operator new 和operator delete 9) 避免隐藏标准形式的new的更多相关文章
- Effective C++ 第二版 10) 写operator delete
条款10 写了operator new就要同时写operator delete 写operator new和operator delete是为了提高效率; default的operator new和o ...
- Effective Java 第二版 Enum
/** * Effective Java 第二版 * 第30条:用enum代替int常量 */ import java.util.HashMap;import java.util.Map; publi ...
- Effective C++ 第二版 17)operator=检查自己 18)接口完整 19)成员和友元函数
条款17 在operator=中检查给自己赋值的情况 1 2 3 class X { ... }; X a; a = a; // a 赋值给自己 >赋值给自己make no sense, 但 ...
- Effective C++ 第二版 5)new和delete形式 6) 析构函数里的delete
内存管理 1)正确得到: 正确调用内存分配和释放程序; 2)有效使用: 写特定版本的内存分配和释放程序; C中用mallco分配的内存没有用free返回, 就会产生内存泄漏, C++中则是new和de ...
- Effective C++ 第二版 31)局部对象引用和函数内new的指针 32)推迟变量定义
条款31 千万不要返回局部对象的引用, 不要返回函数内部用new初始化的指针的引用 第一种情况: 返回局部对象的引用; 局部对象--仅仅是局部的, 在定义时创建, 在离开生命空间时被销毁; 所谓生命空 ...
- 《Effective Java第二版》总结
第1条:考虑用静态工厂方法代替构造器 通常我们会使用 构造方法 来实例化一个对象,例如: // 对象定义 public class Student{ // 姓名 private String name ...
- Effective C++ 第二版 40)分层 41)继承和模板 42)私有继承
条款40 通过分层来体现"有一个"或"用...来实现" 使某个类的对象成为另一个类的数据成员, 实现将一个类构筑在另一个类之上, 这个过程称为 分层Layeri ...
- Effective C++ 第二版 1)const和inline 2)iostream
条款1 尽量用const和inline而不用#define >"尽量用编译器而不用预处理" Ex. #define ASPECT_R 1.653 编译器永远不会看到AS ...
- 《Effective Java 第二版》读书笔记
想成为更优秀,更高效程序员,请阅读此书.总计78个条目,每个对应一个规则. 第二章 创建和销毁对象 一,考虑用静态工厂方法代替构造器 二, 遇到多个构造器参数时要考虑用builder模式 /** * ...
随机推荐
- Codeforces 482 - Diverse Permutation 构造题
这是一道蛮基础的构造题. - k +(k - 1) -(k - 2) 1 + k , 1 , k , 2, ....... ...
- OKR 方法 学习笔记
最近公司兴起了对OKR这个词的讨论,并且听到时总会伴随提到KPI,提到绩效考核.那OKR到底是什么呢?与KPI的区别在哪里?与绩效考核有什么关系?它与我们现在推行的敏捷开发有啥关系呢?因此,就到网上查 ...
- TreePuzzle 一点感想
题目链接 这一道题看起来像是一道贪心的水题,但是情况较难考虑周全,比如下图,我就考虑漏了这种情况,点0是可以移动到点1的.然后我就各种奇怪的分类讨论.最终还是没能A,决定放弃治疗. 然后我看了看解答, ...
- hdu 2757 Ocean Currents(优先队列+bfs)
小伙伴们真心被这道题惊呆了!刚开始是读题,题目都把小伙伴惊呆了,题目都读不懂! 在前面猴子小伙伴的帮助下,理解了一点点,又偷偷的在纸上写写画画,明白了题意! 后来,你懂的,果断拿下!在拿下的过程也经过 ...
- Elven Postman(二叉树)
Elven Postman Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)T ...
- Ubuntu下编译Android JNI最靠谱的方法...
网上资料太杂乱,搞了大半天都还是没搞懂怎么系统的调用NDK.最后干脆放弃了Win改用Ubuntu编译JNI,虽然编译环境简单了,但是资料却少了不少.几乎没有一篇完整的文章.我想或许是能在Ubuntu下 ...
- 更新整理本人全部博文中提供的代码与工具(Java,2014.09)
为了更方便地管理博文中涉及的各种代码与工具资源,如今把这些资源迁移到 GitHub 中,有兴趣者可前往下载. Java 1.<高效 Java Web 应用开发框架 JessMA v3.4.1 正 ...
- Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释
本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在曾经的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本的语法 ...
- Hiddenfield控件
本文转自:http://www.cnblogs.com/weicleer/archive/2012/11/26/2788722.html ASP.NET2.0 HiddenField控件(1)2007 ...
- Qt持久性对象进行序列化(同时比较了MFC与Java的方法)
Mfc和Java中自定义类的对象都可以对其进行持久性保存,Qt持久性对象进行序列化当然也是必不可少的.不过这个问题还真困扰了我很长时间……Mfc通过重写虚函数Serialize().Java则是所属的 ...